title: 进程间的通信————共享内存、消息队列
date: 2019-08-08 12:03:23
tags: [Linux,进程]
categories: Linux
1、共享内存
共享内存是进程间通信(IPC)的一种。因为进程在使用共享内存时,可以直接读写内存,不需要任何数据拷贝,所以它的效率是最高的。关于共享内存是内核专门留出来的一块内存,这段内存可以让访问的进程映射到自己的私有地址空间,从而可以操作共享内存。 共享内存可以让不同的进程来映射地址,从而实现了进程之间的通信。
共享内存是进程中通信效率最高的一种方式。
共享内存的操作步骤:
- 创建共享内存 ,打开共享内存
- 向各自的进程建立映射关系
- 读写操作
- 撤销映射关系
- 删除共享内存
①、 创建或打开共享内存
函数原型 | int shmget(key_t key, size_t size, int shmflg); |
参数 | key:标识共享内存,该值通过ftok()函数创建,函数定义在下面 size:创建共享内存的大小,以字节为单位 shmflg:创建的方式与open的最后一个参数类似 IPC_CREAT:创建共享内存 IPC_EXCL: 与 IPC_CREAT 进行连用检测共享内存是否创建,如果创建则报错 一般使用 :(IPC_CREAT|0666) 创建共享内存权限为读写 |
返回值 | 成功:共享内存的ID,用来标识该内存 失败:-1 |
创建key值,
函数原型 | key_t ftok(const char *pathname, int proj_id); |
参数 | pathname:创建键值的路径文件 该参数也可以是IPC_PRIVATE :一般使用在具有亲缘关系的进程间使用 proj_id:取值范围,是1-255的范围。通信的两个进程该值必须相同 |
返回值 | 成功返回的是 key 值 |
②、 映射共享内存
函数原型 | void *shmat(int shmid, const void *shmaddr, int shmflg); |
功能 | 当前进程与共享内存之间建立映射关系 |
参数 | shmid:共享内存的 id,shmget() 的返回值 shmaddr:建立的映射区域,一般用 NULL 标识自动分配映射地址 shmflg:一般给 0值 ,或者在这个基础上 可以 SHM_RDONLY |
返回值 | 成功: 映射后的地址 失败: (void *) -1 |
③、撤销映射
函数原型 | int shmdt(const void *shmaddr); |
功能 | 撤销映射关系 |
参数 | shmaddr :shmat ()返回值,映射后的地址 |
返回值 | 成功:0 失败:-1 |
④、删除共享内存(对共享内存的控制)
函数原型 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
参数 | shmid:共享内存的id,shmget()的返回值 cmd: IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中 IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内 IPC_RMID :删除这片共享内存 buf:共享内存管理结构体。具体说明参见共享内存内核结构定义部分,删除给NULL |
返回值 | 成功:0 失败:-1,设error number |
案例:
现象:
2、消息队列
消息队列与 FIFO 很相似,都可以有多个进程往里面写信息。消息队列多个进程从队列中读取信息。但 FIFO 需要读、写的两端事先都打开,才能够开始信息传递工作。而消息队列可以事先往队列中写信息,需要时再打开读取信息。
主要通信的特点 :
按照消息的类型发送和接收消息 。 使用一个消息队列 先要先消息队列中写入内容,不同进程可以按照消息类型来获取消息 。
通信步骤:
- 使用消息队列先要 创建消息队列 msgget
- 可以向消息队列写入消息 (先写入消息会将消息发送) msgsnd
- 可以从消息队列读取消息(接收消息)msgrcv
- 控制消息队列 :------销毁消息队列 : msgctl
①、创建打开消息队列
函数原型 | int msgget(key_t key, int msgflg); |
参数 | key: 键值 标识当前的消息队列,键值通过ftok()函数得来,上面有介绍 也可以是IPC_PRIVATE(值 0) : 只能自己创建的进程来使用 ,一般具有亲缘关系的进程间使用 msgflg:与open的最后一个参数类似。例如:(IPC_CREAT|0777) IPC_CREAT:创建消息队列 **IPC_EXCL : **IPC_CREAT 与之连用 检测消息队列是否创建成功 |
返回值 | 成功:消息队列的ID 失败:-1 |
②、添加消息到队尾,并且发送消息
函数原型 | int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); |
函数功能 | 向消息队列添加消息并且发送 |
参数 | msgid:msgget()的返回值,即消息队列的ID msgp:它的指向是一个结构体,该结构体需要自己定义 struct msgbuf{ long mtype; //消息类型 一般的取值 >0 ,区分不同进程的数据 char mtext[1]; //消息正文 ------用来存储消息,大小可以改变 }; msgsz : 发送消息的大小,sizeof该结构体变量 msgflg: IPC_NOWAIT : 不阻塞 操作 0 :表示阻塞状态 直到满足条件之后继续往下执行 |
返回值 | 成功:0 失败:-1 |
③、按类型读消息
函数原型 | ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); |
功能 | 按照消息类型接受消息,不同进程的消息是根据消息类型来区分的 |
参数 | msqid:msgget()的返回值 ,消息id号 msgp:它是指向一个结构体的指针,该结构体要自己定义 struct msgbuf{ long mtype; //消息类型 一般的取值 >0 ,区分不同进程的数据 char mtext[1]; //消息正文 ------用来存储消息,大小可以改变 }; msgsz:接受消息的大小 msgtyp:消息的类型 msgflg: IPC_NOWAIT:不阻塞 操作 0:阻塞操作 |
返回值 | 成功:接受到消息的字节数 失败:-1 |
④、删除消息队列
函数原型 | int msgctl(int msqid, int cmd, struct msqid_ds *buf); |
函数功能 | 对消息队列的设置 ------可以删除消息队列 |
参数 | msqid:消息队列的ID,msgget()的返回值 cmd:要发送的命令,这里有好几个。删除的命令是IPC_RMID buf:清空消息队列的结构体,设置为NULL即可 |
返回值 | 成功:0 失败:-1 |
案列:实现一个进程给其他两个进程群发送数据
实验现象: