linux进程间通信linux进程间通信•进程间通信IPC(interprocessCommunication)提供了一种不同进程间可以互相访问数据的方式。相互访问的数据不仅包括程序运行时的适时数据,也包括对对方代码段的访问。•进程间通信的目的:1、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。2、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。3、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。4、资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。5、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有异常,并能够及时知道它的状态改变。linux进程间通信发展历史•linux进程间通信(IPC)由以下几部分发展而来:linux进程间通信方式•目前Linux中使用较多的进程间通信方式:•(1)管道(Pipe)及有名管道(namedpipe):管道可用于具有亲缘关系进程间的通信;有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。•(2)信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知接受进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的。•(3)消息(message)队列:消息队列是消息的链接表,包括Posix消息队列systemV消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。linux进程间通信•(4)共享内存(sharedmemory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。•(5)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。•(6)套接字(Socket):这是一种更为一般的进程间通信机制,它可用于不同机器之间的进程间通信,应用常广泛。管道通信•管道通信是linux中比较常见,也比较原始的通信方式之一,它实现了数据以一种数据流的方式,在多进程间流动。•管道:是指用于连接一个读进程和一个写进程,以实现它们之间通信的共享文件,又称pipe文件。–管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。–写进程在管道的尾端写入数据,读进程在管道的首端读出数据。–数据读出后将从管道中移走,其它读进程都不能再读到这些数据。–管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。•管道分类:–无名管道:(匿名管道)在系统中没有实名,不能在文件系统中以任何方式看到该管道,它只是进程的一种资源,会随着进程的结束而被系统清除。–有名管道:也称为FIFO管道,是一种文件类型,在文件系统中可以查看到。7匿名管道(pipe)•对匿名管道两端进程而言,是一个只存在于内存的特殊文件•匿名管道是半双工的,数据只能向一个方向流动–一个进程将数据写入管道,另一进程从管道中读取数据–写入的内容添加在管道缓冲区的末尾,每次都是从缓冲区头部读出数据•双向通信的建立–需要建立起两个管道•使用限制–只能用于具有亲缘关系的进程之间•如父子进程或兄弟进程之间管道通信8匿名管道的建立•基本函数–intpipe(intfd[2]);•参数说明–fd[2]描述管道两端•fd[0]只能用于读,称为管道读端•fd[1]只能用于写,称为管道写端•若试图从写端读,或者向读端写都将导致错误发生•返回值–成功时返回0,失败时返回-1•说明–基本文件I/O函数都可用于管道•如close()、read()、write()等•低层系统调用–sys_pipe()--do_pipe()管道通信9匿名管道的读操作•进程调用read()系统调用–内核最终调用与该文件描述符相关的文件操作表中所找到的read()方法–在管道情形下,read方法将指向pipe_read()函数–该系统调用可能以两种方式阻塞当前进程•系统调用开始时管道缓冲区为空•管道缓冲区没有包含所请求的字节(n个字节),写进程在等待缓冲区的空间时曾经被置为睡眠10匿名管道的写操作•进程调用write()系统调用–内核最终调用pipe_write()函数–如果管道没有读进程,写进程发送SIGPIPE信号–管道缓冲区一有空闲区域,写进程将试图写入数据–如果读进程不读出管道缓冲区中的数据,那么写操作将一直阻塞匿名管道示例一•#includestdlib.h•#includestdio.h•#includeunistd.h•intmain(void)•{•intfd[2];•charstr[256];•if(pipe(fd)0){•perror(pipe);•exit(1);•}•write(fd[1],Createthepipesuccessfully!\n,31);•read(fd[0],str,sizeof(str));•printf(%s\n,str);•close(fd[0]);•close(fd[1]);•return0;•}•单个进程中的管道几乎没有任何用处。通常,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程或反之的IPC通道。•fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。对于从子进程到父进程的管道,父进程关闭fd[1],子进程关闭fd[0]。12•#includeunistd.h•#includestdio.h•#includestdlib.h•#defineBUFSZ256•intmain(void)•{•intfd[2];•charbuf[BUFSZ];•pid_tpid;•intlen;•if(pipe(fd)0)•{•perror(failedtopipe);•exit(1);•}•if((pid=fork())0)•{•perror(failedtofork);•exit(1);•}13•elseif(pid0)•{•printf(Thisisfarther,writetofd[1]\n,fd[0],fd[1]);•close(fd[0]);•write(fd[1],fartherwrite,childread\n,25);•exit(0);•}•else•{•printf(Thisischild,readfromfd[0]\n);•close(fd[1]);•read(fd[0],buf,BUFSZ);•printf(%s\n,buf);•}•return0;•}匿名管道----父子进程间通信匿名管道操作基本流程•管道操作的基本流程:1)使用pipe函数创建管道;2)用fork函数创建一个子进程;3)关闭父子进程中不需要的文件描述符,使用管道进行通信;由于以上对管道的操作是比较规范,也比较常用。所以在ANSIC中将以上操作定义在两个标准库函数中,分别是popen和pclose函数。FILE*popen(constchar*command,constchar*type);intpclose(FILE*fp);参数command是一个在shell中可以运行的命令字符串的指针;参数type是一个字符指针,这个参数只有两种值,分别是r和w,分别对应popen函数的返回值是一个读打开文件指针,还是写打开文件指针。函数失败返回NULL,并设置出错变量errno。pclose函数的参数fp是一个popen打开的文件描述符,函数失败时返回-1。•#includeunistd.h•#includestdio.h•#includestdlib.h•#includefcntl.h•#includelimits.h•#defineBUFESPIPE_BUF•intmain(void)•{•FILE*fp;•char*cmd=ls-l;•charbuf[BUFES];•if((fp=popen(cmd,r))==NULL)•{•perror(failedtoopen\n);•exit(1);•}•}•while((fgets(buf,BUFES,fp))!=NULL)•printf(%s\n,buf);•pclose(fp);•exit(0);•}Popen示例16有名管道•匿名管道缺点–没有名字,只能用于具有亲缘关系的进程间通信•FIFO(有名管道)–严格遵循先进先出的读写规则–有名字,FIFO的名字包含在系统的目录树结构中,支持无亲缘关系的进程按名字访问–类似管道,在文件系统中不存在数据块,而是与一块内核缓冲区相关联•read和write操作也由pipe_read()和pipe_write()实现•与匿名管道主要区别–FIFO索引节点出现在系统目录树上而不是pipefs特殊文件系统中–FIFO是一种双向通信管道,可以以读/写模式打开一个FIFO17有名管道的建立•基本函数–intmkfifo(constchar*pathname,mode_tmode);•参数说明–pathname:创建的FIFO名字–mode:规定FIFO的读写权限•返回值–成功时返回0–失败时返回-1–若路径名存在,则返回EEXIST错误•说明–一般文件的I/O函数都可用于管道,如open(),close(),read(),write()等。18有名管道的open()•打开规则–为读操作而打开FIFO文件•若已有进程为写而打开该FIFO,则当前打开操作将成功返回•否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置未设置O_NONBLOCK标志)•或立即返回(当前打开操作设置O_NONBLOCK标志)–为写操作而打开FIFO文件•如果已经有进程为读而打开该FIFO,则当前打开操作将成功返回•否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作未设置O_NONBLOCK标志)•或者,返回ENXIO错误(当前打开操作设置O_NONBLOCK标志)•实例:1,mkfifotestfifo建立有名管道文件testfifo;2,lstestfifo向有名管道文件写入”ls”命令显示的内容;3,cattestfifo用”cat”命令读取”testfifo”文件中的内容;19有名管道示例——创建有名管道•#includesys/types.h•#includesys/stat.h•#includeerrno.h•#includestdio.h•#includestdlib.h•#includeunistd.h•#includelimits.h•#includefcntl.h•intmain(intargc,char*argv[])•{•//mode_tmode=0666;•mode_tmode=O_NONBLOCK;•if(argc!=2)•{•printf(USEMSG:create_fifo{fifoname}\n);•exit(1);•}•if((mkfifo(argv[1],mode))0)•{•perror(failedtomkfifo);•exit(1);•}•else•printf(yousuccessfullycreateaFIFOnameis:%s\n,argv[1]);•exit(0);•}20有名管道举例——读有名管道•#includesys