进程间通信目标本章旨在介绍Linux系统下进程间通信及同步的机制及方法:1)掌握进程间通信的主要方法:管道、命名管道、共享内存、消息队列等2)掌握进程间同步的主要方法:信号量等主要内容进程间通信机制(IPC)进程间通信机制(IPC机制)有五类:主要内容管道管道是一种进程间的通信机制。功能:两个或多个进程间通过管道,可以互相传递信息,利用read和write系统调用函数来进行读写操作。具有亲缘的父子进程间通信管道使用管道符|来连接进程。在Linux系统中,由管道连接起来的进程可以自动运行,就如同在他们之间有一个数据流一样.在下面的这个例子中,我们要使用sort命令来排序ps的输出.而如果我们不使用管道,我们就要分几步来完成:$pspsout.txt$sortpsout.txtpssort.out使用管道:$ps|sortpssort.out管道LinuxC中的管道函数pipefiledes是一个有两个成员的整形数组,用来保存管道的文件描述符,如果调用成功。filedes[0]将用来从管道读取数据。filedes[1]用来向管道写入数据。管道例:创建一个管道,将命令行参数通过管道传输至新派生的子进程。(exam1)//要求输入命令行参数管道//创建管道失败//创建子进程失败管道//关闭管道的写描述符//从管道中循环读取//输出读到的数据//输出换行符//关闭管道的读描述符//退出子进程管道//关闭管道的读描述符//写参数//关闭管道的写描述符//等待子进程退出管道执行结果:主要内容命名管道命名管道或“命名管线”(NamedPipes)是一种简单的进程间通信(IPC)机制。命名管道可在同一台计算机的不同进程之间,或在跨越一个网络的不同计算机的不同进程之间,支持可靠的,单向或双向的数据通信。命名管道创建后,文件系统中将产生一个物理的FIFO文件。命名管道命名管道命令行方式创建FIFO管道mknodfilenamepmkfifo[–m权限]filename命名管道例:用命令方式创建命名管道p2.mkfifo–m0644p2mknodp2pP2管道文件命名管道程序中创建FIFO管道参数filename为指定管道文件的名字参数mode给出了FIFO的访问权限成功返回0,错误返回-1命名管道例:通过编程创建命名管道p1。命名管道例:通过编程分别以只读、只写方式打开p1管道,检验阻塞方式打开管道的过程。(exam.ccc.c)命名管道//以只读方式打开命名管道//以写入方式打开命名管道管道阻塞如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO。(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO。或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。命名管道要想取消阻塞标志,可以在使用open打开管道时加上参数O_NONBLOCK例如open(“p1”,O_RDONLY|O_NONBLOCK)命名管道管道阻塞从FIFO中读数据时(用read函数),如果没有数据,默认是阻塞等待,直到有数据被写入FIFO。如果read函数返回0,说明该FIFO所有的写端都已关闭,程序要做相应的处理。向FIFO写入数据时(使用write函数),如果FIFO有足够空间,write函数会返回写入的字节数;如果空间不够,write函数会阻塞,直到写完为止。当所有的读端都关闭时,再向FIFO写数据会出错。管道分类无名管道不属于任何文件系统,只存在于内存中,它是无名无形的,但是可以把它看作一种特殊的文件,通过使用普通文件的read(),write()函数对管道进行操作。命名管道是有名有形的,为了使用这种管道,LINUX中设立了一个专门的特殊文件系统--管道文件,它存在于文件系统中,任何进程可以在任何时候通过命名管道的路径和文件名来访问管道。但是在磁盘上的只是一个节点,而文件的数据则只存在于内存缓冲页面中,与普通管道一样。主要内容消息队列消息队列:消息队列是消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。消息队列消息队列、共享内存、信号量统称IPC。IPC(Inter-ProcessCommunication进程间通信)查看IPC对象信息ipcs[-asmq]a全部s查看信号量m查看共享内存q查看消息队列消息队列删除IPC对象信息ipcrm[-smq]ID或[-SMQ]keys删除信号量m删除共享内存q删除消息队列消息队列例:查看系统中的IPC对象。消息队列IPC机制关键值keyIPC对象的名字,需要全局唯一,类似文件名。键值生成函数的作用是以path相关的信息为基础返回一个关键值,利用此关键值实现IPC同步和通信机制。ID参数为子序号消息队列例:利用ftok()产生一个可用的键值。(exam2)消息队列消息队列执行结果:•在一般的Linux实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。•key1与key3相同,因为二者项目ID相同。消息队列msgget函数:功能为创建和访问消息队列参数key:为标识好的消息队列参数permflags:有两个值可选择:IPC_CREAT:如果内核中没有此队列,则创建它IPC_EXCL:与IPC_CREAT合用,如果要创建的队列已存在,则失败,返回-1消息队列msgget函数如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。0:取消息队列标识符,若不存在,函数报错消息队列msgsnd函数:功能为向消息队列添加信息msgrcv函数:功能为从队列中读取消息参数mqid:由msgget返回获得的参数message:由用户自定义的结构,结构类似如下结构体:参数flags:只有一个有意义的值IPC_NOWAIT,当消息无法返回,调用立即返回,返回值为-1参数msg_type:决定实际将读到的是哪条消息structmsgbuf{longmType;charmtext[1];}消息队列msg_type:消息类型等于0,则返回队列的最早的一个消息。大于0,则返回其类型为msg_type的第一个消息。小于0,则返回其类型小于或等于msg_type参数的绝对值的最小的一个消息。返回值:成功执行时,msgsnd()返回0,msgrcv()返回拷贝到的实际字节数。失败两者都返回-1。消息队列flags:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果flags和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当flags为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。消息队列flags:取值为IPC_EXCEPT:与msg_type0配合使用,返回队列中第一个类型不为msg_type的消息取值为MSG_NOERROR:截断超长数据消息队列消息队列控制函数msgctl()原型:返回值:成功返回0,失败返回-1.参数:msqid:已打开的消息队列id#includesys/types.h#includesys/ipc.h#includesys/msg.hintmsgctl(intmsqid,intcmd,structmsqid_ds*buf);消息队列cmd:控制类型选项IPC_STAT:取得队列状态IPC_SET:设置队列属性IPC_RMID:删除消息队列Buf:存放队列的属性结构消息队列队列属性如下:structmsqid_ds{structipc_permmsg_perm;/*structuredescribingoperationpermission*/__time_tmsg_stime;/*最后一次发送消息的时间*/unsignedlongint__unused1;/*保留*/__time_tmsg_rtime;/*最后一次接收数据时间*/unsignedlongint__unused2;/*保留*/__time_tmsg_ctime;/*最后修改时间*/unsignedlongint__unused3;/*保留*/unsignedlongint__msg_cbytes;/*当前队列字节数*/msgqnum_tmsg_qnum;/*当前队列的消息数*/msglen_tmsg_qbytes;/*队列中容量*/__pid_tmsg_lspid;/*最后发送消息的进程号*/__pid_tmsg_lrpid;/*最后接收队列的进程号*/unsignedlongint__unused4;/*保留*/unsignedlongint__unused5;/*保留*/};消息队列例:创建一个消息队列,向其中发送一条消息。(exam3)//自定义message结构体消息队列获取键值key消息队列//创建消息队列失败//队列已经存在消息队列//清空消息队列//指定消息类型//指定消息内容//非阻塞发送消息消息队列执行结果:消息队列例:从前面的消息队列中读取消息。(exam4)//自定义message结构体消息队列获取键值key,n;消息队列//获取消息队列标示符消息队列//从消息队列中接收消息//输出接收到的消息消息队列执行结果:主要内容共享内存共享内存是在系统内核分配的一块缓冲区,多个进程可以访问该缓冲区。共享内存共享内存原理共享内存shmget函数:功能为创建一段内存用于进程共享数据参数key:内存段的关键值参数size:内存段所需最小字节数参数permflags:给出了内存段的访问权限共享内存例:调用shmget()创建一块共享内存。(exam5)共享内存共享内存执行结果:主要内容信号量信号量也称为信号灯,用于进程间的同步。是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将AcquireSemaphoreVI以及ReleaseSemaphoreVI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。信号量PV操作的定义PV操作与信号灯的处理相关,P表示通过的意思,V表示释放的意思。作用:解决进程互斥与进程同步问题的机制。信号量信号量的类型定义信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。信号量信号量的类型定义一般来说,信号量S=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当