System V IPC ---- 消息队列
一、System V 消息队列创建及操作流程:
二、System V 消息队列相关API:
Ⅰ、msgget()
Ⅱ、msgsnd() & msgrcv()
说明:
- msgp是一个由程序员自己定义的结构体指针,该结构体常规形式:
struct mymsg
{
long mtype; //消息类型
char mtext[]; //消息内容
}
1、其中,
mtext[]
长度和内容任意,无需是一个字符数组。因此,mgsp
参数的类型为void *
,这样就允许传入任意结构的指针。
2、使用msgsnd()
发送消息时,必须要将消息结构中的mtype
的值设为一个大于0
的值
3、使用msgrcv()
接收消息时,msgtyp
参数控制选择接收的消息:
msgtyp==0
时,删除队列中的第一条消息并将其返回给调用进程(符合队列的FIFO);msgtyp>0
时,将队列中 第一条mtype
等于msgtyp
的消息删除并将其返回给调用进程。 通过指定不同的msgtyp
值,多个进程能够从同一个消息队列中读取消息而不出现竞争读取同一条消息的情况。(一般都是用每个进程自己的进程ID进行匹配);msgtyp<0
时,删除并返回队列中mtype
小于msgtyp
绝对值的消息,如果这种消息有多个,则取第一个mtype
最小的消息。
- 每个消息队列都有一个关联的
msqid_ds
结构体,其形式如下:
struct msqid_ds
{
strcut ipc_perm msg_perm; //消息权限信息结构体
time_t msg_stime; //最后一次发送消息的时间
time_t msg_rtime; //最后一次接收消息的时间
time_t msg_ctime; //最后一次状态变更的时间
unsigned long _msg_cbytes; //当前消息队列中的数据尺寸
msgqnum_t msg_qnum; //当前消息队列中的消息个数
msglen_t msg_qbytes; //消息队列的最大数据尺寸
pid_t msg_lspid; //最后一个发送消息的进程PID
pid_t msg_lrpid; //最后一个接收消息的进程PID
};
其中,权限相关的消息用如下结构体来表示:
struct ipc_perm
{
key_t _key; //当前消息队列的key值
uid_t uid; //当前消息队列所有者的有效UID
gid_t gid; //当前消息队列所有者的有效GID
uid_t cuid; //当前消息队列创建者的有效UID
gid_t cgid; //当前消息队列创建者的有效GID
unsigned short mode; //消息队列的读写权限
unsigned short _seq; //序列号
};
- 当使用
IPC_INFO
(相关信息存放在/proc/sys/kernel
中)时,需要定义一个如下结构体来获取系统关于消息队列的限制信息,并且将这个结构体的指针强制类型转化为第三个参数的类型:
struct msginfo
{
int msgpool; //系统消息总尺寸(千字节为单位)的最大值
int msgmap; //系统消息个数最大值
int msgmax; //系统单个消息尺寸最大值
int msgmnb; //写入消息队列字节数最大值
int msgmni; //系统消息队列个数最大值
int msgssz; //消息段尺寸
int msgtql; //系统中所有消息队列中的消息总数最大值
unsigned short int msgseg; //分配给消息队列的数据段最大值
};
当使用选项
MSG_INFO
时,跟IPC_INFO
一样也是获得一个msginfo
结构体,但是有如下几点不同:
msgpool
记录的是系统当前存在的MSG
的个数总和msgmap
记录的是系统当前所有MSG
中的消息个数总和msgtql
记录的是系统当前所有MSG
中的所有消息的所有字节数总和
三、System V 消息队列的缺点:
Ⅰ、 System V 消息队列是通过标识符引用的,而不是文件描述符。这意味着各种基于文件描述符的I/O技术将无法应用于消息队列上。
Ⅱ、 使用键key
而不是文件名来标识消息队列会增加额外的程序设计复杂性,同时还需要使用ipcs
和ipcrm
来替换ls
和rm
。ftok()
函数通常能产生一个唯一的键,但却无法百分百保证。使用IPC_PRIVATE
能确保产生唯一的队列标识符,但需要使这个标识符对需要用到它的其他进程可见。
Ⅲ、 消息队列是无法连接的,内核不会像对待管道、FIFO以及socket那样维护引用队列的进程数。因此,就需要注意以下问题:
- 一个应用程序何时能够安全地删除一个消息队列?(不管是否有进程在后面某个时刻需要从队列中读取数据而过早地删除队列会导致数据丢失。<相对于其他采用文件描述符的I/O在接到删除指令会等待最后一个使用该文件的进程使用完毕后才会删除该文件来说的>)
- 应用程序如何确保不再使用的队列会被删除?
Ⅳ、 消息队列的总数、消息的大小以及单个队列的容量都是有限制的。(可以在/proc/sys/kernel
中对相应文件进行修改配置,但是操作麻烦,不方便移植)