您的位置:首页 > 运维架构 > Linux

Linux进程间通信之消息队列

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

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



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

#include<sys/msg.h>#消息队列相关函数及数据结构头文件


intmsgctl(intmsqid,intcmd,structmsqid_ds*buf);#控制消息队列函数


intmsgget(key_tkey,intmsgflg);#创建消息队列,key值唯一标识该消息队列


intmsgrcv(intmsqid,void*msg_ptr,size_tmsg_sz,longintmsgtype,intmsgflg);#接收消息


intmsgsnd(intmsqid,constvoid*msg_ptr,size_tmsg_sz,intmsgflg);#发送消息

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

#msg1.c接收端

#include<stdlib.h>

#include<stdio.h>

#include<string.h>

#include<errno.h>

#include<unistd.h>

#include<sys/msg.h>#包含消息队列相关函数及数据结构的头文件

structmy_msg_st{

longintmy_msg_type;

charsome_text[BUFSIZ];

};#消息格式

intmain()

{

intrunning=1;

intmsgid;

structmy_msg_stsome_data;

longintmsg_to_receive=0;


msgid=msgget((key_t)1234,0666|IPC_CREAT);#创建标识符为key=1234的消息队列,注意发送端与接收端该值的一致性

if(msgid==-1){

fprintf(stderr,“msggetfailedwitherror:%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,“msgrcvfailedwitherror:%d\n”,errno);

exit(EXIT_FAILURE);

}

printf(“Youwrote:%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>

#defineMAX_TEXT512

structmy_msg_st{

longintmy_msg_type;

charsome_text[MAX_TEXT];

};#消息格式,与接收端一致

intmain()

{

intrunning=1;

structmy_msg_stsome_data;

intmsgid;

charbuffer[BUFSIZ];

msgid=msgget((key_t)1234,0666|IPC_CREAT);#创建消息标识符key=1234的消息队列。如果该队列已经存在,则直接返回该队列的标识符,以便向该消息队列收发消息

if(msgid==-1){

fprintf(stderr,“msggetfailedwitherror:%d\n”,errno);

exit(EXIT_FAILURE);

}#错误处理,同接收者msg1

while(running){

printf(“Entersometext:“);

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,“msgsndfailed\n”);

exit(EXIT_FAILURE);

}

if(strncmp(buffer,“end”,3)==0){#如果发送的“end”,则在发送“end”之后,退出while,结束程序

running=0;

}

}

exit(EXIT_SUCCESS);

}

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

$./msg2

Entersometext:hello

Entersometext:Howareyoutoday?

Entersometext:end

$./msg1

Youwrote:hello

Youwrote:Howareyoutoday?

Youwrote:end

$

消息队列潜在应用



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



消息队列潜在应用之升华

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

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

守护进程:将应用程序的共性部分提出出来实现为守护进程,通过某种形式的通信提供该守护进程服务的数据。

参考文献:《Linux程序设计第四版》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 软件架构 c语言