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

案例:

共享内存 加上消息队列 共享内存和消息队列_共享内存

现象:

共享内存 加上消息队列 共享内存和消息队列_共享内存 加上消息队列_02

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

案列:实现一个进程给其他两个进程群发送数据

共享内存 加上消息队列 共享内存和消息队列_共享内存_03

共享内存 加上消息队列 共享内存和消息队列_共享内存 加上消息队列_04

实验现象:

共享内存 加上消息队列 共享内存和消息队列_共享内存 加上消息队列_05