搜索
您的当前位置:首页正文

Linux系统下-进程间通信(消息队列-详解)

来源:易榕旅网

Linux下进程间通信方式:

# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

# 信号 ( signal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

# 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

System V 与 POSIX的区别:
System V 存在时间比较老,许多系统都支持,但是接口复杂,并且可能各平台上实现略有区别(如ftok的实现及限制)。
是新标准,现在多数也已实现,我觉得如果只是开发的话,那么还是好,因为语法简单,并且各平台上实现都一样。

一、什么是消息队列

消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。

1、每种进程通信方式实现方式和功能不一样,带来适用的场景也有所不同:

消息队列是链表队列,它通过内核提供一个struct msqid_ds *msgque[MSGMNI]向量维护内核的一个消息队列列表,因此linux系统支持的最大消息队列数由msgque数组大小来决定,每一个msqid_ds表示一个消息队列,并通过msqid_ds.msg_first、msg_last维护一个先进先出的msg链表队列,当发送一个消息到该消息队列时,把发送的消息构造成一个msg结构对象,并添加到msqid_ds.msg_first、msg_last维护的链表队列,同样,接收消息的时候也是从msg链表队列尾部查找到一个msg_type匹配的msg节点,从链表队列中删除该msg节点,并修改msqid_ds结构对象的数据。 

2、消息队列的

struct msqid_ds *msgque[MSGMNI]向量:

       msgque[MSGMNI]是一个msqid_ds结构的指针数组,每个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI=128,也就是说系统最多有MSGMNI=128个消息队列。

二、消息队列与命名管道的比较

消息队列跟命名管道有不少的相同之处,与命名管道一样,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。

与命名管道相比,消息队列的优势在于,

1、消息队列可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。

2、发送消息可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。

3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。

 

1、创建或者使用消息队列:msgget函数

申请一块内存,创建一个新的消息队列(数据结构msqid_ds),将其初始化后加入到msgque向量表中的某个空位置处,返回标示符。或者在msgque向量表中找键值为key的消息队列。

 

若父进程退出,子进程尚未结束,则子进程会被init进程领养,也就是说init进程将成为该子进程的父进程。

若希望父进程退出,子进程也退出的话,可以使用线程,因为若进程结束,则还没结束的线程一定会立刻结束。

如果要使用POSIX标准来创建消息队列,1、mq_open来创建非默认个数大小消息队列:

函数原型

mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
 第4个参数为 mq_attr 指针
 struct mq_attr{ 
   long mq_flags;    
   long mq_maxmsg;    
   long mq_msgsize;    
   long mq_curmsgs; };
 当第四个参数为空指针时,就使用默认属性。  
当指向mq_attr结构的指针作为参数时,允许我们在该函数的实际操作时创建一个新队列时,给它指定mq_maxmsg和mq_msgsize属性.mq_open忽略该结构的另外两个成员.
 

(1)attr.mq_maxmsg 不能超过文件 /proc/sys/fs/mqueue/msg_max 中的数值;

(2)attr.mq_msgsize不能超过 /proc/sys/fs/mqueue/msgsize_max 的数值;

(3)消息队列名称前面必须加上斜杆。

在POSIX消息队列中 msg_max 默认为 10 ,msgsize_max 默认为8192 ,否则会报错!!!

可以在 /proc/sys/fs/mqueue# cat msg_max        /proc/sys/fs/mqueue# cat msgsize_max 查看
修改的话,要使用:echo 1000 > /proc/sys/fs/mqueue/msg_max往里面写。

 

 

2、获取消息队列的属性 
一个进程在发送和接收消息之前,需要了解消息对象的属性,如消息的最大长度。以便
设定接收和发送的buffer大小。 mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);参数: 
Mqdes:打开消息队列时获取的描述符。 
Attr:指向结构struct mq_attr的指针,用来获取消息队列的四个属性 
struct mq_attr { 
    long mq_flags;    // 0或者O_NONBLOCK 
    long mq_maxmsg; //队列中包含的消息数的最大限制数  

    long mq_msgsize; //每个消息大小的最大限制数

    long mq_curmsgs;  //当前队列中的消息数 


  
3、设置消息队列属性 
我们可以设置消息队列的属性,实际只能设置flag标志,说明队列中没有消息时,接收消息的进程是否在队列上继续等待。 
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); 
参数: 
Mqdes:打开消息队列时获取的描述符。 
Attr:指向结构struct mq_attr的指针,用来获取消息队列的最大消息个数和最大消息长
度。放到数据结构的mq_maxmsg和mq_msgsize中。

 struct mq_attr { 
    long mq_flags;    // 0或者O_NONBLOCK,只能设置这个

    long mq_maxmsg; //队列中包含的消息数的最大限制数 

    long mq_msgsize; //每个消息大小的最大限制数   

    long mq_curmsgs;  //当前队列中的消息数

 } 
oldattr:用来保存设置之前的attr值,可以为NULL. 

 

