什么是消息队列?
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法,消息队列具有内核持续性;
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值;
消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)
cat /proc/sys/kernel/msgmax 最大消息长度限制,8192=8K
cat /proc/sys/kernel/msgmnb 消息队列总的字节数,16384 = 16K
cat /proc/sys/kernel/msgmni 消息条目数,169
内核为每个IPC对象维护一个数据结构,在调用msgctl的时候就会用到;
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
//The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
主要函数API:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
对消息队列进行控制
msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
IPC_STAT(将消息队列的msqid_ds结构体的值复制到当前buf指向的内存空间,读取消息队列的信息)
IPC_SET(当前buf指向的内存空间的值复制给消息队列的msqid_ds结构体的值,设置消息队列的信息)
IPC_RMID(删除消息队列)
2:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
用来创建和访问一个消息队列
key: 某个消息队列的名字, 比如0x1234, 0x3232.....
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的;
If msgflg specifies both IPC_CREAT and IPC_EXCL and a message queue already exists for key, then msgget() fails with errno set to EEXIST
返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1
返回的错误信息:
On failure, errno is set to one of the following values:
EACCES A message queue exists for key, but the calling process does not have permission to access the queue, and does not have the CAP_IPC_OWNER capability.
EEXIST A message queue exists for key and msgflg specified both IPC_CREAT and IPC_EXCL.
ENOENT No message queue exists for key and msgflg did not specify IPC_CREAT.
ENOMEM A message queue has to be created but the system does not have enough memory for the new data structure.
ENOSPC A message queue has to be created but the system limit for the maximum number of message queues (MSGMNI) would be exceeded.
3:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数msgp指向类似于下面的结构体,形式一样,但是具体的值自己改,msgbuf这个名字一定要改:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data ,长度可以自己改,不能超过系统规定的长度*/
};
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情,msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。
4:
#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);
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备接收的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:它可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
5:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
用这个函数为消息队列产生一个key值;
实例1:
进程先创建一个消息队列,并向该消息队列中写入消息,消息类型是自身的PID;然后睡眠3秒之后再从消息队列中按照消息类型读取消息;
从运行结果可以看出,当写入之后,消息队列的大小变为了12, 当将消息读走之后消息队列的大小变为了0;
/*************************************************************************
> File Name: msg1.cpp
> Author:
> Mail:
> Created Time: 2015年12月17日 星期四 20时25分44秒
************************************************************************/
#include <iostream>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
using namespace std;
struct msg_buf {
long mtype; /* message type, must be > 0 */
char mtext[100]; /* message data */
};
/*
生成一个key值,ftok(首先要先创建一个文件myMsgFile)
创建消息队列,msgget
生成消息结构体对象并填充消息类型-一般是PID和数据内容
将消息写到消息队列
睡眠3秒
从消息队列里面读出来
*/
int main()
{
key_t key;
key = ftok("./myMsgFile", 'k');
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if(msgid == -1){
if(errno == EEXIST){
key = ftok("./myMsgFile", 'k');
msgid = msgget(key, IPC_CREAT | 0666);
}
else{
cout << "msgget error..." << endl;
exit(-1);
}
}
struct msg_buf msgbuf;
int msg_type = getpid();
msgbuf.mtype = msg_type;
strcpy(msgbuf.mtext, "message data");
int ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
if(ret == -1){
cout << "msgsnd error..." << endl;
exit(-1);
}
sleep(3);
memset(&msgbuf, 0, sizeof(msgbuf));
ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf), msg_type, IPC_NOWAIT);
if(ret == -1){
cout << "msgrcv error..." << endl;
exit(-1);
}
cout << "receive the data is: " << msgbuf.mtext << endl;
exit(0);
}
备注:ipcs查看系统的共享内存,消息队列,信号量 ipcrm -q msqid删除消息队列
实例2:
这次是父子进程通过消息队列来传递信息;
进程先创建一个消息队列,并向该消息队列中写入消息,消息类型是自身的PID;然后又向该消息队列中写入消息,消息类型是111;此时查看消息队列已用了26字节;
然后睡眠5秒之后创建子进程,子进程再从消息队列中获得消息类型位111的消息并读走;读走了13字节,还有一个消息,剩下13字节;
/*************************************************************************
> File Name: msg1.cpp
> Author:
> Mail:
> Created Time: 2015年12月17日 星期四 20时25分44秒
************************************************************************/
#include <iostream>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
using namespace std;
struct msg_buf {
long mtype; /* message type, must be > 0 */
char mtext[100]; /* message data */
};
/*
生成一个key值,ftok(首先要先创建一个文件myMsgFile)
创建消息队列,msgget
生成消息结构体对象并填充消息类型-一般是PID和数据内容
将消息写到消息队列
睡眠3秒
从消息队列里面读出来
*/
int main()
{
key_t key;
key = ftok("./myMsgFile", 'k');
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if(msgid == -1){
if(errno == EEXIST){
key = ftok("./myMsgFile", 'k');
msgid = msgget(key, IPC_CREAT | 0666);
}
else{
cout << "msgget error..." << endl;
exit(-1);
}
}
struct msg_buf msgbuf;
int msg_type = getpid();
msgbuf.mtype = msg_type;
strcpy(msgbuf.mtext, "message1 data");
int ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
if(ret == -1){
cout << "msgsnd error..." << endl;
exit(-1);
}
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.mtype = 111;
strcpy(msgbuf.mtext, "message2 data");
ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
if(ret == -1){
cout << "msgsnd error..." << endl;
exit(-1);
}
sleep(5);
pid_t pid = fork();
if(pid < 0){
cout << "fork error..." << endl;
exit(-1);
}
else if (pid == 0){
memset(&msgbuf, 0, sizeof(msgbuf));
ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf), 111, IPC_NOWAIT); //获得第二条消息
if(ret == -1){
cout << "msgrcv error..." << endl;
exit(-1);
}
cout << "child process receive the data is: " << msgbuf.mtext << endl;
cout << "child exit..." << endl;
exit(0);
}
int child_status;
wait(&child_status);
cout << "parent exit..." << endl;
exit(0);
}