消息队列是两个进程之间传递二进制数据一种简单有效的方式。每个数据块都有一个特定的类型,接收方可以根据类型来有效的接收数据,而不一定像管道和命名管道那样必须以先进先出的方式接收数据。
数据块的类型其实是自己定义的,这里的类型并不是指的int、long,而是一个正整数。笔者将这里的类型理解为一个标识,我们可以用下图来为大家解释。
消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道都有一个共同的不足,就是每个消息最大长度是有上限的,而且呢消息队列的生命周期是伴随内核。
内核中维护的消息队列其实是一个链表,那么让我们一起看看内核中的消息队列是如何被管理的。
struct msqid_ds
{
struct ipc_perm msg_perm;//消息队列操作权限信息
struct msg *msg_first; //消息队列上的第一条信息指针
struct msg *msg_last;//消息队列上的最后一条信息指针
__kernel_time_t msg_stime;//最后一次调用msgsnd的时间
__kernel_time_t msg_rtime;//最后一次调用msgrcv的时间
__kernel_time_t msg_ctime;//最后一次被修改的时间
unsigned long msg_lcbytes;
unsigned long msg_lqbytes;
unsigned short msg_cbytes;
unsigned short msg_qnum;//消息队列中已经有的消息数
unsigned short msg_qbytes;//消息队列允许的最大字节数
__kernel_ipc_pid_t msg_lspid;//最后一次调用msgsnd的进程pid
__kernel_ipc_pid_t msg_lrpid;//最后一次调用msgrcv的进程pid
};
在这里它的结构已经解释它的链式队列,msg_first和msg_last就是它的头指针和尾指针
msgget函数:创建一个新的消息队列,或者获取一个已经存在的消息队列
int msgget(key_t key,int msgflg);
参数介绍:
函数调用成功后就会在系统中为我们创建一个msqid_ds信号集合,并且初始化其中的某些部分。函数返回值返回一个此消息队列的标识符,失败时返回-1。其实我们发现System V版本的进程间通信机制系统调用都大同小异,记住一个的用法,其他的也就不难理解。
msgsnd函数:向消息队列发送数据
int msgsnd(int msqid,const void* msg_ptr,size_t msg_sz,int msgflg);
参数介绍:
struct msgbuf
{
long mtype; //大于0
char mtext[512];// 消息的大小
};
这里的mtype就是不同数据的标识,需要填充一个大于0的数据,mtext中存放你要发送的消息
msgrcv函数:从消息队列接收数据
ssize_t msgrcv(int msqid,void *msg_ptr,size_t msg_sz,long mtype,int msgflg);
参数介绍:
这里参数除了mtype其他参数全部同上,我们这里着重介绍一下mtype这个参数
msgmctl函数:该函数用来直接控制消息队列信息
int msgctl(int msgqid,int cmd,struct msqid_ds *buf);
使用方式和之前信号量共享内存基本相同,我们这里只把经常使用的三个cmd列出:
命令 | 描述 |
---|---|
IPC_STAT | 该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指定的地址空间 |
IPC_SET | 该命令用来设置消息队列的属性,要设置的属性存储在buf中 |
IPC_RMID | 从内核中删除msqid标识的消息队列 |
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define TYPE 1 //我们要接收的消息定义为类型1
//struct msgbuf //系统中已经帮我们定义了
//{
// long mtype;
// char mtext[512];
//};
int main()
{
int k = ftok("/tmp", 0x6666);
int msgid = msgget(k, IPC_CREAT|0644);
pid_t id = fork();
if(id < 0){
cout << "fork error !" <<endl;
}
if(id == 0){
struct msgbuf _mb;
_mb.mtype = TYPE;
string msg = "hello msgqueue!";
strcpy(_mb.mtext, msg.c_str());
if(msgsnd(msgid, (void*)&_mb, sizeof(_mb.mtext), 0) < 0){
cout << "msgsnd error !!" <<endl;
}
}
else{
struct msgbuf _mb;
if (msgrcv(msgid, &_mb, sizeof(_mb.mtext), TYPE, 0)< 0){
cout << "msgrcv error !!" <<endl;
}
char buf[100];
strcpy(buf,_mb.mtext);
cout << "father recv:" << buf <<endl;
}
waitpid(id,NULL,0);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
使用ipcs查看系统中的消息队列、共享内存、信号量
如果想单独的查询某一个那么可以使用选项,-m表示共享内存、-q表示消息队列、-s表示信号量。当我们需要手动的删除某个通信机制时使用ipcrm [选项] [shmid]。