消息队列是Linux提供的另一个进程通信方法,它源自System V,所以跟典型的用read/write的那些机制长得不一样。
消息队列相关系统调用的手册
mstget(2) msgsnd(2) msgrcv(2) msgctl(2)
消息队列和管道很相似,也是类似于创建、收发、关闭这样的过程。
下面代码模拟了两个用消息队列通信的进程。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
struct msg_st{
long mtype;
char mtext[BUFSIZ];
};
int main(int argc, char **argv){
key_t key = ftok(".", 0);
int pid = fork();
if (pid > 0){
//父进程扮演[发]消息的角色
int id = msgget(key, 0777 | IPC_CREAT);
if (id < 0){
printf("error@%d, %s\n", __LINE__, strerror(errno));
}
if (id >= 0){
struct msg_st data = {1, "hello world!"};
sleep(1);//故意推迟发送,以便看到接收端的阻塞
printf("before\tsend\n");
if (msgsnd(id, (void*)&data, sizeof("hello world!"), 0) < 0){
printf("error@%d, %s\n", __LINE__, strerror(errno));
}
printf("after \tsend\n");
}
waitpid(pid,NULL,0);
}
else if (pid == 0){
//子进程扮演[收]消息的角色
struct msg_st data;
int id = msgget(key, 0777 | IPC_CREAT);
int n;
if(id < 0){
printf("error@%d, %s\n", __LINE__, strerror(errno));
exit(-1);
}
printf("before\treceive\n");
if ((n = msgrcv(id, (void*)&data, BUFSIZ, 0, 0)) <= 0){
printf("error@%d, %s\n", __LINE__, strerror(errno));
exit(-1);
}
printf("after \treceive\n");
printf("receive: type=%ld, text=[%s]\n", data.mtype, data.mtext);
if (msgctl(id, IPC_RMID, 0) < 0){
printf("error@%d, %s\n", __LINE__, strerror(errno));
exit(-1);
}
printf("delete message queue.\n");
exit(0);
}
return 0;
}
编译和输出
#gcc test.c -o test && ./test
before receive
before send
after send
after receive
receive: type=1, text=[hello world!]
delete message queue.
从打印的顺序可以看出,接收是阻塞的。其实发送也是阻塞的,但要在消息队列满的时候才会发生。关于阻塞行为,可以用IPC_NOWAIT
这个flag控制。
补充
- 队列多少是多呢?消息能有多长?
宏 | 意义 |
---|---|
MSGMNI |
系统最大消息队列数 |
MSGMNB |
单个消息队列最大字节数 |
MSGMAX |
单个消息最大字节数 |
- 消息队列与管道相比有什么不同
消息队列的收发更灵活,一个消息队列就可以分频道,收发都可以选频道,struct msg_st的mtype成员可以当做这个频道。
消息队列还有什么有趣的用途?