当前位置:首页 > 电子/通信 > 综合/其它 > 106 SYSV进程间通信
10.6SYSV进程间通信内容:•信号量;•消息队列;•共享内存。10.6.1三者共有的特性IPC资源:表示单独的消息队列、共享内存或是信号量集合。三者均有XXXget()及XXXctl()函数(XXX代表msg,sem,shm三者之一)。1)XXXget()函数两个共同参数:key和oflag。key既可由ftok()函数产生,也可以是IPC_PRIVATE常量,key值是IPC资源的外部表示。oflag包括读写权限,还可以包含IPC_CREATE和IPC_EXCL标志位。它们组合的效果如下:(1)指定key为IPC_PRIVATE,保证创建一个惟一的IPC资源。(2)设置oflag参数的IPC_CREATE标志位,但不设置IPC_EXCL。如果相应key的IPC资源不存在,则创建一个IPC资源,否则返回已存在的IPC资源。(3)oflag参数的IPC_CREATE和IPC_EXCL同时设置。如果相应key的IPC资源不存在,则创建一个IPC资源。否则返回一个错误信息。2)XXXctl()均提供IPC_SET,IPC_STAT和IPC_RMID命令。前两者用来设置或得到IPC资源的状态信息,IPC_RMID用来释放IPC资源。共同的操作模式:•都是先通过XXXget()创建一个IPC资源,返回值是该IPC资源ID。在以后的操作中,均以IPC资源ID为参数,以对相应的IPC资源进行操作。•别的进程可以通过XXXget()取得已有的IPC资源ID(权限允许的话)并对其操作,从而使进程间通信成为可能。共同的数据结构:每一类IPC资源都有一个ipc_ids结构的全局变量用来描述同一类资源的公有数据,三个全局变量分别是semid_ds,msgid_ds和shmid_ds。structipc_ids{intsize;/*entries数组的大小*/intin_use;/*entries数组已使用的元素个数*/intmax_id;unsignedshortseq;unsignedshortseq_max;tructsemaphoresem;/*控制对ipc_ids结构的访问*/spinlock_tary;/*自旋锁控制对数组entries的访问*/structipc_id*entries;};structipc_id{structkern_ipc_perm*p;};数组entries的每一项指向一个kern_ipc_perm结构,kern_ipc_perm结构表示每一个IPC资源的属性,用来控制操作权限。structkern_ipc_perm{key_tkey;/*用户提供的键值,为XXXget()所用*/uid_tuid;/*创建者用户ID*/gid_tgid;/*创建者组ID*/uid_tcuid;/*所有者用户ID*/gid_tcgid;/*所有者组ID*/mode_tmode;/*操作权限,包括读、写等*/unsignedlongseq;};因为每个IPC资源描述符的第一个成员就是kern_ipc_perm结构。因此,我们可以认为数组entries的每一非空项均指向一个IPC资源。IPC资源ID与entries数组下标的联系:1)当创建一个IPC资源时2)当知道IPC资源ID时,可通过IPC资源ID%SEQ_MULTIPLIER得到其在entries数组中的index,从而找到相应的IPC资源。3)why保证在一段时期内IPC资源ID的惟一性。•调用函数ipc_addid()从相应ipc_ids结构的entries数组中找出第一个未使用的项,然后返回其下标index。•返回IPC资源IDIPC资源ID=SEQ_MULTIPLIER×seq+indexSEQ_MULTIPLIER是可用资源的最大数目,seq是ipc_ids结构中的seq。每当分配一个IPC资源时,ipc_ids结构中的seq就增1。10.6.2信号量•信号量是具有整数值的对象,它支持P,V原语。进程可以利用信号量实现同步和互斥。•SYSV支持的信号量实质上是一个信号量集合,由多个单独的信号量组成。我们称SYSV信号量为信号量集合,而单个的信号量直接称为信号量。•信号量集合在内核中用结构sem_array表示。structsem_array{structkern_ipc_permsem_perm;time_tsem_otime;/*最近一次操作时间*/time_tsem_ctime;/*最近一次的改变时间*/structsem*sem_base;/*指向第一个信号量*/structsem_queue*sem_pending;/*挂起操作队列*/structsem_queue**sem_pending_last;structsem_undo*undo;unsignedlongsem_nsems;/*信号量的个数*/};信号量集合中的每一个信号用结构sem表示:structsem{intsemval;/*信号量的当前值*/intsempid;/*最近对信号量操作进程的pid*/};信号量的初始值可以调用函数semctl()进行设置。•用户可以调用函数semop()对信号量集合中的一个或多个信号量进行操作。每一个操作都是sembuf结构变量:structsembuf{unsignedshortsem_num;/*在sem_base[]数组中的下标*/shortsem_op;shortsem_flg;};intsemop(intsemid,structsembuf*opsptr,size_tnops);semid:IPC资源IDopsptr:操作的集合nops:数组opsptr的大小内核必须保证操作数组opsptr原子的执行。sem_number指明是对哪一个信号操作。sem_flag指明一些操作标志位,可以有如下值:(1)SEM_UNDO。当进程结束但还拥有信号量资源时,应将信号量资源返还给相应的信号量集合。内核有一个sem_undo结构用于跟踪这方面的情况,进程描述符有个semundo成员记录进程这方面的信息。(2)IPC_NOWAIT。当操作不能立即完成时,若IPC_NOWAIT被设置的话,进程立即返回。否则进程进入睡眠状态,等待时机成熟时被唤醒完成该操作。sem_op指定具体的操作,它的值有如下含义:(1)大于0,则将该值加到信号量的当前值上。(2)等于0,那么用户希望信号量的当前值变为0。如果值已经是0,则立即返回。如果不是0,则取决于IPC_NOWAIT是否被设置。(3)小于0,则要看信号量的当前值是否大于等于sem_op的绝对值。如果大于等于它的绝对值,就从信号量的当前值中减去sem_op的绝对值。如果小于它的绝对值,则取决于IPC_NOWAIT是否被设置。•当进程的信号量操作不能完成睡眠时,需要将一个代表着当前进程的sem_queue结构链入相应的信号量集合的等待队列,即sem_array结构的sem_pending队列。structsem_queue{structsem_queue*next;/*队列中的下一个元素*/structsem_queue**prev;/*队列中的前一个元素*/structtask_struct*sleeper;/*睡眠进程的描述符*/structsem_undo*undo;intpid;/*睡眠进程的pid*/intstatus;structsem_array*sma;/*所属的信号量集合*/structsembuf*sops;/*挂起的操作数组*/intnsops;/*挂起的操作个数*/......};10.6.3消息队列•具有权限的进程可以往消息队列中读写消息,这就是消息队列支持进程通信的方式。•msgsnd()函数将消息放入队列中。intmsgsnd(intmsqid,constvoid*msgp,size_tmsgsz,intmsgflg);msqid:消息队列的资源ID号。msgp:消息缓冲区的首地址。消息缓冲区消息的类型及数据两部分组成。msgsz:消息缓冲区的长度。msgflg:可以是0,也可以是IPC_NOWAIT。•msgrcv()从某个消息队列中读一个消息并将其移出消息队列。intmsgrcv(intmsqid,void*msgp,intmsgsz,longmsgtyp,intmsgflg);msgp:接收消息的缓冲区首址。msgsz:接收缓冲区的大小,这是函数能返回的最大数据量。msgtyp:指定接收消息的类型。分为三种情况:值为0,返回队列中的第一个消息。值大于0,返回类型为msgtype的第一个消息。值小于0,则返回类型值小于或等于msgtype的绝对值的消息中类型值最小的第一个消息。msgflg:可以是IPC_NOWAIT,还可指定为MSG_NOERROR。MSG_NOERROR允许消息长度大于接收缓冲区长度时截短消息返回。•在内核中消息队列用msg_queue结构表示。structmsg_queue{structkern_ipc_permq_perm;time_tq_stime;/*最近一次msgsnd时间*/time_tq_rtime;/*最近一次msgrcv时间*/time_tq_ctime;/*最近的改变时间*/unsignedlongq_cbytes;/*队列中的字节数*/unsignedlongq_qnum;/*队列中的消息数目*/unsignedlongq_qbytes;/*队列中允许的最大字节数*/pid_tq_lspid;/*最近一次msgsnd()发送进程的pid*/pid_tq_lrpid;/*最近一次msgrcv()接收进程的pid*/structlist_headq_messages;/*消息队列*/structlist_headq_receivers;/*待接收消息的睡眠进程队列*/structlist_headq_senders;/*待发送消息的睡眠进程队列*/};•若IPC_NOWAIT未被设置,则当消息队列的容量已满时,发送消息的进程会进入睡眠状态并添加到相应的q_senders队列,而当消息队列中无合适的消息时,接收进程会进入睡眠状态并添加到相应的q_receivers队列。•消息队列中的每个消息都链入q_message队列中,每个消息用一个msg_msg结构描述。structmsg_msg{structlist_headm_list;/*消息队列链表*/longm_type;/*消息的类型*/intm_ts;/*消息的长度*/structmsg_msgseg*next;/*链接属于这个消息的下一个消息片*/};structmsg_msgseg{structmsg_msgseg*next;};msg_msg结构只是一个消息头部,并不包含消息的数据部分。数据部分的空间紧接msg_msg结构分配,但是当数据部分的空间与msg_msg结构所占空间大于一个页面时,则将其以页面为单位分片。第一个页面存储msg_msg结构与首部分数据,随后的再分配空间则存储structmsg_msgseg结构与剩余的数据,如果这两者所占空间之和仍大于一个页面,则继续分配下去。msg_msgseg结构用以把消息片链接在一起。10.6.4共享内存•共享内存是多个进程共享的一块内存区域。不同的进程可把共享内存映射到自己的一块地址空间,不同的进程进行映射的地址空间不一定相同。•共享内存区的进程对该区域的操作是互见的。•共享内存没有提供进程同步与互斥的机制,往往需要和信号量配合使用。•相比起其他进程通信方式,共享内存在进行数据交换方面是效率比较高的。无需用户态、核心态切换开销。•shmget()函数有一个参数指定共享内存区域的大小,该函数建立的共享内存区在内核中用shmid_kernel结构表示。structshmid_kernel{structkern_ipc_permshm_perm;structfile*shm_file;intid;unsignedlongshm_nattch;/
本文标题:106 SYSV进程间通信
链接地址:https://www.777doc.com/doc-308248 .html