`
mylxiaoyi
  • 浏览: 328423 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

进程间通信之消息队列

阅读更多


消息队列

现在我们来讨论第三种也是最后一种System V IPV工具:消息队列。在许多方面看来,消息队列类似于有名管道,但是却没有与打开与关闭管道的复杂关联。然而,使用消息队列并没有解决我们使用有名管道所遇到的问题,例如管道上的阻塞。

消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法。与有名管道比较起来,消息队列的优点在独立于发送与接收进程,这减少了在打开与关闭有名管道之间同步的困难。

消息队列提供了一种由一个进程向另一个进程发送块数据的方法。另外,每一个数据块被看作有一个类型,而接收进程可以独立接收具有不同类型的数据块。消息队列的好处在于我们几乎可以完全避免同步问题,并且可以通过发送消息屏蔽有名管道的问题。更好的是,我们可以使用某些紧急方式发送消息。坏处在于,与管道类似,在每一个数据块上有一个最大尺寸限制,同时在系统中所有消息队列上的块尺寸上也有一个最大尺寸限制。

尽管有这些限制,但是X/Open规范并没有定义这些限制的具体值,除了指出超过这些尺寸是某些消息队列功能失败的原因。Linux系统有两个定义,MSGMAX与MSGMNB,这分别定义单个消息与一个队列的最大尺寸。这些宏定义在其他系统上也许并不相同,甚至也许就不存在。

消息队列函数定义如下:

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int msgget(key_t key, int msgflg);
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);

与信息号和共享内存一样,头文件sys/types.h与sys/ipc.h通常也是需要的。

msgget

我们可以使用msgget函数创建与访问一个消息队列:

int msgget(key_t key, int msgflg);

与其他IPC工具类似,程序必须提供一个指定一个特定消息队列的key值。特殊值IPC_PRIVATE创建一个私有队列,这在理论上只可以为当前进程所访问。与信息量和共享内存一样,在某些Linux系统上,消息队列并不是私有的。因为私有队列用处较少,因而这并不是一个严重问题。与前面一样,第二个参数,msgflg,由9个权限标记组成。要创建一个新的消息队列,由IPC_CREAT特殊位必须与其他的权限位进行或操作。设置IPC_CREAT标记与指定一个已存在的消息队列并不是错误。如果消息队列已经存在,IPC_CREAT标记只是简单的被忽略。

如果成功,msgget函数会返回一个正数作为队列标识符,如果失败则会返回-1。

msgsnd

msgsnd函数允许我们将消息添加到消息队列:

int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

消息结构由两种方式来限定。第一,他必须小于系统限制,第二,必须以long int开始,这在接收函数中会用作一个消息类型。当我们在使用消息时,最好是以如下形式来定义我们的消息结构:

struct my_message {
long int message_type;
/* The data you wish to transfer */
}

因为message_type用于消息接收,所以我们不能简单的忽略他。我们必须定义我们自己的数据结构来包含并对其进行初始化,从而他可以包含一个可知的值。

第一个参数,msgid,是由msgget函数所返回的消息队列标识符。

第二个参数,msg_ptr,是一个指向要发送消息的指针,正如前面所描述的,这个消息必须以long int类型开始。

第三个参数,msg_sz,是由msg_ptr所指向的消息的尺寸。这个尺寸必须不包含long int消息类型。

第四个参数,msgflg,控制如果当前消息队列已满或是达到了队列消息的系统限制时如何处理。如果msgflg标记设置了IPC_NOWAIT,函数就会立即返回而不发送消息,并且返回值为-1。如果msgflg标记清除了IPC_NOWAIT标记,发送进程就会被挂起,等待队列中有可用的空间。

如果成功,函数会返回0,如果失败,则会返回-1。如果调用成功,系统就会复杂一份消息数据并将其放入消息队列中。

msgrcv

msgrcv函数由一个消息队列中收取消息:

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

第一个参数,msqid,是由msgget函数所返回的消息队列标记符。

第二个参数,msg_ptr,是一个指向将要接收消息的指针,正如在msgsnd函数中所描述的,这个消息必须以long int类型开始。

第三个参数,msg_sz,是由msg_ptr所指向的消息的尺寸,并不包含long int消息类型。

第四个参数,msgtype,是一个long int类型,允许一个接收优先级形式的实现。如果msgtype的值为0,队列中第一个可用的消息就会被接收。如果其值大于0,具有相同消息类型的第一个消息就会被接收。如果其值小于0,第一个具有相同类型或是小于msgtype绝对值的消息就会被接收。

这听起来要比实际操作复杂得多。如果我们只是简单的希望以其发送的顺序来接收消息,我们可以将msgtype设置为0。如果我们希望接收特殊消息类型的消息,我们可以将msgtype设置为等于这个值。如果我们希望接收消息类型为n或是小于n的值,我们可以将msgtype设置为-n。

第五个参数,msgflg,控制当没有合适类型的消息正在等待被接收时如何处理。如果在msgflg中设置了IPC_NOWAIT位,调用就会立即返回,而返回值为-1。如果msgflg标记中消除了IPC_NOWAIT位,进程就会被挂起,等待一个合适类型的消息到来。

如果成功,msgrcv会返回放入接收缓冲区中的字节数,消息会被拷贝到由msg_ptr所指向的用户分配缓冲区中,而数据就会由消息队列中删除。如果失败则会返回-1。

msgctl

最后一个消息队列函数是msgctl,这与共享内存中的控制函数十分类型。

int msgctl(int msqid, int command, struct msqid_ds *buf);

msqid_ds结构至少包含下列成员:

struct msqid_ds {
uid_t msg_perm.uid;
uid_t msg_perm.gid
mode_t msg_perm.mode;
}

第一个参数,msqid,是由msgget函数所返回的标记符。

第二个参数,command,是要执行的动作。他可以取下面三个值:

命令 描述
IPC_STAT 设置msqid_ds结构中的数据来反射与消息队列相关联的值。
IPC_SET 如果进程有权限这样做,这个命令会设置与msqid_ds数据结构中所提供的消息队列相关联的值。
IPC_RMID 删除消息队列。

如果成功则会返回0,如果失败则会返回-1。当进程正在msgsnd或是msgrcv函数中等待时如果消息队列被删除,发送或接收函数就会失败。

试验--消息队列

现在我们已经了解了消息队列的定义,我们可以来看一下他们是如何实际工作的。与前面一样,我们将会编写两个程序:msg1.c来接收,msg2.c来发送。我们会允许任意一个程序创建消息队列,但是使用接收者在接收到最后一条消息后删除消息队列。

1 下面是接收程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/ipc.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;

2 首先,我们设置消息队列:

msgid = msgget((key_t)1234,0666|IPC_CREAT);

if(msgid == -1)
{
fprintf(stderr,"msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}

3 然后,接收消息队列中的消息直到遇到一个end消息。最后,消息队列被删除:

while(running)
{
if(msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
{
fprintf(stderr, "msgrcv failed with errno: %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);
}

exit(EXIT_SUCCESS);
}

4 发送程序与msg1.c类似。在main函数中,删除msg_to_receive声明,代之以buffer[BUFSIZ]。移除消息队列删除代码,并且在running循环中做出如下更改。现在我们调用msgsnd来将输入的文本发送到队列中。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/ipc.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;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];

msgid = msgget((key_t)1234, 0666|IPC_CREAT);

if(msgid==-1)
{
fprintf(stderr,"msgget failed with errno: %d\n", errno);
exit(EXIT_FAILURE);
}

while(running)
{
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
some_data.my_msg_type = 1;
strcpy(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);
}

与管道中的例子不同,进程并没有必要提供自己的同步机制。这是消息队列比起管道的一个巨大优点。

假设消息队列有空间,发送者可以创建队列,在队列中放入一些数据,并且甚至可以在接收者启动之前退出。我们会首先运行发送者。如下面的例子输出:

$ ./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
$

工作原理


发送者程序使用msgget创建一个消息队列;然后使用msgsnd函数向队列中添加消息。接收者使用msgget来获得消息队列标识符,并且接收消息,直到接收到特殊消息end。然后他会使用msgctl删除消息队列进行一些清理工作。

分享到:
评论

相关推荐

    进程间通信之消息队列 ( message queue )——完整代码

    进程间通信之消息队列 ( message queue ) 消息队列是消息的链表,具有特定的格式,并由消息队列标识符标识. 七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四....

    Linux进程间通信之消息队列

    本教程将深入探讨Linux进程间通信中的消息队列,并通过一个基础模型来阐述其工作原理。 消息队列是一种异步通信方法,允许进程发送和接收特定长度的消息,这些消息会被存储在内核维护的队列中,直到被接收方读取。...

    C#进程间通信-消息队列代码实例

    本实例以C#语言为基础,详细讲解如何利用消息队列进行进程间通信。 首先,我们需要理解消息队列的基本概念。消息队列是一种存储和转发机制,它将消息从一个进程发送到另一个进程,而无需两者同时在线。消息队列的...

    进程间通信

    七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四....进程间通信之消息队列 ( message queue ) 进程间通信之信号量( semophore ) 进程间通信之套接字( socket )

    C#实现进程间通信(使用消息队列实现)

    在C#中,我们可以通过多种方式实现进程间通信,其中之一是利用消息队列(Message Queue)。消息队列提供了一种可靠且异步的方式来传递消息,使得一个进程可以在不依赖其他进程的状态下发送消息。 标题"**C#实现进程...

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

    本示例中,我们关注的是利用消息队列这一特定的IPC机制,来实现在Linux系统下的进程间通信。消息队列提供了异步通信的能力,使得进程可以在不同时刻发送和接收消息,而无需相互等待。 首先,我们有两个进程,进程A...

    进程间通信:消息队列示例代码

    在Linux系统编程中,进程间通信(IPC, Inter-Process Communication)是多个进程协同...通过学习和理解消息队列的工作原理以及示例代码,开发者可以更好地掌握进程间通信的技巧,从而编写更高效、稳定的多进程应用。

    linux下进程间通信--消息队列

    标题"Linux下进程间通信--消息队列"指出了我们讨论的核心——如何在Linux环境中利用消息队列进行进程间的通信。下面我们将深入探讨消息队列的概念、工作原理、使用方法以及提供的优点。 1. **消息队列概念**: ...

    linux消息队列进程间通信

    《Linux环境进程间通信(三):消息队列.doc》详细阐述了消息队列的使用,包括如何创建、发送、接收和控制消息队列。《Linux环境进程间通信(五):_共享内存(上).doc》则介绍了共享内存,这是一种效率极高的通信方式,...

    Linux进程间通信-消息队列实例.pdf

    【Linux进程间通信-消息队列实例】 在操作系统中,进程间通信(Inter-Process Communication, IPC)是多个进程共享数据、协调工作的重要机制。在Linux系统中,提供了多种进程间通信方式,如管道、信号量、共享内存...

    消息队列程序,进程间通信。

    消息队列,进程间通信的方式之一。消息队列函数

    Liunx嵌入式系统进程间通信实例(二)—消息队列

    在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是...通过对`ivos-service-asm-api.c`、`AudioModeControl.c`等源代码的学习,我们可以更深入地理解如何在实际项目中应用消息队列进行进程间通信。

    进程与消息队列进程与消息队列简单例子

    进程与消息队列是操作系统中两种基本的进程间通信方式。进程是指计算机系统中正在运行的程序实体,而消息队列则是一种特殊的数据结构,用于在进程之间传递数据。 进程 在操作系统中,进程是指计算机系统中正在运行...

    进程间消息队列通信机制的使用

    ### 进程间消息队列通信机制的使用 ...通过以上分析,我们可以看到消息队列作为一种有效的进程间通信手段,在实际应用中具有重要的作用。无论是简单的数据传输还是复杂的任务调度,消息队列都能提供灵活而强大的支持。

    操作系统+进程间通信-消息队列+实现聊天室功能

    总之,通过这个项目,你不仅可以学习到操作系统中进程间通信的基本概念,还能掌握消息队列的具体使用方法,以及如何利用这些知识实现一个实用的聊天室应用。这将有助于深化对操作系统底层原理的理解,提高你的编程...

    Linux进程间通信(消息队列、信号量+共享内存).zip

    在这个“Linux进程间通信(消息队列、信号量+共享内存).zip”压缩包中,我们可以看到涉及到Linux IPC的三种主要方法:消息队列、信号量以及共享内存。这些技术在实现分布式锁和信号量控制等方面有着广泛的应用。 1...

    linux c 进程间通信 消息队列

    本教程将深入探讨Linux C语言中如何利用消息队列进行进程间通信,并提供亲测可用的代码示例。 首先,理解消息队列的基本概念。消息队列是一种特殊的文件系统对象,它允许一个进程发送消息到另一个进程,即使接收...

    linux使用消息队列实现进程间双向通信

    在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程共享数据或交换信息的一种机制。本文将深入探讨如何利用消息队列这一IPC机制实现进程间的双向通信。消息队列允许进程异步地发送和接收...

    消息队列在Linux线程或进程间通信中的应用.pdf

    【消息队列在Linux线程或进程间通信中的应用】 消息队列是Linux操作系统中用于线程和进程间通信的一种重要机制。它提供了一种可靠的数据传递方式,克服了其他通信方式的一些局限,如信号传递信息量有限、管道数据流...

Global site tag (gtag.js) - Google Analytics