利用进程间通信IPC中的三个对象:共享内存、信号灯数组、消息队列,解决并发进程同步与互斥的问题
- 共享内存是 OS 内核为并发进程间交换数据而提供的一块内存区(段)。如果段
的权限设置恰当,每个要访问该段内存的进程都可以把它映射到自己私有的地址空
间中。如果一进程更新了段中数据,那么其他进程立即会看到这一更新。进程创建
的段也可由另一进程读写。 (ipcs -m) - 信号灯数组是 OS 内核控制并发进程间共享资源的一种进程同步与互斥机制。 (ipcs -s)
- 消息队列是 OS 内核控制并发进程间共享资源的另一种进程同步机制。 (ipcs -q)
在权限允许的情况下您可以使用 ipcrm 命令删除系统当前存在的 IPC 对象中的任一个对象。
ipcrm -m 21482 删除标号为 21482 的共享内存。 ipcrm -s 32673 删除标号为 32673 的信号灯数组。 ipcrm -q 18465 删除标号为 18465 的消息队列。在 linux 的 proc 文件系统中有 3 个虚拟文件动态记录了由以上 ipcs 命令显示的
当前 IPC 对象的信息,它们分别是:
- /proc/sysvipc/shm 共享内存
- /proc/sysvipc/sem 信号量
- /proc/sysvipc/msg 消息队列
创建共享内存
#include <sys/shm.h> int shmget(key_t key,int size,int flags); key 共享内存的键值,可以为 IPC_PRIVATE,也可以用整数指定一个 size 共享内存字节长度 flags 共享内存权限位。 shmget 调用成功后,如果 key 用新整数指定,且 flags 中设置了 IPC_CREAT位,则返回一个新建立的共享内存段标识符。 如果指定的 key 已存在则返回与 key关联的标识符。 不成功返回-1
令一段共享内存附加到调用进程
#include <sys/shm.h> char *shmat(int shmid, char *shmaddr,int flags) shmid 由 shmget 创建的共享内存的标识符 shmaddr 总为 0,表示用调用者指定的指针指向共享段 flags 共享内存权限位 shmat 调用成功后返回附加的共享内存首地址
令一段共享内存从调用进程中分离出去
#include <sys/shm.h> int shmdt(char *shmadr); shmadr 进程中指向附加共享内存的指针 shmdt 调用成功将递减附加计数,当计数为 0,将删除共享内存。调用不成功返回-1。
创建信号灯数组
#include <sys/sem.h> int semget(key_t key,int nsems, int flags); key 信号灯数组的键值,可以为 IPC_PRIVATE,也可以用整数指定一个 nsems 信号灯数组中信号灯的个数 flags 信号灯数组权限位。如果 key 用整数指定,应设置 IPC_CREAT 位。 semget 调用成功,如果 key 用新整数指定,且 flags 中设置了 IPC_CREAT 位,则 返回一个新建立的信号等数组标识符。 如果指定的整数 key 已存在则返回与 key 关联的标识符。 不成功返回-1
操作信号灯数组
#include <sys/sem.h> int semop(int semid,struct sembuf *semop, unsigned nops); semid 由 semget 创建的信号灯数组的标识符 semop 指向 sembuf 数据结构的指针 nops 信号灯数组元素的个数。 semop 调用成功返回 0,不成功返回-1。
控制信号灯数组
#include <sys/sem.h> int semctl(int semid,int semnum,int cmd, union semun arg); semid 由 semget 创建的信号灯数组的标识符 semnum 该信号灯数组中的第几个信号灯 cmd 对信号灯发出的控制命令。例如: GETVAL 返回当前信号灯状态 SETVAL 设置信号灯状态 IPC_RMD 删除标号为 semid 的信号灯 arg 保存信号灯状态的联合体,信号灯的值是其中一个基本成员 union semun { int val; /* value for SETVAL */ ...... }; semctl 执行不成功返回-1,否则返回指定的 cmd 的值。
创建消息队列
#include<sys/msg.h> int msgget(key_t key,int flags) key 消息队列的键值,可以为 IPC_PRIVATE,也可以用整数指定一个 flags 消息队列权限位。 msgget 调用成功,如果 key 用新整数指定,且 flags 中设置了 IPC_CREAT位,则返回一个新建立的消息队列标识符。 如果指定的整数 key 已存在则返回与 key 关联的标识符。成功返回-1。
从到消息队列中读出一条新消息
#include <sys.msg.h> int msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtype, int msgflg); msqid 由消息队列的标识符 msgp 消息缓冲区指针。消息缓冲区结构为: struct msgbuf { long mtype; /* 消息类型,必须大于0 */ char mtext[1]; /* 消息数据,长度应于 msgsz 声明的一致*/ } msgsz 消息数据的长度 msgtype 决定从队列中返回哪条消息: =0 返回消息队列中第一条消息 >0 返回消息队列中等于 mtype 类型的第一条消息。 <0 返回 mtype<=type 绝对值最小值的第一条消息。 msgflg 为0表示阻塞方式,设置 IPC_NOWAIT 表示非阻塞方式 msgrcv 调用成功返回 0,不成功返回-1。
删除消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf); msqid 由消息队列的标识符 cmd 控制命令。常用的有: IPC_RMID 删除 msgid 标识的消息队列 IPC_STAT 为非破坏性读,从队列中读出一个 msgid_ds 结构填充缓冲 buf IPC_SET 改变队列的 UID,GID,访问模式和最大字节数。 msgctl 调用成功返回 0,不成功返回-1。