Linux进程通信之消息队列
消息队列就是一个消息的链表,我们可以把消息看作一个记录,具有特定的格式。消息队列也是进程中通信常见的方式之一,进程可以向消息队列中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息。下面我们就来看看常见的消息队列函数:
(1) 创建或打开一个消息队列
int msgget(key_t key , intflag);
消息队列的通信编程与我前面学的信号量、共享内存差不多,都属于IPC结构,我们创建一个消息队列其实也就是创建一个消息队列的标识符。msgget的两个参数应该是非常清楚的,因为在我们的共享内存和信号量都会用到这两个参数,第一个就是我们的键值,用于产生我们的标识符,第二个参数是一个标志,指定消息队列的属性和权限。
该函数成功执行后就返回消息队列ID,以后我们就可以根据这个ID来访问这个消息队列。
(2) 将数据放到消息队列中
int msgsnd(int mspid , const void *ptr, size_t nbytes , int flag);
这个函数的参数有点多,我们来一一介绍一下
mspid:这个参数就是我们前面用msgget函数返回的消息队列的ID。
prt: prt参数指向一个准备发送消息的结构,这个结构如下:
struct mymesg
{
long mtype;
char mtext[512];
}
该结构必须包含一个长整型成员变量,用来标志这个消息的类型。第二个成员就用来指定存放我们消息。
nbytes: 消息长度(不包括长整型成员变量的长度)。
flag:一般指定为IPC_NOWAIT,表示非阻塞状态。
该函数成功执行时返回0,否则返回-1。
(3)从消息队列中获取消息
ssize_t msgrcv(int msqid , void *ptr , size_t nbytes , long type , int flag);
该函数的参数更多,不过大部分与msgget函数的参数类似。
msqid :消息队里的ID。
prt:与msgget一样。
nbytes:获取消息的长度。
type: 获取消息的类型,上面我们说过prt指向的结构体,有一个参数是指定消息的类型,我们在获取消息时,就要指定这个类型。如果type为0,就获取队列中的第一个消息。如果它的值大于0,将获取指定类型的消息,如果小于0,就获取类型等于或者小于type的绝对值的第一个消息。
flag:一般指定为IPC_NOWAIT。
该函数执行成功后返回消息的数据部分的长度,以字节为单位,否则返回-1。
需要说明的是,从msqid代表的消息队列中读取一个type类型的消息,并把消息存储在msgbuf结构中,在成功地读取了一条消息以后,队列中的这条消息将被删除。
(4)控制消息队列
int msgctl(int msgid , int command , struct msgid_ds *buf);
该函数用来控制消息队列,它与共享内存的shmctl函数类似,其参数意义为:
msgid: 为获取消息队列的标识符。
command:该参数说明对由msgid指定的队列要执行的命令:
IPC_STAT:取此队列的msqid_ds结构,并将它存放在buf指向的结构中。
IPC_SET:按由buf指向结构中的值,设置该队列的msqid_ds结构,不过该命令需要有足够的权限才能执行。
IPC_RMID:删除消息队列。
buf:是指向msgid_ds结构的指针,我们知道,在创建一个消息队列的时候,内核为每一消息队列都会指定一个关联结构,该结构包含了该消息队列的相关信息,msgid_ds就是这样的一个结构。
该函数成功时返回0,否则返回-1.
说太多理论的东西可能不太好理解,下面我们就来写一个实例程序,利用消息队列方式实现两个不相关进程间的通信:
发送进程程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#define MAX 256
struct msgbuf
{
long int mtype;
char mtext[MAX];
};
void main()
{
int mspid;
key_t key;
int ret;
struct msgbuf mymsg;
//创建一个消息队列,首先获取键值
key = ftok("/home" , 1);
mspid = msgget(key ,IPC_CREAT|0666);
if(mspid==-1)
{
printf("creat the queuefailure\n");
return;
}
//向消息队列中填入数据,需要定义一个结构体
mymsg.mtype = 1;
strncpy(mymsg.mtext , "it is a memsg_queue test!" , 30);
ret = msgsnd(mspid , &mymsg , 256 ,IPC_NOWAIT);
if(ret == -1)
{
printf("send memsage failure\n");
return;
}
}
接收进程程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
struct msgbuf
{
long mtype;
char mtext[256];
};
void main()
{
int msqid;
key_t key;
int size;
struct msgbuf mymsg;
//获取一个消息队列,首先获取键值
key = ftok("/home" , 1);
msqid = msgget(key ,IPC_CREAT|0666);
if(msqid==-1)
{
printf("creat the queuefailure\n");
return;
}
//读取一个消息队列中的内容
msgrcv(msqid , &mymsg ,sizeof(mymsg.mtext),1 , IPC_NOWAIT);
printf("the msg is : %s\n",mymsg.mtext);
//删除消息队列
if( msgctl(msqid , IPC_RMID , 0) == -1)
{
printf("delete the queuefailure\n");
return;
}
}
发送进程程序创建一个消息队列,并向其中写入“it is a memsg_queue test!“这条消息语句,接收进程就获取这个消息队列,然后取出其中的消息,并打印出来,程序中关键的地方都有解释,需要注意的就是消息队列结构体的长整型,发送进程和接收进程此值一定要对应,否则读不出要的消息。打开linux命令行,在命令行中编译运行这两个程序,先运行发送进程,然后运行接收进程,可以发现我们的接收进程正确的获取了我们消息队列中的消息。有个细节大家注意没,我们运行发送进程后,在运行接收进程可以接收到消息,然后再运行我们的接收进程却收不到消息,这是为什么?我们前面在学习共享内存的时候,写入进程写入数据后,我们读取进程可以多次读取到数据,可是在消息队列中却只能读一次,原因是前面我们提到了在成功地读取了消息队列中的一条消息以后,队列中的这条消息将被删除。因此要想多次读取同样的消息,就得让发送进程多次发送。
以上便是linux进程通信之消息队列的简单介绍,希望得到指正。