/usr/include/linux/ipc.h
#define IPC_PRIVATE ((__kernel_key_t) 0)
struct ipc_perm
{
__kernel_key_t key; /* Key supplied to xxxget(2) */
__kernel_uid_t uid; /* Effective UID of owner */
__kernel_gid_t gid; /* Effective GID of owner */
__kernel_uid_t cuid; /* Effective UID of creator */
__kernel_gid_t cgid; /* Effective GID of creator */
__kernel_mode_t mode; /* Permissions */
unsigned short seq; /* Sequence number */
};
System V IPC 用到的一个公共结构体,其中,ipc_perm.key 被称之为 ipckey,
进程间通信本质上其实就是在内核中申请一定的资源,而 ipckey 就描述着内核中唯一一个消息队列(一块共享内存/一个信号量集)这样的资源,
毕竟一个操作系统可以创建出很多个消息队列
/usr/include/linux/msg.h
struct msqid_ds
{
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
描述消息队列的结构体
功能:专门用于生成 ipckey
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:存在且可访问的目录/文件
proj_id:虽说是 int 类型,但只有低 8 位有效
返回值:
成功返回 key_t(类似 pid_t) 值
失败返回 -1,并设置错误码
注意:传入的参数相同,生成的 key_t 值相同
功能:创建/打开一个消息队列
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数:
key:ftok() 返回值
msgflg:
IPC_CREAT:如果消息队列不存在就创建,如果存在就打开
用于打开一个消息队列
IPC_CREATE | IPC_EXCL | 0666:如果消息队列不存在就创建,如果存在就返回 -1
用于创建一个消息队列
返回值:
成功返回非 0 值的消息队列句柄
失败返回 -1,并设置错误码
/* ################################################################ */
功能:消息队列的控制函数
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
msqid:msgget() 返回值
cmd:
IPC_STAT:将与 msqid 相关联的 msqid_ds 数据结构的每个成员值放入 buf 指向的结构中
IPC_SET:在进程有足够权限的前提下,可以修改与 msqid 相关联的 msqid_ds 数据结构的以下成员值
msg_perm.uid
msg_perm.gid
msg_perm.mode
msg_qbytes:消息队列的总字节数
IPC_RMID:在进程有足够权限的前提下,删除消息队列
返回值:
成功返回 0
失败返回 -1,并设置错误码
/* ################################################################ */
功能:发送一条消息到消息队列中
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid:msgget() 返回值
struct msgbuf
{
long mtype; /* Message type, must > 0 */
char mtext[1]; /* Message text, max MSGMAX */
}
msgp:struct msgbuf
msgsz:sizeof(msgbuf.mtext)
msgflg:
& IPC_NOWAIT:队列满时不等待,返回 -1,并设置 EAGAIN 错误码
返回值:
成功返回 0
失败返回 -1,并设置错误码
/* ################################################################ */
功能:从消息队列中接收一条消息
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
msqid:msgget() 返回值
msgp:struct msgbuf
msgsz:sizeof(msgbuf.mtext)
msgtyp:
= 0:接收队列中第一条消息
> 0:接收队列中类型等于 msgtype 的第一条消息
< 0:接收队列中 <= msgtyp 绝对值的最低类型的第一条消息
msgflg:
& IPC_NOWAIT:队列没有类型等于 msgtyp 的消息时不等待,返回 -1,并设置 ENOMSG 错误码
& MSG_NOERROR:接收到的消息长度大于 msgsz 时,将其截断成 msgsz 字节大小
& MSG_NOERROR is 0:接收到的消息长度大于 msgsz 时,返回 -1,并设置 E2BIG 错误码
& MSG_EXCEPT && msgtype > 0:接收类型不等于 msgtype 的第一条消息
返回值:
成功返回实际放入缓冲区 msg_buf.mtext 的字节数
失败返回 -1,并设置错误码
特点
1、全双工通信
2、可以发送带有类型的数据块,此处的类型指的是具体的业务场景
3、操作系统上的消息队列总数有一个上限(MSGMNI),每个消息队列的总字节数有一个上限(MSGMNB,每个消息队列中所有消息长度总和 Σsizeof(msgbuf.mtext) 最大是 16384,测试验证),每个消息的字节数有一个上限(MSGMAX)
$ ipcs -q -l
------ Messages Limits --------
max queues system wide = 32000
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384
$
4、消息队列是数据结构-队列的应用,但消息队列并没有严格遵守先进先出规则,根据特点2,消息队列严格遵守基于类型的先进先出规则,举个现实生活中的例子,超声科室出来个医生说:“过来一个检查胃的”,此时即便产检的排在队列的第一位,也不是她出队,而是在检查胃中排在最前的先出
5、进程退出,如果代码中并没有销毁(msgctl(IPC_RMID))动作的话,消息队列是不会被释放的,也就是消息队列的生命周期随内核,当然也可以通过 ipcrm -q +msqid 手动删除,或者重启主机
$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 0 root 660 0 0
0x00000000 1 root 660 0 0
0x00000000 2 root 660 0 0
0x01026636 16 mam 666 0 0
$ ipcrm -q 16
$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 0 root 660 0 0
0x00000000 1 root 660 0 0
0x00000000 2 root 660 0 0
$