2009年3月19日星期四

[转]利用消息队列实现进程间通信

UNIX系统程序设计-10-编程语言-电脑网络-搜狐社区: "第十章:利用消息队列实现进程间通信

所谓消息功能,是指由一个进程产生并送出的消息,被放在一个叫做“消息队列”的列中管理,然后由接收方从消息队列中取出消息,这么一种功能。(先入先出)

做一个例子程序看看:
下面的两个程序分为发送方和接收方。发送方将自己的PID和从标准输入输入的一个字符串送至消息队列,接收方把消息队列中所有的PID和字符串读出,并输出至标准输出。

/* msend.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;

p = &msgdata ;
printf('Enter message : ');
fflush( stdin ); /* 刷新标准输入缓冲区 */

gets( p->mdata ); /* 输入字符串 */
p->mtype = getpid();
msgkey = ftok ( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 建立消息队列 */
msgsnd( msqid , p , sizeof(p->mdata) , 0 ); /* 送消息 */
return 0;
}

/* mrecv.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

int main()
{
int msqid;
key_t msgkey;
struct msgbuf
{
long mtype;
char mdata[256];
};
struct msgbuf msgdata , *p ;
p = &msgdata ;
msgkey = ftok( 'mrecv' , 'a' ); /* 计算标识符 */
msqid = msgget( msgkey , IPC_CREAT | 0666 ) ; /* 取得消息队列的ID */
while(1)
{
msgrcv( msqid , p , sizeof(p->mdata) , 0 , 0 ) ; /* 读消息 */
printf('Message received from %ld\n%s\n' , p->mtype , p->mdata );
}
return 0;
}

执行例:
% msend
Enter message : Hello , world
%msend
Enter message : I am Syuu I

%mrecv
Message received from 321
Hello , world
Message received from 326
I am Syuu I

使用消息功能,可分如下几步
发送方:
1.生成消息队列,取得ID。
2.向消息队列送消息。
接收方:
1.根据标识符,取得ID。
2.从消息队列接收消息。
3.删除消息队列。

例程序相对来说比较简单。我们就不解释了。

消息队列的使用方法以及函数的构造等等基本上和上一章所讲的共享内存的使用方法一样。可参考上一章来理解本章内容。
说说出现的函数:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget( key_t msgkey , int flag );
取得一个消息队列的ID,如不存在则建立。
返回值: 成功时:消息队列的ID
失败时:-1

int msgsnd( int msqid , struct msgbuf *msgp , size_t msgsiz , int msgflag );
向消息队列送消息
返回值: 成功时:0
失败时:-1
msqid是消息队列的ID,size_t msgsiz是结构体成员mdata的大小,msgflag与上一章所讲的共享内存的flag起一样的作用,不过,当这个参数为IPC_NOWAIT的时候,如果消息队列已满,则返回错误值。如果不为IPC_NOWAIT,在消息队列已满 的情况下,会一直等到消息队列有空地方的时候再发送。
注意这里的这个 struct msgbuf *msgp 。要求的格式如下:
struct msgbuf
{
long mtype;
char mdata[256];
};
long mtype在这里我们用来保存本进程的PID。mdata则是保存要发送的数据。由于mdata的大小不一定(根据实际需要定义),所以这个结构体并没有事先定义好。但是我们定义这个结构体的时候一定要遵循这个规定。你可以改的,只有mdata的大小,和结构体的名称。尽量不要修改结构体成员的名称和类型。实际上,根据mtype,我们还可以有所选择地接受消息。这在下面将会谈到。

int msgrcv( int msqid , struct msgbuf *msgp , size_t msgsiz , long msgtyp , int msgflag );
从消息队列取得一个消息
返回值: 成功时:0
失败时:-1
msqid , *msgp , msgsiz不用说了。long msgtyp是结构体msgbuf的mtype成员。msgflag与上述一样。只不过为IPC_NOWAIT的时候,如果消息队列是空的,则等到有消息可读的时候再读。当不为IPC_NOWAIT的时候,如果消息队列是空的,则返回错误值(与字面上理解的有些相反)

同样地,为了控制管理消息队列,一样有一个函数msgctl()如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl( int msqid , int cmd , struct msqid_ds *buf );
返回值: 成功时:0
失败时:-1
cmd所指定的值与共享内存部分相同。

本来还应该有一章关于Semaphore的。其使用方法于共享内存和消息队列差不多,只不过使用场合不同。可是我自己也没太弄明白那个东东是怎么回事,为了避免以讹传讹……就不写了。

看了我这么多贴子,大家辛苦了。《UNIX系统程序设计》这一系列的贴子到此宣告完成。在这些文章的创作过程中得到了SOHU C\C++论坛诸位朋友的大力支持。在此笔者深表谢意。
接下来,如果有时间的话,我会继续写一套《UNIX网络程序设计(C语言版)》。不知大家有兴趣否。
再有一点。本文中所解释的所有的函数,严格地说,应该叫:系统调用( system call )。"

没有评论: