进程间通信-POSIX 消息队列

POSIX 消息队列

POSIX 消息队列可以认为是一个消息链表。进程(线程)可以往里写消息,也可以从里面取出消息。可以在不相关的进程之间发送和接收数据。

创建(打开)消息队列-mq_open()函数

mq_open()函数用于打开或创建一个消息队列,该函数定义如下:

#include <mqueue.h>

 mqd_t mq_open(const char *name, int oflag);
 mqd_t mq_open(const char *name, int oflag, mode_t mode,
               struct mq_attr *attr);

参数说明

  • name:消息队列的名称,形式为一个斜杠 "/" 开头的字符串,类似于文件路径。
  • oflag:打开标志,用于指定消息队列的打开方式,可以是以下之一或它们的组合:
    • O_RDONLY:只读方式打开消息队列。
    • O_WRONLY:只写方式打开消息队列。
    • O_RDWR:读写方式打开消息队列。
    • O_CREAT:如果消息队列不存在,则创建它。
    • O_EXCL:与 O_CREAT 同时使用,确保创建一个新的消息队列。
  • mode:用于指定创建的消息队列的操作权限。类似于 chmod 命令中的权限设置。
  • attr:指向结构体 mq_attr 的指针,指定消息队列的属性,如最大消息数、消息大小等。

返回值

  • 如果函数执行成功,返回一个非负整数,表示消息队列的标识符(文件描述符)。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

注意事项

  • 消息队列的名称在系统中必须是唯一的,不同进程可以通过相同名称打开同一个消息队列。
  • 消息队列的名称需要以斜杠 "/" 开头,必须符合路径名规则(最多由PATH_MAX个字节构成,包括结尾的空字节)。
  • 创建的消息队列以文件形式保存在 /dev/mqueque 目录下。

用法

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<mqueue.h>
 4 #include<sys/stat.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     mqd_t mqd = mq_open("/myqueue", O_RDONLY | O_CREAT | O_EXCL, 0666, NULL);
 9     if(mqd == -1)
10     {
11         perror("mq_open");
12         return -1;
13     }
14 
15     return 0;
16 }

如果代码中使用了 POSIX 消息队列相关代码,在编译时,需要指定额外的库文件,例如:

gcc my_mqueue.c -o a.out -lrt

输出:

$ ./a.out
$ ls -l /dev/mqueue/
总用量 0
-rw-rw-r-- 1 test test 80 12月  21 10:04 myqueue

关闭消息队列-mq_close()函数

mq_close()函数用于关闭一个已经打开的消息队列描述符,该函数定义如下:

#include <mqueue.h>

int mq_close(mqd_t mqdes);

参数说明

  • mqdes:待关闭的消息队列描述符。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

删除消息队列-mq_unlink()函数

mq_unlink()函数用于删除已经存在的消息队列,即删除消息队列的名称。该函数定义如下:

#include <mqueue.h>

int mq_unlink(const char *name);

参数说明

  • mqdes:要删除的消息队列的名称。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

注意事项

  • 如果在调用 mq_unlink() 函数之前,没有其他进程打开该消息队列,那么该消息队列将被立即删除,并且所有之前打开的描述符将成为无效。
  • 如果有其他进程打开了该消息队列,但没有对该消息队列进行写入或读取操作,那么调用 mq_unlink() 函数后,对该消息队列的访问将立即被阻止,但消息队列本身将保持存在。
  • 如果有其他进程当前正在使用该消息队列(执行了读写操作),删除操作将会延迟到所有打开的描述符都关闭或相应进程终止时才会生效。

消息队列的属性

struct mq_attr结构体

struct mq_attr结构体用于描述 POSIX 消息队列属性,包含如下成员:

struct mq_attr {
    long mq_flags;
    long mq_maxmsg;
    long mq_msgsize;
    long mq_curmsgs;
};

各个成员的含义如下:

  • mq_flags:标志位,取值为0 或者 O_NONBLOCK(非阻塞)
  • mq_maxmsg: 是消息队列中允许的最大消息数,默认是10。
  • mq_msgsize:消息队列中消息的最大大小,单位是字节,默认是8192字节。
  • mq_curmsgs:输出参数,当前队列中的消息数。

查看消息队列默认值:

cat /proc/sys/fs/mqueue/msg_max     #查看消息队列的消息最大长度
cat /proc/sys/fs/mqueue/msgsize_max #查看消息队列的消息最大个数

在消息队列的四个属性中:

  • mq_curmsgs只能获取不能设置。
  • mq_flags只能通过 mq_setattr() 函数设置,该函数的唯一作用就是设置或清除非阻塞标志。
  • mq_maxmsg 和 mq_msgsize 只能在创建新队列时由 mq_open() 函数的 attr 参数设置。
  • mq_maxmsg 和 mq_msgsize 必须同时指定,否则 mq_open() 函数创建新队列会失败。
  • O_NONBLOCK 标识不能通过 mq_flags 指定,可以通过 mq_open() 函数 oflag 参数指定,如果想要移除,通过 mq_setattr() 函数设置。

获取消息队列属性-mq_getattr()函数

mq_getattr()函数用于获取消息队列的属性信息,该函数定义如下:

#include <mqueue.h>

int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); 

参数说明

  • mqdes:要获取属性的消息队列描述符。
  • mqstat:指向 struct mq_attr 结构的指针,用于存储获取的属性信息。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

设置消息队列属性-mq_setattr()函数

