10-6Linux操作系统 - SYSV进程间通信
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
sem_op指定具体的操作,它的值有如下含义: (1)大于0,则将该值加到信号量的当前值上。 (2)等于0,那么用户希望信号量的当前值变为0。 如果值已经是0,则立即返回。如果不是0,则取 决于IPC_NOWAIT是否被设置。 (3)小于0,则要看信号量的当前值是否大于等 于sem_op的绝对值。如果大于等于,就从信号量 的当前值中减去sem_op的绝对值。如果小于,则 取决于IPC_NOWAIT是否被设置。
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资源。否则返回一个错误信息。
struct msg_msg { struct list_head m_list; /*消息队列链表*/ long m_type; /* 消息的类型 */ int m_ts; /* 消息的长度 */ struct msg_msgseg* next; /* 链接属于这个消息的下 一个消息片*/ };
struct msg_msgseg { struct msg_msgseg* next; };
int semop(int semid, struct sembuf *opsptr, size_t nops); semid : IPC资源ID opsptr: 操作的集合 nops: 数组opsptr的大小
内核必须保证操作数组opsptr原子地执行 每一个操作都是sembuf结构变量
struct sembuf { unsigned short sem_num;/*在sem_base[] 数组中的下标*/ short sem_op; short sem_flg; };
•在内核中消息队列用msg_queue结构表示,
struct msg_queue { struct kern_ipc_perm q_perm; time_t q_stime; /*最近一次msgsnd时间*/ time_t q_rtime; /*最近一次msgrcv 时间*/ time_t q_ctime; /* 最近的改变时间 */ unsigned long q_cbytes; /* 队列中的字节数 */ unsigned long q_qnum; /* 队列中的消息数目 */ unsigned long q_qbytes;/*队列中允许的最大字节数 */ pid_t q_lspid;/*最近一次msgsnd()发送进程的pid */ pid_t q_lrpid;/*最近一次msgrcv()接收进程的pid */ struct list_head q_messages; /*消息队列*/ struct list_head q_receivers; /*待接收消息的睡 眠进程队列*/ struct list_head q_senders; /*待发送消息的睡眠 进程队列*/ };
10.6.3 消息队列 •具有权限的进程可以往消息队列中读写消息, 这就是消息队列支持进程通信的方式。
•msgsnd()函数将消息放入队列中
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); msqid: 消息队列的资源ID号 msgp: 消息缓冲区的首地址。消息缓冲区消息的类 型及数据部分两部分组成。 msgsz:消息缓冲区的长度。 msgflg:可以是0,也可以是IPC_NOWAIT。
msg_msg结构只是一个消息头部,并不包含消息 的数据部分。数据部分的空间紧接msg_msg结构 分配,但是当数据部分的空间与msg_msg结构所 占空间大于一个页面时,则将其以页面为单位分 片。第一个页面存储msg_msg结构与首部分数据, 随后的再分配空间则存储struct msg_msgseg结 构与剩余的数据,如果这两者所占空间之和仍大 于一个页面,则继续分配下去。msg_msgseg结构 用以把消息片链接在一起
因 为 每 个 IPC 资 源 描 述 符 的 第 一 个 成 员 就 是 kern_ipc_perm结构。因此,我们可以认为数组 entries 的每一非空项均指向一个IPC资源。
IPC资源ID与entries数组下标的联系 (1)当创建一个IPC资源时, •调 用 函 数 ipc_addid() 从 相 应 ipc_ids 结 构 的 entries数组中找出第一个未使用的项然后返回 其下标index。 •返回IPC资源ID
•若IPC_NOWAIT未被设置,则当消息队列的容量已 满时发送消息的进程会进入睡眠状态并添加到相 应的q_senders队列,而当消息队列中无合适的消 息时接收进程会进入睡眠状态并添加到相应的 q_receivers队列。 •消息队列中的每个消息都链入q_message队列中, 每个消息用一个msg_msg结构描述
•当进程的信号量操作不能完成睡眠时,需要将 一个代表着当前进程的sem_queue结构链入相应 的信号量集合的等待队列,即sem_array结构的 sem_pending队列。
struct sem_queue { struct sem_queue *next; /*队列中的下一个元素*/ struct sem_queue **prev; /*队列中的前一个元素*/ struct task_struct *sleeper;/* 睡眠进程的描述符*/ struct sem_undo * undo; int pid; /* 睡眠进程的pid */ int status; struct sem_array *sma; /* 所属的信号量集合 */ struct sembuf *sops; /* 挂起的操作数组 */ int nsops; /* 挂起的操作个数*/ ...... };
共同的数据结构 每一类IPC资源都有一个ipc_ids结构的全局变 量用来描述同一类资源的公有数据,三个全局变 量分别是semid_ds,msgid_ds和shmid_ds。
struct ipc_ids { int size; /* entries数组的大小*/ int in_use; /* entries数组已使用的元素个数*/ int max_id; unsigned short seq; unsigned short seq_max; truct semaphore sem; /*控制对ipc_ids结构的访问*/ spinlock_t ary;/*自旋锁控制对数组entries的访问*/ struct ipc_id* entries; }; struct ipc_id { struct kern_ipc_perm* p;};
IPC资源ID = SEQ_MULTIPLIER * seq + index
SEQ_MULTIPLIER是可用资源的最大数目,seq是 ipc_ids结构中的seq。每当分配一个IPC资源时, ipc_ids结构中的seq就增一。 (2)当知道IPC资源ID时,可通过 IPC 资 源 ID % SEQ_MULTIPLIER 得到其在entries数组中的index,从而找到相应 的IPC资源。 (3)why 保证在一段时期内IPC资源ID的唯一性
数组entries的每一项指向一个kern_ipc_perm 结构,kern_ipc_perm结构表示每一个IPC资源的 属性,用来控制操作权限。
struct kern_ipc_perm{ key_t key;/*用户提供的键值,为XXXget()所用*/ uid_t uid; /*创建者用户ID*/ gid_t gid; /*创建者组ID*/ uid_t cuid; /*所有者用户ID*/ gid_t cgid; /*所有者组ID*/ mode_t mode; /*操作权限,包括读、写等*/ unsigned long seq; };
•msgrcv()从某个消息队列中读一个消息并将其 移出消息队列。
int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg); msgp:接收消息的缓冲区首址。 msgsz:接收缓冲区的大小,这是函数能返回的最大 数据量。 msgtyp:指定接收消息的类型。分为三种情况: 值为0,返回队列中的第一个消息。 值大于0,返回类型为msgtype的第一个消息。 值小于0,则返回类型值小于或等于msgtype 的绝对值的消息中类型值最小的第一个消息。 msgflg : 可 以 是 IPC_NOWAIT , 还 可 指 定 为 MSG_NOERROR。MSG_NOERROR允许消息长度大于接收缓冲 区长度时截短消息返回。
sem_number指明是对哪一个信号操作。 sem_flag指明一些操作标志位,可以有如下值: (1)SEM_UNDO 当进程结束但还拥有信号量资源 时,应将信号量资源返还给相应的信 号量集 合。内核有一个sem_undo结构用于跟踪这方面的 情况,进程描述符有个semundo成员记录进程这 方面的信息。 ( 2 ) IPC_NOWAIT 当 操 作 不 能 立 即 完 成 时 , IPC_NOWAIT被设置的话进程立即返回 否则进程进入睡眠状态等待时机成熟时被唤 醒完成该操作。
信 号 量 集 合 中 的 每 一 个 信 号 用 结 构sem表 示 ,
struct sem { int semval;/* 信号量的当前值 */ int sempid;/* 最近对信号量操作进程的pid */ };
信号量的初始值可以调用函数semctl()进行设 置。
•用户可以调用函数semop()对信号量集合中的 一个或多个信号量进行操作。10.6Fra bibliotek2信号量
•信号量是具有整数值的对象,它支持P、V原语。 进程可以利用信号量实现同步和互斥 •SYSV支持的信号量实质上是一个信号量集合,由 多个单独的信号量组成。我们称SYSV信号量为信 号量集合,而单个的信号量直接称为信号量。 •信号量集合在内核中用结构sem_array表示
struct sem_array { struct kern_ipc_perm sem_perm; time_t sem_otime; /* 最近一次操作时间 */ time_t sem_ctime; /* 最近一次的改变时间 */ struct sem *sem_base; /*指向第一个信号量 */ struct sem_queue *sem_pending;/* 挂起操作队列*/ struct sem_queue **sem_pending_last; struct sem_undo *undo; unsigned long }; sem_nsems;/*信号量的个数*/
(b)XXXctl() 均提供IPC_SET、IPC_STAT和 IPC_RMID命令。前两者用来设置或得到IPC资源 的状态信息,IPC_RMID用来释放IPC资源。 共同的操作模式 •都是先通过XXXget()创建一个IPC资源,返回 值是该IPC资源ID。在以后的操作中,均以IPC资 源ID为参数以对相应的IPC资源进行操作。 •别的进程可以通过XXXget()取得已有的IPC资 源ID(权限允许的话)到并对其操作,从而进程 间通信成为可能。
10.6
内容
SYS V 进程间通信
• 信号量
• 消息队列
• 共享内存
10.6.1 三者共有的特性 IPC资源 表示单独的消息队列、共享内存或是 信号量集合
三者均有XXXget()及XXXctl()函数
代表msg、sem、shm三者之一)
(XXX
(a) XXXget()函数
两个共同参数:key和oflag。