12进程间通信-PIPE进程间通信―FIFO3信号中断处理进程通信有如下一些目的:A、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间;B、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到;C、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(们)发生了某种事件(如进程终止时要通知父进程);D、资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供锁和同步机制;E、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。2019/8/162进程间通信的方式linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了“systemVIPC”,通信进程局限在单个计算机内;后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。Linux则把两者继承了下来,其中,最初UnixIPC包括:管道、FIFO、信号;SystemVIPC包括:SystemV消息队列、SystemV信号量、SystemV共享内存区;PosixIPC包括:Posix消息队列、Posix信号量、Posix共享内存区。2019/8/163Linux进程通信方式linux下进程间通信的几种主要手段简介:1、管道(Pipe)及有名管道(namedpipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;2、信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数signal外,还支持语义符合Posix.1标准的信号函数sigaction。2019/8/164Linux进程通信方式3、报文(Message)队列(消息队列):消息队列是消息的链接表。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。4、共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。5、信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。2019/8/165第四章进程间通信-管道和信号12进程间通信-PIPE进程间通信―FIFO3信号中断处理管道管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区中存取数据:管道一端的进程顺序地将数据写入缓冲区,另一端的进程则顺序地读出数据。该缓冲区可以看作是一个循环队列,读和写的位置都是自动增加的,不能随意改变,一个数据只能被读一次,读出以后在缓冲区中就不复存在了。当缓冲区读空或写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列;当空的缓冲区有新数据写入或满的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。2019/8/167管道管道实际上以类似文件的方式与进程交互,但它并不与磁盘打交道,所以效率要比文件操作高很多。它有两个局限性:(1)支持半双工;(2)只有具有亲缘关系的进程之间才能使用这种无名管道;使用管道的注意事项:1.当读一个写端已经关闭的管道时,在所有数据被读取之后,read函数返回值为0,以指示到了文件结束处;2.如果写一个读端关闭的管道,则产生SIGPIPE信号。如果忽略该信号或者捕捉该信号并处理程序返回,则write返回-1,errno设置为EPIPE2019/8/168管道示例例如$ls|more功能是将ls命令的输出作为more命令的输入,并显示more的最终输出。这里ls与more要由两个进程来完成。这两个进程的通信就通过父进程shell创建管道。ls向管道输入数据,more从管道读出数据。2019/8/169stdinstdinstdoutstdoutstderrstderrlsmore管道连接创建无名管道externintpipt(int__pipedes[2])此函数的参数是一个整型数组。如果执行成功,pipe将存储两个整形文件描述符于__pipedes数组中,它们分别指向管道的两端。如果系统调用失败,将返回-1。pipedes[1]pipedes[0]写读进程A进程B2019/8/1610例:父子进程通过无名管道通信#includestdio.h#includestdlib.h#includesys/types.h#includesys/wait.h#includeunistd.h#includestring.hmain(){pid_tresult;intr_num;intpipe_fd[2];charbuf_r[100],buf_w[100];memset(buf_r,0,sizeof(buf_r));2019/8/1611例:父子进程通过无名管道通信if(pipe(pipe_fd)0){perror(pipe);exit(EXIT_FAILURE);}result=fork();if(result0){perror(fork);exit(EXIT_FAILURE);}elseif(result==0){close(pipe_fd[1]);if((r_num=read(pipe_fd[0],buf_r,100))0)printf(childprocesshasread%dcharactersfromthepipe,thestringis:%s\n,r_num,buf_r);close(pipe_fd[0]);exit(0);}2019/8/1612例:父子进程通过无名管道通信else{close(pipe_fd[0]);printf(pleaseinputthestring:);scanf(%s,buf_w);if(write(pipe_fd[1],buf_w,strlen(buf_w))!=-1)printf(parentprocesshaswritten:%stothepipe!\n,buf_w);close(pipe_fd[1]);waitpid(result,NULL,0);exit(0);}}2019/8/1613dup()externintdup(int__fd)dup()会复制一份原来已经打开的文件描述符,新的描述符指向系统文件表中下一个可用的最小非负文件描述符,它与原来的文件描述符共享同一个文件指针,并拥有相同的文件权限及模式。当调用dup()时,总返回下一个最小的可用文件描述符。例如:下面语句即可以将输出重定向到管道:intf_des[2];pipe(f_des);close(fileno(stdout));dup(f_des[1])此后,所有写向标准输出的数据都将写入到管道中。因此,要复制标准输出输入设备,应先关闭这一设备,然后再复制。2019/8/1614复制文件描述符fd1=0files进程信息task_structfd2=1fd3=2f_des[0]=3打开文件列表file_struct文件读写位置打开的mode操作指针文件v节点位置f_des[1]=4V节点信息I节点信息……打开的文件structfile管道的读端2019/8/1615dup2()externintdup2(int__fd,int__fd2)dup2()有两个参数,fd和fd2,fd2是小于文件描述符的最大允许值的非负整数。如果fd2是一个已打开的文件描述符,则首先关闭该文件,然后再复制。2019/8/1616综合应用举例#includeerrno.h#includesys/wait.h#includestdio.h#includestdlib.h#includeunistd.h#defineDEF_PAGER/bin/more“#defineMAXLINE10main(intargc,char*argv[]){intn;intfd[2];pid_tpid;char*pager,*argv0;charline[MAXLINE];FILE*fp;2019/8/1617综合应用举例if(argc!=2){printf(errorusage!\n);exit(EXIT_FAILURE);}if((fp=fopen(argv[1],r))==NULL){perror(open);exit(EXIT_FAILURE);}if(pipe(fd)0){perror(pipe);exit(EXIT_FAILURE);}if((pid=fork())0){perror(fork);exit(EXIT_FAILURE);}2019/8/1618综合应用举例if(pid0){close(fd[0]);while(fgets(line,MAXLINE,fp)!=NULL){n=strlen(line);if(write(fd[1],line,n)!=n){perror(write);exit(EXIT_FAILURE);}}close(fd[1]);if(waitpid(pid,NULL,0)0){perror(wait);exit(EXIT_FAILURE);}exit(0);}2019/8/1619综合应用举例else{close(fd[1]);if(fd[0]!=STDIN_FILENO){if(dup2(fd[0],STDIN_FILENO)!=STDIN_FILENO){perror(dup);exit(EXIT_FAILURE);}close(fd[0]);}if((pager=getenv(PAGER))==NULL)pager=DEF_PAGER;if((argv0=strrchr(pager,'/'))!=NULL)argv0++;elseargv0=pager;if(execl(pager,argv0,(char*)0)0){perror(exec);exit(EXIT_FAILURE);}exit(0);}}2019/8/1620流重定向externFILE*popen(__constchar*__command,__constchar*__modes);popen函数创建一个子进程,并在子进程中执行第一个参数程序,同时返回一个文件指针,即第一个参数*__command指向要执行的命令的指针。第二个参数表示I/O模式的类型。如果此命令的输出将作为其他命令的输入,即输出重定向,则需要设置其第二个参数为“r”权限;如果此命令的输入数据要从其他命令的输出数据,即输入重定向,则需要设置其第二个参数为“w”权限;在使用完重定向后,需要使用pclose()关闭相应的流对象,该函数声明如下:externintpclose(FILE*__stream);2019/8/1621例:流重定向的应用#includeerrno.h//#includesys/wait.h#includestdio.h#includestdlib.h#includeunistd.h#definePAGER${PAGER:-more}“#defineMAXLINE10main(intargc,char*argv[]){charline[MAXLINE];FILE*fpin,*fpout;if(argc!=2){printf(errorusage!\n);exit(EXIT_FAILURE);}