mq_setattr()函数用于设置消息队列的属性,该函数定义如下:

#include <mqueue.h>

int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, 
               struct mq_attr *omqstat); 

参数说明

  • mqdes:要设置属性的消息队列描述符。
  • mqstat:指向 struct mq_attr 结构的指针,指向新的属性信息。
  • omqstat:指向 struct mq_attr 结构的指针,保存原先的属性信息。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

消息发送与接收

消息发送-mq_send()函数

mq_send() 函数用于向消息队列发送消息,该函数定义如下:

#include <mqueue.h>

int mq_send(mqd_t mqdes, const char *msg_ptr, 
                size_t msg_len, unsigned int msg_prio);           

参数说明

  • mqdes:消息队列描述符。
  • msg_ptr:指向要发送的消息的指针。
  • msg_len:要发送的消息的长度,不能超过消息队列的最大长度。
  • msg_prio:消息的优先级, 数值越大,优先级越高。优先级低的消息会排在优先级高的消息之后。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
  • 如果消息队列已满,mq_send() 函数会阻塞直到有空间可用。
  • 如果设置了非阻塞标志,此时会立即返回,错误码为 EAGAIN。

消息接收-mq_receive函数

mq_receive() 函数用于从消息队列接收消息。该函数定义如下:

#include <mqueue.h>

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, 
                         size_t msg_len, unsigned int *msg_prio);

参数说明

  • mqdes:消息队列描述符。
  • msg_ptr:指向接收消息的缓冲区的指针,用于存储接收到的消息。
  • msg_len:接收消息的最大长度,必须大于等于消息的实际长度。
  • msg_prio:用于存储接收到的消息的优先级。

返回值

  • 如果函数执行成功,返回接收到的消息的长度(即实际读取的字节数)。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
  • 如果消息队列为空,mq_receive() 函数会阻塞直到有消息可用。
  • 如果设置了非阻塞标志,此时会立即返回,错误码为 EAGAIN。

示例

进程A负责读取:

 1 #include<stdio.h>
 2 #include<sys/stat.h>
 3 #include<mqueue.h>
 4 #include<fcntl.h>
 5 #include<errno.h>
 6 #include<unistd.h>
 7 #include<stdlib.h>
 8 
 9 int main(int argc, char** argv)
10 {
11     mqd_t mqd = mq_open("/my_mqueue", O_RDONLY);
12     if(mqd == -1)
13     {
14         perror("mq_open");
15         return -1;
16     }
17 
18     struct mq_attr attr;
19     if(mq_getattr(mqd, &attr) == -1)
20     {
21         perror("mq_getattr");
22         return -1;
23     }
24     
25     char* pBuf = (char*)malloc(attr.mq_msgsize);
26     if(pBuf == NULL)
27     {
28         perror("malloc");
29         return -1;
30     }
31 
32     int prio;
33     while(1)
34     {
35         int ret = mq_receive(mqd, pBuf,attr.mq_msgsize, &prio);
36         if(ret == -1)
37         {
38             if(errno == EAGAIN)
39             {
40                 printf("no block, contimue to read\n");
41                 continue;
42             }
43             perror("mq_receive");
44             return -1;
45         }
46         pBuf[ret] = '\0';
47         printf("read data : %s, prio : %d\n", pBuf, prio);
48         sleep(1);
49     }
50 
51     free(pBuf);
52     return 0;
53 }

进程B负责写入:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<unistd.h>
 5 #include<sys/stat.h>
 6 #include<errno.h>
 7 #include<mqueue.h>
 8 
 9 int main(int argc, char** argv)
10 {
11     struct mq_attr attr0;
12     //attr0.mq_flags = O_NONBLOCK;      //在此处指定无效
13     attr0.mq_maxmsg = 5;
14     attr0.mq_msgsize = 4096;
15     mqd_t mqd;
16 
17 AGAIN:
18     mqd = mq_open("/my_mqueue", O_WRONLY | O_CREAT | O_EXCL /*| O_NONBLOCK*/, 0666, &attr0);
19     if(mqd == -1)
20     {
21         if(errno == EEXIST)
22         {
23             mq_unlink("/my_mqueue");
24             goto AGAIN;
25         }
26         perror("mq_open");
27         return -1;
28     }
29 
30     struct mq_attr attr;
31     if(mq_getattr(mqd, &attr) == -1)
32     {
33         perror("mq_getattr");
34         return -1;
35     }
36 
37     char* pBuf = (char*)malloc(attr.mq_msgsize);
38     if(pBuf == NULL)
39     {
40         perror("malloc");
41         return -1;
42     }
43 
44 /*  
45     attr.mq_flags = 0;    //取消非阻塞
46     if(mq_setattr(mqd, &attr, NULL) == -1)
47     {
48         perror("mq_setattr");
49         return -1;
50     }
51 */
52     int i = 0;
53     while(1)
54     {
55         sprintf(pBuf, "Data%d", i++);
56         int ret = mq_send(mqd, pBuf, strlen(pBuf) + 1, 10);
57         if(ret == -1)
58         {
59             if(errno == EAGAIN)
60             {
61                 printf("no block, continue to write\n");
62                 continue;
63             }
64             perror("mq_send");
65             return -1;
66         }
67         printf("send data : %s\n", pBuf);
68         sleep(1);
69     }
70 
71     free(pBuf);
72     return 0;
73 }
posted @ 2024-03-07 20:00  西兰花战士  阅读(199)  评论(0编辑  收藏  举报