什么是消息队列
消息队列是消息的
链表
,存储在内核中,由消息队列标识符标识。消息顺序地发送到队列中,并以不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地识别。内核中的消息队列是通过IPC的标识符进行识别的,不同的消息队列之间是相互独立的,每个消息队列中的消息,又构成一个独立的链表。
特点
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会删除。
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
内核中的每个消息队列都有一个相应的结构体与其关联:
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 */
};
其中,消息结构体长这个样子:
struct msgbuf {
long mtype; /* type of message */
char mtext[1]; /* message text */
};
创建或获取消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数
- key :
- 如果要创建消息队列,则msgflg为IPC_CREAT | IPC_EXCL外加权限。
- 如果要获得消息队列,则msgfla为IPC_CREATE。
返回值
- 成功:消息队列标识符(非负整数)
- 失败:-1,errno
当一个消息队列被创建时,该消息队列相关联的数据结构msgqid_ds被初始化:
- msg_perm.cuid 和 msg_perm.uid 被初始化为开启进程的用户ID
- msg_perm.cgid 和 msg_perm.gid 被初始化为开启进程的组ID
- msg_perm.mode 被初始化为msgflg中的权限值
- msg_qnum, msg_lspid, msg_lrpid, msg_stime, msg_rtime 被初始化为0
- msg_ctime 被初始化为当前时间
- msg_qbytes 被初始化为MSGMNB
操作消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数
- msqid: 消息队列标识符
- cmd: 操作命令
- IPC_STAT: 将内核中的msqid复制到buf所指向的 msgqid_ds
- IPC_SET: 将字段msg_perm.uid、msg_perm.gid、msg_perm.mode、msg_qbytes从buf指向的结构复制到与这个队列相关的msqid_ds结构中。这种命令只能由下列两种进程执行:一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid;另一种是具有超级用户特权的进程。只有超级用户才能增加msg_qbytes的值。
- IPC_RMID: 立即删除消息队列。
- buf: 消息队列结构体指针
发送消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg);
参数
- msqid: 消息队列标识符
- msgp: 消息,可以自定义,但是第一个必须是long类型,第二个可以是自定义的任意类型
- msgsz: msgbuf.mtext的长度
- msgflg:
- IPC_NOWAIT: 如果没有指定类型的消息可读则立即返回,errno被置为ENOMSG
- MSG_EXCEPT: 任意读取一个不是msgtyp类型的消息
- MSG_NOERROR: 如果消息体的长度超过了msgsz,则截断消息
返回值
- 成功:0,消息队列会自动更新
- msg_lspid: 被置为发送进程的ID
- msg_qnum: 自增1
- msg_stime: 设置为当前时间
- 失败:-1,errno被设置为相应的错误
- 指定了IPC_NOWAIT且消息队列满,errno被置为EAGAIN
- 当队列被删除时,errno被设置为EIDRM
- 捕捉到信号,errno被置为EINTR(msgsnd()被打断后不会自动重启,除非在定义信号处理函数的时候指定了SA_RESTART)
接收消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
参数
- msqid: 消息队列标识符
- msgp: 消息,可以自定义,但是第一个必须是long类型,第二个可以是自定义的任意类型
- msgsz: msgbuf.mtext的长度
- msgtyp: 消息类型
- =0: 读取消息队列中的第一个消息
- >0: 读取消息队列中类型为msgtyp的第一个消息,若指定了MSG_EXCEPT,则读取类型不为msgtyp的第一个消息
- <0: 读取消息队列中类型小于等于msgtyp的绝对值的第一个消息
- msgflg:
- IPC_NOWAIT: 如果没有指定类型的消息可读则立即返回,errno被置为ENOMSG
- MSG_EXCEPT: 任意读取一个不是msgtyp类型的消息
- MSG_NOERROR: 如果消息体的长度超过了msgsz,则截断消息
返回值
- 成功:复制到msgbuf.mtext中的字节数,消息队列会自动更新
- msg_lrpid: 设置为进程ID
- msg_qnum: 自减1
- msg_rtime: 设置为当前时间
- 失败:-1,errno
- 指定了IPC_NOWAIT且没有指定的消息可读,errno被置为EAGAIN
- 消息队列被删除,errno被设置为EIDRM
- 捕捉到信号,errno被置为EINTR(msgrcv()被打断后不会自动重启,除非在定义信号处理函数的时候指定了SA_RESTART)