你的位置:首页 > 操作系统

[操作系统]Linux进程间通信之消息队列


本文依据以下思路展开,首先从宏观上阐述消息队列的机制,然后以具体代码为例进一步阐述该机制,最后试着畅想一下该通信机制潜在的应用。

 

消息队列是在两个不相关进程间传递数据的一种简单、高效方式,她独立于发送进程、接受进程而存在。

 

 

图1 消息队列通信机制示意图

 首先从宏观的角度了解一下消息队列的工作机制。因为消息队列独立于进程而存在,为了区别不同的消息队列,需要以key值标记消息队列,这样两个不相关进程可以通过事先约定的key值通过消息队列进行消息收发。例如进程A向key消息队列发送消息,进程B从Key消息队列读取消息。在这一过程中主要涉及到四个函数:

#include <sys/msg.h> # 消息队列相关函数及数据结构头文件int msgctl(int msqid, int cmd, struct msqid_ds *buf);# 控制消息队列函数int msgget(key_t key, int msgflg); # 创建消息队列,key值唯一标识该消息队列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);# 发送消息

下面结合代码实例,对上述过程进行分析(具体分析见代码注释)

# msg1.c 接收端

#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)1234, 0666 | IPC_CREAT);# 创建标识符为key = 1234 的消息队列,注意发送端与接收端该值的一致性 if (msgid == -1) { fprintf(stderr, “msgget failed with error: %d\n”, errno); exit(EXIT_FAILURE); }# 错误处理:msgget调用成功返回消息队列标识符,调用失败返回-1 while(running) { if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1) { # 从消息队列接收消息,如果接收失败执行if语句并退出 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) { # 如果接收到文本含有“end”,将running设置为0,效果是:退出while循环 running = 0; } } if (msgctl(msgid, IPC_RMID, 0) == -1) { # 删除消息队列,如果删除失败执行if语句并退出 fprintf(stderr, “msgctl(IPC_RMID) failed\n”); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS);}

# msg2.c 发送端

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <sys/msg.h>#define MAX_TEXT 512struct 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);# 创建消息标识符key = 1234的消息队列。如果该队列已经存在,则直接返回该队列的标识符,以便向该消息队列收发消息 if (msgid == -1) { fprintf(stderr, “msgget failed with error: %d\n”, errno); exit(EXIT_FAILURE); }# 错误处理,同接收者msg1 while(running) { printf(“Enter some text: “); fgets(buffer, BUFSIZ, stdin);# 由控制台输入文本,并将其存放在buffer之中 some_data.my_msg_type = 1;# 类型填充,在本例中没有特别含义 strcpy(some_data.some_text, buffer);# 将buffer数据复制到some_text之中 if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { # 向消息队列发送消息,如果发送失败执行if语句并退出 fprintf(stderr, “msgsnd failed\n”); exit(EXIT_FAILURE); } if (strncmp(buffer, “end”, 3) == 0) {# 如果发送的“end”,则在发送“end”之后,退出while,结束程序 running = 0; } } exit(EXIT_SUCCESS);}

 以下是在控制台模拟的结果:

$ ./msg2

Enter some text: hello

Enter some text: How are you today?

Enter some text: end

$ ./msg1

You wrote: hello

You wrote: How are you today?

You wrote: end

$

消息队列潜在应用

图2 消息队列在守护进程中的应用

如图2所示,假如有三个图形界面程序,他们分别对应进程1、进程2、进程3。这三个应用程序都需要鼠标、键盘操作,如果在每个进程都加入捕获鼠标、键盘操作的代码,那么一共需要三份这样的代码,有点浪费资源(内存空间)。如果我们将捕获鼠标、键盘操作的代码独立出来做成一个单独的进程,该进程向特定的消息队列发送捕获的鼠标、键盘操作,当前激活图像程序可以从该消息队列中提取相应的鼠标、键盘操作,然后据此执行后续的指令。以这种方式,能够将不同应用程序中,共性的部分提取出来,从而简化应用程序的设计和设计更加优化的共性处理程序。

 

消息队列潜在应用之升华

 

处理程序共性部分的一些方法:

库:通用的一些功能实现为库函数,对外提供定义良好的借口;应用程序在应用这些功能的时候,只需调用相应接口即可。

守护进程:将应用程序的共性部分提出出来实现为守护进程,通过某种通信机制实现守护进程与应用程序的信息交互。

 

参考资料:《Linux程序设计 第四版》

 

 

 

 




暑假去泰国旅游价格泰国旅游6天泰国旅游费用多少泰国旅游哪里好玩泰国旅游行程东莞春节哪里有活动?2015东莞春节哪里有优惠? 2015年春节东莞有什么好玩的地方?东莞春节旅游攻略? 东莞梦幻百花洲春节门票多少钱?2015梦幻百花洲春节门票优惠吗? 2015梦幻百花洲春节有活动吗?东莞梦幻百花洲春节活动介绍? 三亚西岛学生有优惠吗?西岛门票学生优惠政策? 七仙岭君澜度假酒店9月优惠活动?保亭七仙岭君澜酒店有什么优惠? 知青逛公园 漫游明孝陵 三亚鹿回头山顶公园景点介绍?鹿回头山顶公园有什么好玩的? 马拉湾的门票包含什么?黄河谷马拉湾2015票价? 思罗河漂流带雨衣吗?石表山思罗河漂流需要自带什么? 思罗河漂流团购便宜吗?石表山思罗河漂流怎么预约? 思罗河漂流小孩可以玩吗?石表山思罗河漂流安全吗? 锦绣中华泼水节什么时候结束?深圳锦绣中华泼水节还有几天? 深圳锦绣中华中秋节有什么活动?锦绣中华中秋节门票多少钱? 锦绣中华泼水节夜场门票多少钱?深圳锦绣中华泼水节夜场攻略? 教师节去广州塔门票优惠吗?广州电视塔凭教师证门票半价吗? SL03A2-E1HWA Datasheet SL03A2-E1HWA Datasheet SL03A2-E1SRWA Datasheet SL03A2-E1SRWA Datasheet SL04A1-B1EWA Datasheet SL04A1-B1EWA Datasheet 雨亭 雨亭 雨亭 九乡溶洞 九乡溶洞 九乡溶洞 峨眉山温泉 峨眉山温泉 峨眉山温泉