现在我们来讨论第三种也是最后一种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删除消息队列进行一些清理工作。

linux 消息队列机制相关推荐

  1. linux消息队列非亲缘,linux进程

    linux进程Tag内容描述: 1.linux消息队列进程通信 一.消息队列的基本概念消息队列(也叫做报文队列)是Unix系统V版本中3种进程间通信机制之一.另外两种是信号灯和共享内存.这些IPC机制 ...

  2. android的消息队列机制

    android下的线程,Looper线程,MessageQueue,Handler,Message等之间的关系,以及Message的send/post及Message dispatch的过程. Loo ...

  3. linux 消息队列 msgget/msgsnd/msgrecv

    专栏内容:linux下并发编程 个人主页:我的主页 座右铭:天行健,君子以自强不息:地势坤,君子以厚德载物. 目录 前言 概述 原理 消息队列的大小 查看资源 接口 代码演示 结尾 前言 本专栏主要分 ...

  4. linux.调整收发队列,linux消息队列通信

    程序目的:学习linux消息队列通信 所用主要函数:msgget(),msgsnd(),msgrcv(),msgctl() 首先介绍每个函数的用法: (1)msgget 使用格式: #include ...

  5. android消息队列模型,Android 消息队列机制

    在非UI线程使用Handler进行线程通信时,一般都需要进行3个步骤: 创建Looper Looper.prepar() 创建Handler 启动消息循环Looper.loop() 通过这3步,基本就 ...

  6. ZWave 中的消息队列机制

    文章主题 文章主题   在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave ...

  7. linux 消息队列_Linux消息队列

    消息队列,Unix的通信机制之一,可以理解为是一个存放消息(数据)容器.将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序.可以解决两个进程的读写速度不同(处理数据速度不同),系统 ...

  8. Linux消息队列原理与应用

    消息队列 (也叫做报文队列)是Unix系统V版本中3种进程间通信机制之一.另外两种是信号灯和共享内存.这些IPC机制使用共同的授权方法.只有通过系统调用将标志符传递给核心之后,进程才能存取这些资源.这 ...

  9. Linux消息队列编程(简单应用)

    消息队列,Unix的通信机制之一,可以理解为是一个存放消息(数据)容器.将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序.可以解决两个进程的读写速度不同(处理数据速度不同),系统 ...

最新文章

  1. 数据科学+python+R+数据库+机器学习+(速查表)cheat sheets大全
  2. Iframe 高度自适应
  3. excel执行INSERT和UPDATE操作语句
  4. java biginteger转int_如何在不使用java.math.BigInteger的情况下使用Java处理非常大的数字...
  5. Could not close the output stream for file hdfs://192.168.190.129:9000/BJ_4.c
  6. 4.6 高斯约当消元法
  7. 用java编写日历添加窗口一角_Java 实训4 编写一个窗体程序显示日历
  8. 从浏览器中下载文件如何修改默认保存位置
  9. 微信小程序自动检测更新新版本
  10. java迭代器不能修改集合值_关于使用迭代器对集合进行遍历时,不能对集合进行修改的论证...
  11. 转:lnmp 搭建手册-黑一路人
  12. Linux chapter 8
  13. 【君思智慧园区】智慧园区建设规划方案
  14. 结构光三维扫描仪(单相机+单投影仪)
  15. 面对SSD的步步紧逼,HDD依然奋斗不息
  16. SyntaxError: Non-UTF-8 code starting with ‘\xd5‘ in file
  17. 【移动网络】5G NR: 压缩设计与帧结构等核心操作特性
  18. IntelliJ IDEA调整字体大小
  19. 如何安装适用于win11的安卓子系统(WSA)的谷歌框架安卓13版本
  20. [单片机框架][bsp层][cx32l003][bsp_adc] ADC配置和使用

热门文章

  1. 第7章 输入/输出系统
  2. python基础总结(6)
  3. 揭开Python科学计算的面纱
  4. oracle 删除表中重复记录,并保留一条
  5. nios pio interrupt 的使能
  6. CFUpdate上传控件的使用
  7. 删除了注册表winsock项及winsock2项怎么办
  8. d9900 修改ip 思科dcm_思科设备SSH登陆详细配置过程
  9. 如何在linux环境下安装kvm,如何在Linux发行版上安装和配置KVM和Open vSwitch?
  10. java单例模式理解_快速理解Java中的五种单例模式