消息队列提供了一种在两个不相关的进程之间传递数据的相当简单且有效的方法。与命名管道相比,消息队列的优势在于,它独立于发送和接收进程而存在,这消除了在同步命名管道的打开与关闭的可能产生的一些困难。
Linux系统有两个宏定义MSGMAX和MSGMNB,它们以字节为单位分别定义了一条消息的最大长度和一个队列的最大长度。
#include<sys/msg.h>
int msgctl(int msgid , int cmd, struct msqid_ds *buf);
int msgget(key_t key , int msgflg);
int msgrcv(int msqid , void *msg_ptr , size_t msg_sz , long int msgtype , int msgflg);
int msgsnd(int msqid , const void *msg_ptr , size_t msg_sz , int msgflg);
1.msgget函数
我们用msgget函数来创建与访问一个消息队列。
int msgget(key_t key , int msgflg);
第一个参数可以任意指定一个key_t类型的变量。第二个参数msgflg由9个权限标志组成。
成功时msgget函数返回一个正整数,即队列标识符,失败时返回-1.
2.msgsnd函数
msgsnd函数用来把消息添加到消息队列中
int msgsnd(int msqid , const void *msg_ptr , size_t msg_sz , int msgflg);
消息的结构收到两方面的约束,首先,它的长度必须小于系统规定的上限,其次,它必须以一个长整型成员变量开始,接收函数将用这个成员变量来确定消息的类型。
当使用消息时,最好把消息结构定义为下面这个样子:
struct my_message
{
long int mesage_type;
/*你想要发送的数据*/
第一个参数msqid是由msgget函数返回的消息队列标识符。
第二个参数msg_ptr是一个指向准备发送消息的指针,消息必须像刚才说的那样以一个长整型成员变量开始。
第三个参数msg_sz是msg_ptr指向的消息的长度。这个长度不能包括长整型消息类型成员变量的长度。
第四个参数msgflg控制在当前消息队列满或者队列消息到达系统范围的限制时将要发生的事情。
3.msgrcv函数
msgrcv函数从一个消息队列中获取消息:
int msgrcv(int msqid , void *msg_ptr , size_t msg_sz , long int msgtype , int msgflg);
第一个参数是由msgget函数返回的消息队列标识符。
第二个参数是一个指向准备接受消息的指针,消息必须像前面msgsnd函数中介绍的那样以一个长整型成员变量开始。
第三个参数msg_sz是msg_ptr指向的消息的长度,它不包括长整型消息类型成员变量的程度。
第四个参数msgtype是一个长整数,它可以实现一种简单形式的接收优先级。如果msgtype的值为0,就获取消息队列中第一个可用的消息。如果它的值大于0,将获取具有相同消息类型的第一个消息。如果它的取值小于0,就把msgtype设置为-n。
第五个参数msgflg用于控制消息队列中没有相应类型的消息可以接收时,将要发生的事情。
4.msgctl函数
最后一个消息队列函数是msgctl,它的作用与共享内存的控制函数非常相识。
int msgctl(int msgid , int cmd, struct msqid_ds *buf);
msqid_ds结构至少包含以下成员:
struct msqid_ds
{
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
}
第一个参数msqid是由msgget函数返回的消息队列的标识符。
第二个参数command是将要采取的动作,它可以取三个值
命令 说明
IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前值
IPC_SET 如果进程有足够的权限,就把消息队列的当前关联值设置为msqid_ds结构中给出的值
IPC_RMID 删除消息队列。
函数在成员共返回0,失败时返回-1.
/* Here's the receiver program. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> struct my_msg_st { long int my_msg_type; char some_text[BUFSIZ]; }; int main() { int running = 1; int msgid; struct my_msg_st some_data; long int msg_to_receive = 0; /* First, we set up the message queue. */ msgid = msgget((key_t)1234, 0666 | IPC_CREAT); if (msgid == -1) { fprintf(stderr, "msgget failed with error: %d\n", errno); exit(EXIT_FAILURE); } /* Then the messages are retrieved from the queue, until an end message is encountered. Lastly, the message queue is deleted. */ while(running) { if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1) { fprintf(stderr, "msgrcv failed with error: %d\n", errno); exit(EXIT_FAILURE); } printf("You wrote: %s", some_data.some_text); if (strncmp(some_data.some_text, "end", 3) == 0) { running = 0; } } if (msgctl(msgid, IPC_RMID, 0) == -1) { fprintf(stderr, "msgctl(IPC_RMID) failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
/* The sender program is very similar to msg1.c. In the main set up, delete the msg_to_receive declaration and replace it with buffer[BUFSIZ], remove the message queue delete and make the following changes to the running loop. We now have a call to msgsnd to send the entered text to the queue. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> #define MAX_TEXT 512 struct my_msg_st { long int my_msg_type; char some_text[MAX_TEXT]; }; int main() { int running = 1; struct my_msg_st some_data; int msgid; char buffer[BUFSIZ]; msgid = msgget((key_t)1234, 0666 | IPC_CREAT); if (msgid == -1) { fprintf(stderr, "msgget failed with error: %d\n", errno); exit(EXIT_FAILURE); } while(running) { printf("Enter some text: "); fgets(buffer, BUFSIZ, stdin); some_data.my_msg_type = 1; strcpy(some_data.some_text, buffer); if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { fprintf(stderr, "msgsnd failed\n"); exit(EXIT_FAILURE); } if (strncmp(buffer, "end", 3) == 0) { running = 0; } } exit(EXIT_SUCCESS); }