> 文章列表 > 【Linux】System V IPC-命名管道共享内存消息队列

【Linux】System V IPC-命名管道共享内存消息队列

【Linux】System V IPC-命名管道共享内存消息队列

System V IPC-命名管道&共享内存&消息队列

  • 命名管道
  • 共享内存
    • 创建共享内存
    • 附加和分离共享内存
  • 消息队列
    • 消息队列的接口

命名管道

使用mkfifo命令,创建一个命名管道,通过ll可以查看当前命名管道的类型
【Linux】System V IPC-命名管道共享内存消息队列
p类型,也就是pipe管道类型。

之前我们说,匿名管道是没有名字的,只能用于有亲缘性的进程之间进行通信,而命名管道,由于有自己的名字,所以可以通过名字来找到这个管道,因此没有亲缘性的要求了。

匿名管道 命名管道
是通过内核提供的通道实现 通过文件系统实现
通过pipe打开,打开文件描述符 通过mkfifo打开
无需创建空间 需要手动通过mkfifo创建管道
要求亲缘性进程之间 没有亲缘性要求
生命周期随着进程结束而终止 生命周期同操作系统

共享内存

共享内存的原理是:在物理内存中开辟一段空间,不同的进程通过页表将物理内存空间映射到自己的进程虚拟地址空间中,不同的进程通过操作自己的进程虚拟地址空间来操作共享内存。

【Linux】System V IPC-命名管道共享内存消息队列
了解原理之后,我们需要知道如何创建共享内存,以及如何附加和分离共享内存到进程中。

创建共享内存

int shmget(key_t key,size_t size,int shmflg);

功能:用来创建共享内存
参数
key:这个共享内存段名字,0(IPC_PRIVATE)会创建新的共享内存对象
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段

附加和分离共享内存

附加:

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:将共享内存段连接到进程地址空间
参数
shmid: 共享内存标识
shmaddr:指定连接的地址,一般传NULL,让操作系统分配
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个地址;失败返回-1

分离:

 int shmdt(const void *shmaddr);

功能:将共享内存段与当前进程脱离
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

操作共享内存:

 int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:用于控制共享内存
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)(IPC_RMID)删除共享内存,(IPC_SET)设置共享内存属性信息,(IPC_STAT)获取共享内存属性信息
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

共享内存有一些特性:
1.共享内存的生命周期是跟随操作系统的
2.进程对共享内存进行写的时候是覆盖写,读的时候是访问地址:也就是覆盖写,访问读
3.(重点)要删除共享内存时,有一些删除特性。

使用ipcs -m 可以查看当前的共享内存信息
【Linux】System V IPC-命名管道共享内存消息队列

要删除共享内存,除了使用shmctl,设置cmd为IPC_RMID,还可以使用命令删除ipcrm -m 共享内存操作句柄
【Linux】System V IPC-命名管道共享内存消息队列

其实在删除的时候是分两种情况的:
1.删除的时候没有进程附加到这个共享内存,那么就会直接将当前的共享内存删除,并且在内核中描述当前共享内存的结构体也被释放了。
2.当前共享内存的附加进程不为0,会将当前共享内存的key设为0x00000000,,表示当前共享内存不能被其他进程所附加,共享内存的状态被设置为destory。一旦当前共享内存的附加进程为0,当前共享内存在内核中的结构体就会被释放。

【Linux】System V IPC-命名管道共享内存消息队列

消息队列

消息队列的原理:
【Linux】System V IPC-命名管道共享内存消息队列

采用链表来实现消息队列,该链表是由系统内核维护的。系统中可能存在多个消息队列,每个消息队列都有自己的消息队列描述符(消息队列ID),区分不同的消息队列。
在进行进程间通信时,一个进程将消息队列添加到尾端,另一个进程从消息队列中取出消息(并不一定是以先进先出的方式,也可以按照消息类型字段先进先出)

消息队列的接口

创建消息队列:

int msgget(key_t key, int msgflg);

参数:
key表示消息队列的标识符
msgflg:创建的标志,例如IPC_CREATE | 权限
返回值:
创建成功队列的ID,创建失败返回-1

发送消息:

int msgsnd(int msgid, const void* msgp, size_t msgsz, int msgflg);

参数:
msgid:消息队列id
msgp:指向msgbuf的指针,用来指定发送的信息
msgsz:发送信息的长度
msgflg:创建标记,如果指定IPC_NOWAIT(非阻塞), 失败会立刻返回
返回值:
成功返回0,失败返回-1

接收消息:

ssize_t msgrcv(int msgid, void* msgp, size_t msgsz, long msgtyp, int msgflg);

参数:
msgid:消息队列id
msgp:指向msgbuf的指针,用于接收消息
msgsz:接收消息的长度
msgtyp接收消息的方式
msgtyp=0:直接读取队列中第一条消息
msgtyp>0,读取队列类型中msgtyp类型的第一条消息(如果在msgflg中设置了MSG_EXCEPT,则按照前面的方式出队,如果没有设置,则出的是msgtyp之外类型的第一条消息)
msgtyp<0,读取队列类型中小于或等于msgtyp绝对值的第一条消息
msgflg:创建标记,如果指定IPC_NOWAIT,则失败会立刻返回

返回值:
成功返回0,失败返回-1

操作消息队列的接口:

int msgctl(int msgid, int cmd, struct msgid_ds* buf);

参数:
msgid:消息队列id
cmd:控制命令,可以设置为IPC_RMID(删除消息队列),IPC_STAT(获取消息队列状态)
buf:存储消息队列的相关信息的buf
返回值:
成功根据不同cmd返回不同值,失败返回-1