、进程间通信概述进程间通信有如下一些目的:数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。进程间通信(IPC)由以下几部分发展而来:早期UNIX进程间通信、基于SystemV进程间通信、基于Socket进程间通信和POSIX进程间通信。UNIX进程间通信方式包括:管道、FIFO、信号。SystemV进程间通信方式包括:SystemV消息队列、SystemV信号灯、SystemV共享内存。POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。使用的进程间通信方式:(1)管道(pipe)和有名管道(FIFO)(2)信号(signal)(3)消息队列(4)共享内存(5)信号量(6)套接字(socket)、管道通信普通的Linuxshell都允许重定向,而重定向使用的就是管道。例如:ps|grepvsftpd管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。管道创建与关闭创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。系统调用:pipe();原型:intpipe(intfd[2]);返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:errno=EMFILE(没有空闲的文件描述符)EMFILE(系统文件表已满)EFAULT(fd数组无效)注意:fd[0]用于读取管道,fd[1]用于写入管道。图1linux中管道与文件描述符的关系(){intpipe_fd[2];if(pipe(pipe_fd)0){printf(pipecreateerror\n);return-1;}elseprintf(pipecreatesuccess\n);close(pipe_fd[0]);close(pipe_fd[1]);}管道读写管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。图2父子进程管道的文件描述符对应关系子进程写入和父进程读的命名管道:图3关闭父进程fd[1]和子进程[0]管道读写注意事项可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。管道实例见:pipe_rw.c一样,管道的操作也支持基于文件流的模式。接口函数如下库函数:popen();原型:FILE*popen(char*command,char*type);返回值:如果成功,返回一个新的文件流。如果无法创建进程或者管道,返回NULL。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或者w,分别代表读或写。但不能同时为读和写。在Linux系统下,管道将会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。库函数:pclose();原型:intpclose(FILE*stream);返回值:返回系统调用wait4()的状态。如果stream无效,或者系统调用wait4()失败,则返回-1。注意此库函数等待管道进程运行结束,然后关闭文件流。库函数pclose()在使用popen()创建的进程上执行wait4()函数。当它返回时,它将破坏管道和文件系统。(){FILE*fp;char*cmd=ps-ef;charbuf[BUFSIZE];buf[BUFSIZE]='\0';if((fp=popen(cmd,r))==NULL)perror(popen);while((fgets(buf,BUFSIZE,fp))!=NULL)printf(%s,buf);pclose(fp);exit(0);}命名管道(FIFO)2.5.1基本概念命名管道和一般的管道基本相同,但也有一些显著的不同:命名管道是在文件系统中作为一个特殊的设备文件而存在的。不同祖先的进程之间可以通过管道共享数据。当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。命名管道创建与操作名管道创建#includesys/types.h#includesys/stat.hintmkfifo(constchar*pathname,mode_tmode);返回:若成功则为0,若出错则为-1一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。时,非阻塞标志(O_NONBLOCK)产生下列影响:(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。(2)如果指定了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。相关出错信息:EACCES(无存取权限)EEXIST(指定文件不存在)ENAMETOOLONG(路径名太长)ENOENT(包含的目录不存在)ENOSPC(文件系统剩余空间不足)ENOTDIR(文件路径无效)EROFS(指定的文件存在于只读文件系统中)实例见:fifo_write.c、fifo_read.c、信号通信3.1信号概述信号是软件中断。信号(signal)机制是Unix系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。很多条件可以产生一个信号。当用户按某些终端键时,产生信号。在终端上按DELETE键通常产生中断信号(SIGINT)。这是停止一个已失去控制程序的方法。(第11章将说明此信号可被映射为终端上的任一字符。)硬件异常产生信号:除数为0、无效的存储访问等等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。例如,对执行一个无效存储访问的进程产生一个SIGSEGV。进程用kill(2)函数可将信号发送给另一个进程或进程组。自然,有些限制:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。用户可用kill(1)命令将信号发送给其他进程。此程序是kill函数的界面。常用此命令终止一个失控的后台进程。当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。这里并不是指硬件产生条件(如被0除),而是软件条件。例如SIGURG(在网络连接上传来非规定波特率的数据)、SIGPIPE(在管道的读进程已终止后一个进程写此管道),以及SIGALRM(进程所设置的闹钟时间已经超时)。内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:异常:进程运行过程中出现异常;其它进程:一个进程可以向另一个或一组进程发送信号;终端中断:Ctrl-C,Ctrl-\等;作业控制:前台、后台进程的管理;分配额:CPU超时或文件大小突破限制;通知:通知进程某事件发生,如I/O就绪等;报警:计时器到期。中的信号:1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL5)SIGTRAP6)SIGIOT7)SIGBUS8)SIGFPE9)SIGKILL10)SIGUSR111)SIGSEGV12)SIGUSR213)SIGPIPE14)SIGALRM15)SIGTERM17)SIGCHLD18)SIGCONT19)SIGSTOP20)SIGTSTP21)SIGTTIN22)SIGTTOU23)SIGURG24)SIGXCPU25)SIGXFSZ26)SIGVTALRM27)SIGPROF28)SIGWINCH29)SIGIO30)SIGPWR下面是几个常见的信号。SIGHUP:从终端上发出的结束信号;SIGINT:来自键盘的中断信号(Ctrl-C);SIGQUIT:来自键盘的退出信号(Ctrl-\);SIGFPE:浮点异常信号(例如浮点运算溢出);SIGKILL:该信号结束接收信号的进程;SIGALRM:进程的定时器到期时,发送该信号;SIGTERM:kill命令发出的信号;SIGCHLD:标识子进程停止或结束的信号;SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号…………可以要求系统在某个信号出现时按照下列三种方式中的一种进行操作。(1)忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的原因是:它们向超级用