4、发送消息  
进程在打开消息队列后,可以使用下面的函数发送消息  
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);  
参数:  
mqdes: 打开消息队列时获得的描述符。  
ptr: 指向发送缓冲区的指针,发送缓冲区存放了要发送的数据。  
Len: 要发送的数据的长度。 prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。  
POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。  返回值:发送成功,返回0,失败,返回-1.    


5、接收消息 
进程在打开消息队列后,可以使用下面的函数接收消息。 
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio); 
参数: 
mqdes: 打开消息队列时获得的描述符。 
ptr: 指向接收缓冲区的指针。接收缓冲区用来存放收到的消息。 
Len: 接收缓冲区的长度。 len不能小于mq_msgsize,否则会返回EMSGSIZE prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。 POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。 返回值:  接收成功,返回0,失败,返回-1.

 

6、消息队列的关闭 
mqd_t mq_close(mqd_t mqdes); 关闭消息队列,但不能删除它 成功返回0,失败返回-1   


7、删除消息队列 
 mqd_t mq_unlink(const char *name); 成功返回0,失败返回-1 
当某个进程还没有关闭此消息队列时,调用mq_unlink时,不会马上删除队列,当最后一个进程关闭队列时,该队列被删除

 

 

 


 

 

 

 
  1. /*

  2. *

  3. * Filename: consumer.c

  4. *

  5. * Description: 消费者进程

  6. *

  7. * Version: 1.0

  8. * Created: 09/30/2011 04:52:23 PM

  9. * Revision: none

  10. * Compiler: gcc(g++)

  11. *

  12. * Author: |Zhenghe Zhang|, |zhenghe.zhang@gmail.com|

  13. * Company: |Shenzhen XXX Technology Co., Ltd.|

  14. *

  15. */

  16.  
  17. #include <stdio.h>

  18. #include <mqueue.h>

  19. #include <sys/stat.h>

  20. #include <stdlib.h>

  21. #include <unistd.h>

  22. #include <time.h>

  23. #include <string.h>

  24.  
  25. #define MAXSIZE 10 //定义buf大小

  26. #define BUFFER 8192 //定义Msg大小

  27.  
  28. struct MsgType{

  29. int len;

  30. char buf[MAXSIZE];

  31. char x;

  32. short y;

  33. };

  34.  
  35. int main()

  36. {

  37. /*消息队列*/

  38. mqd_t msgq_id;

  39. struct MsgType msg;

  40.  
  41. unsigned int sender;

  42. struct mq_attr msgq_attr;

  43.  
  44. unsigned int recv_size = BUFFER;

  45. const char *file = "/posix";

  46.  
  47. /* mq_open() for opening an existing queue */

  48. msgq_id = mq_open(file, O_RDWR);

  49. if(msgq_id == (mqd_t)-1)

  50. {

  51. perror("mq_open");

  52. exit(1);

  53. }

  54.  
  55. /* getting the attributes from the queue -- mq_getattr() */

  56. if(mq_getattr(msgq_id, &msgq_attr) == -1)

  57. {

  58. perror("mq_getattr");

  59. exit(1);

  60. }

  61.  
  62. printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- \

  63. large at most %ld bytes each\n\t- currently holds %ld messages\n",

  64. file, msgq_attr.mq_maxmsg, msgq_attr.mq_msgsize, msgq_attr.mq_curmsgs);

  65.  
  66. if(recv_size < msgq_attr.mq_msgsize)

  67. recv_size = msgq_attr.mq_msgsize;

  68.  
  69. int i = 0;

  70. while(i < 10) //运行一个consumenr,为 10 ,同时运行两个consumer进程,为 5

  71. {

  72. msg.len = -1;

  73. memset(msg.buf, 0, MAXSIZE);

  74. msg.x = ' ';

  75. msg.y = -1;

  76.  
  77. /* getting a message */

  78.  
  79. /*mq_receive() 从由描述符 mqdes 引用的队列时删除优先级最高的最老的消息,并把放置到 msg_ptr 的缓存区内。*/

  80. /*参数 msg_len 指定缓冲区 msg_ptr 的大小:它必须大于队列的 mq_msgsize 属性(参数 mq_getattr)。*/

  81. /*如果 prio 不是 NULL,那么它指向的内存用于返回收到消息相关的优先级。*/

  82. if (mq_receive(msgq_id, (char*)&msg, recv_size, &sender) == -1)

  83. {

  84. perror("mq_receive");

  85. exit(1);

  86. }

  87.  
  88. printf("msg.len = %d, msg.buf = %s, msg.x = %c, msg.y = %d\n", msg.len, msg.buf, msg.x, msg.y);

  89.  
  90. i++;

  91. sleep(2);

  92. }

  93.  
  94. if(mq_close(msgq_id) == -1)

  95. {

  96. perror("mq_close");

  97. exit(1);

  98. }

  99.  
  100. return 0;

  101. }


 

 

 

1、僵尸进程;2、共享内存;3、延时函数;4、kill信号;5、fork子进程;6信号量;

因篇幅问题不能全部显示,请点此查看更多更全内容

Top