04 | linux进程通信(消息队列)
概述
1.消息队列与命名管道有许多相似之处,但少了在打开和关闭管道方面的复杂性。但使用消息队列并未解决我们在使用命名管道时遇到的一些问题,比如管道满时的阻塞问题
2.消息队列提供了一种在两个不相关的进程之间传递数据的相当简单且有效的方法。与命名管道相比,消息队列的优势在于,它独立于发送和接受进程而存在,这消除了在同步命名管道的打开和关闭时可能产生的一些困难
3.消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。而且。每个数据块都被认为含有一个类型,接受进程可以独立的接收含有不同类型值的数据块。
4.我们可以通过发送消息来几乎完全避免命名管道的同步和阻塞问题。
5.我们可以用一些方法来提前查看紧急消息。
6.与管道一样,每个数据块都有一个最大长度的限制,系统中所有队列所包含的全部数据块的总长度也有一个上限。
基本函数
msgget函数
msgsnd函数
第一个参数msgid是msgget函数的返回值
第二个参数msg_ptr是一个指向准备发送消息的指针,消息必须像刚才说的那样以一个长整型成员变量开始
第三个参数msg_sz是msg_ptr指向的消息的长度。这个长度不能包括长整型消息类型成员变量的长度
第四个参数msgflg控制在当前消息队列满或消息队列到达系统范围的限制时将要发生的事情。如果设置了IPC_NOWAIT标志,函数将立刻返回,不发送消息并且返回-1.否则发送进程挂起以等待队列中腾出可用空间
msgrcv 函数
第四个参数msgtype是一个长整数,它可以实现一种简单形式的接收优先级。如果msgtype的值为0,就获取队列中的第一个可用消息。如果它的值大于零,将获取具有相同消息类型的第一个消息。如果它的值小于零,将获取消息类型等于或小于msgtype的绝对值的第一个消息。
msgctl函数
此函数最主要的作用就是销毁消息队列
小例子
//生产者
#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;
int msgid;
struct my_msg_st some_data;
char buffer[BUFSIZ];
/*创建消息队列*/
msgid = msgget((key_t)123, 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;
strcmp(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);
}
//消费者
#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;
/*创建消息队列*/
msgid = msgget((key_t)123, 0666 | IPC_CREAT);
if (msgid == -1) {
fprintf(stderr, "msgget failed with error:%d\n", errno);
exit(EXIT_FAILURE);
}
/*从队列中获取消息,直到遇见end为止,删除消息队列*/
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);
}
}
我们可以先启动生产者
再启动消费者