您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > LINUX环境编程-进程间通讯
LINUX应用编程–进程间通讯2009年12月22日2LINUX应用编程-目录IPC概述管道FIFO共享存储3进程间通讯–IPC概述信号(signal)亲缘进程和非亲缘进程都可以,也可以进程自己给自己递送信号。信号量(semaphore)主要是线程间和亲缘进程间的同步手段,不做数据传输之用。命名信号量可以在非亲缘进程间使用。管道只允许亲缘进程间的通讯。命名管道(FIFO)除了亲缘进程可以通讯外,非亲缘进程也可以通讯。共享内存多个进程可以访问同一块内存空间,是最快的IPC方式。在进程间传递数据时无须任何内存的拷贝。套接字最通用的进程间通讯方式,它提供了一种让不同机器上进程间通讯方式。4进程间通讯–管道管道是UNIXIPC的最老形式,并且所有UNIX系统都提供此种通信机制,管道有两种限制;(1)它们是半双工的。数据只能在一个方向上流动。(2)它们只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。管道的写和读有两个特性:(1)数据写入时,放在管道的结尾。(2)数据读取时,从管道的头开始读取。5进程间通讯–pipe函数管道是由调用pipe函数而创建的。#includeunistd.hintpipe(intfiledes[2]);•参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。•由于管道是单向的,所以通常一个进程需要关闭写端或者读端,而另一个进程则关闭相应的读端或写端。写端不存在时,读端会收到文件结束符。读端不存在时,写端会收到SIGPIPE信号。如果忽略该信号或者捕捉该信号并从其处理程序返回,则write出错返回,errno设置为EPIPE•成功返回0,失败返回-1.•缺点:只能用于亲缘进程间通讯,在一个进程内管道几乎是没有实际用处的•在写管道时,常数PIPE_BUF规定了内核中管道缓存器的大小。如果对管道进行write调用,而且要求写的字节数小于等于PIPE_BUF,则此操作不会与其他进程对同一管道(或FIFO)的write操作穿插进行。但是,若有多个进程同时写一个管道(或FIFO),而且某个或某些进程要求写的字节数超过PIPE_BUF字节数,则数据可能会与其他写操作的数据相穿插。6进程间通讯–fork前后管道关系fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])Fork后的半双工管道从父进程到子进程的管道对于从子进程到父进程的管道,父进程关闭fd[1],子进程关闭fd[0]7进程间通讯–popen和pclose函数我们常用到的一个操作是创建一个连接到另一个进程的管道,然后读其输出或向其发送输入,所以标准I/O库为实现这些操作提供了两个函数popen和pclose。这两个函数实现的操作是:创建一个管道,fork一个子进程,关闭管道的不使用端,exec一个shell以执行命令,等待命令终止。#includestdio.hFILE*popen(constchar*command,constchar*type);返回:若成功则为文件指针,若出错则为NULLintpclose(FILE*stream);返回:command的终止状态,若出错则为-1•函数popen先执行fork,然后调用exec以执行command,并且返回一个标准I/O文件指针。•如果type是r,则文件指针连接到command的标准输出。•如果type是“w”,则文件指针连接到command的标准输入。•pclose函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose返回的终止状态与shell执行exit一样。8进程间通讯–FIFOFIFO有时被称为命名管道。管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。创建FIFO类似于创建文件。确实,FIFO的路径名存在于文件系统中。#includesys/types.h#includesys/stat.hintmkfifo(constchar*pathname,mode_tmode);返回0成功,返回-1失败•mkfifo函数中mode参数的规格说明与open函数中的mode相同。•一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。•FIFO有两种用途:(1)FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。(2)FIFO用于客户机-服务器应用程序中,以在客户机和服务器之间传递数据9进程间通讯–FIFO当打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。(2)如果指定了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。一个给定的FIFO有多个写进程是常见的。这就意味着如果不希望多个进程所写的数据互相穿插,则需考虑原子写操作。正如对于管道一样,常数PIPE_BUF说明了可被原子写到FIFO的最大数据量。10进程间通讯–共享存储共享存储允许两个或多个进程共享一给定的存储区。因为数据不需要在客户机和服务器之间复制,所以这是最快的一种IPC。使用共享存储的唯一窍门是多个进程之间对一给定存储区的同步存取。若服务器将数据放入共享存储区,则在服务器做完这一操作之前,客户机不应当去取这些数据。通常,信号量被用来实现对共享存储存取的同步。11进程间通讯–mmapvoid*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset)•mmap在进程地址空间创建一个映射。它既可以把一个文件映射到内存,也可以映射一块内存,实现进程间内存共享。•addr为共享内存的起始地址,为NULL时,内核会自动选择一个起始地址。length为共享内存的长度。•prot指明了共享内存保护状态:PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE•flagsMAP_SHARED共享此内存,MAP_PRIVATE只有该进程可见。MAP_ANONYMOUS为匿名映射。•参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。12进程间通讯–内存共享内核为每个共享存储段设置了一个shmid_ds结构。#includesys/ipc.h#includesys/shm.h#includesys/types.hintshmget(key_tkey,size_tsize,intshmflg);void*shmat(intshmid,constvoid*shmaddr,intshmflg);intshmdt(constvoid*shmaddr);intshmctl(intshmid,intcmd,structshmid_ds*buf);key_tftok(constchar*pathname,intproj_id);13进程间通讯–shmget函数调用的第一个函数通常是shmget,它获得一个指定大小的共享存储标识符。intshmget(key_tkey,size_tsize,intshmflg);•key参数用来标识共享内存•size参数该共享存储段的最小值,如果正在创建一个新段(一般在服务器中),则必须指定其size。如果正在存访一个现存的段(一个客户机),则将size指定为0。•shmflg参数有IPC_CREAT和IPC_EXCL,最为重要的是在shmflg中指明访问权限,跟open的mode参数一样。否则会出现permissiondenied等错误。•返回:若成功则为共享内存ID,若出错则为-1key可由ftok生成。pathname必须为调用进程可以访问的。proj_id的低8bit有效。pathname和proj_id共同组成一个key。14进程间通讯–shmat一旦创建了一个共享存储段,进程就可调用shmat将其连接到它的地址空间中。void*shmat(intshmid,constvoid*shmaddr,intshmflg);返回:若成功则为指向共享存储段的指针,若出错则为-1共享存储段连接到调用进程的哪个地址上与addr参数以及在flag中是否指定SHM_RND位有关。(1)如果addr为0,则此段连接到由内核选择的第一个可用地址上。(2)如果addr非0,并且没有指定SHM_RND,则此段连接到addr所指定的地址上。(3)如果addr非0,并且指定了SHM_RND,则此段连接到(addr-(addrmodSHMLBA))所表示的地址上。SHM_RND命令的意思是:取整。SHM_LBA的意思是:低边界地址倍数,它总是2的乘方。该算式是将地址向下取最近1个SHMLBA的倍数。一般应指定addr为0,以便由内核选择地址。15进程间通讯–shmctlshmctl函数对共享存储段执行多种操作。intshmctl(intshmid,intcmd,structshmid_ds*buf);cmd参数指定下列5种命令中一种,使其在shmid指定的段上执行:•IPC_STAT对此段取shmid_ds结构,并存放在由buf指向的结构中。•IPC_SET按buf指向的结构中的值设置与此段相关结构中的下列三个字段:shm_perm.uid、shm_perm.gid以及shm_perm.mode。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一种是具有超级用户特权的进程。•IPC_RMID从系统中删除该共享存储段。因为每个共享存储段有一个连接计数(shm_nattch在shmid_ds结构中),所以除非使用该段的最后一个进程终止或与该段脱接,否则不会实际上删除该存储段。不管此段是否仍在使用,该段标识符立即被删除,所以不能再用shmat与该段连接。此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一种是具有超级用户特权的进程。•SHM_LOCK锁住共享存储段。此命令只能由超级用户执行。•SHM_UNLOCK解锁共享存储段。此命令只能由超级用户执行。一旦创建了一个共享存储段,进程就可调用shmat将其连接到它的地址空间中。返回:若成功则为0,若出错则为-116进程间通讯–shmdt当对共享存储段的操作已经结束时,则调用shmdt脱接该段。注意,这并不从系统中删除其标识符以及其数据结构。该标识符仍然存在,直至某个进程(一般是服务器)调用shmctl(带命令IPCRMID)特地删除它。intshmdt(constvoid*shmaddr);•shmaddr参数是以前调用shmat时的返回值。•返回:若成功则为0,若出错则为-1•Shmex.c例子展示了在亲缘关系的进程中内存共享•wshm.c例子往一片共享内存内写入数据,而rshm.c则从
本文标题:LINUX环境编程-进程间通讯
链接地址:https://www.777doc.com/doc-3975685 .html