消息队列的原理
- 消息队列允许进程以消息的形式交换数据。
- 消息队列进行的通信是面向消息的,即读者接收到写者写入的整条消息。读取一条消息的一部分而让剩余部分遗留在队列中或一次读取多条消息都是不可能的。这一点与管道不同,管道提供的是一个无法区分的字节流(即使使用管道时读者一次可以读取任意数量的字节数,不管写者写入的数据块大小是什么)。
- 除了包含数据外,每条消息还有一个用整数表示的类型。从消息队列中读消息既可以按照先入先出的顺序,也可以根据类型来读取消息。
- 此外,System V消息队列存在一些限制。所以新应用程序应该尽可能避免使用System V消息队列,而应该使用其他形式的IPC机制,如FIFO、POSIX消息队列以及socket。但是还存在一些既有应用程序在使用,所以我们应该对这种消息队列有所了解。
消息队列的接口
头文件
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key, int msgflg);
成功返回唯一的消息队列标识符,失败返回-1。
key 1.可以IPC_PRIVATE(值为0),创建一个只有创建者进程才能访问的消息队列,用于父子进程间通信。
2.可以由fork函数获得非0值的key,创建一个可以被多个进程使用的消息队列。
msgflg 1.指明队列的访问权限和队列标志。
2.常用为IPC_CREAT,如果消息队列不存在就创建,如果消息队列存在,则返回具有相同key的消息队列id。
3.IPC_EXCL可以与上述IPC_CREAT一同使用,如果消息队列存在,调用失败返回-1.
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
调用成功返回0,失败返回-1。
如果消息队列不存在,errno置为EIDRM;
如果捕获到一个信号,这时候系统调用失败,errno置为EINTR。msgsnd不会自动重发。
msqid 消息队列id
msgp 一个指针,指向下列结构体
struct msgbuf{
long mtype; //信息类型,必须大于0
char mtext[1]; //信息的数据内容
};
mtext是一个有msgsz指定大小的数组。
msgsz 消息块大小,每个消息块最大不要超过4K
msgflg 标志位
msgflg
1为0(常用)。
2为IPC_NOWAIT,此时,msgsnd和msgrcv都不会阻塞,如果队列满并调用msgsnd,或者队列空调用msgrcv将返回错误。
ssize_t msgrcv(int msqid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
调用成功返回消息块字节数,失败返回-1
前三个和前面一样不再重复。
这个函数主要用于接收消息队列中的数据。
msgtyp
1.如果为0(常用),接收队列中第一个消息
2.如果大于0,接收队列中第一个类型等于msgtyp的消息
3.如果小于0,接收其类型小于或等于msgtyp绝对值中最小类型的第一个消息。
msgflg
1为0(常用)。
2为IPC_NOWAIT,此时,msgsnd和msgrcv都不会阻塞,如果队列满并调用msgsnd,或者队列空调用msgrcv将返回错误。
3当接受长度小于消息队列中对应消息的长度,如果msgflg设置了MSG_NOERROR,则msgrcv函数会将消息块截断,后边多出部分丢弃。
如果没有设置MSG_NOERROR,返回-1,并将errno设置为E2BIG。
注:msgp.mtext需要置0(用memset),因为msgrcv函数只会将消息队列中的消息传入msgp.mtext中,不会在后面添加'\0'。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数msqid 为消息队列标识符。
参数cmd 为
IPC_RMID表示删除队列。
IPC_STAT将与这个消息队列有关联的msqid_ds数据结构的副本放到buf指向的缓冲区中。
IPC_SET使用buf指向的缓冲区提供的值更新与这个消息队列关联的msqid_ds数据结构中被选中的字段。
参数buf通常为NULL即可。
struct msqid_ds{
struct 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; //最后一次发送消息的进程id
pid_t msg_lrpid; //最后一次接收消息的进程id
};
详细细节在linux系统编程手册(下册)778页