Linux下的进程通信方式之System_V消息队列

Linux下的通信方式之 System_V 消息队列

消息队列:允许进程之间使用以消息的形式传输数据。虽然消息队列在某些地方和管道|FIFO类似,但是仍然存在者显著的差别。

消息队列的特点有:

  • 用来引用消息队列的句柄是一个由 msgget()调用返回的标识符。这些标识符与 UNIX系统上大多数其他形式的 I/O所使用的文件描述符是不同的。

  • 每次在读取数据的时候,读取的单位只能是以整条消息为单位的读取,无法像管道那样想一次读取多少数据就可以有多少数据。

  • 除了包含数据之外,每条消息还有一个用整数表示的类型。从消息队列中读取消息既可以按照先入先出的顺序,也可以根据类型来读取消息。

需要注意的是,在新的一个应用程序中应该要少用 System V 消息队列,这是因为这类消息队列有很大的限制。我们可以使用管道、FIFO、POSIX 消息队列、Socket 来进行替代。

每个队列都有一个msqid_ds结构与其关联:

1
2
3
4
5
6
7
8
struct msqid_ds{
struct ipc_perm msg_perm;
msgqnum_t msg_qnum;
msglen_t msg_qbytes;
pid_t msg_lspid;
pid_t msg_lrpid;
...
}

关于消息队列的几个函数

在前面我们提到,对于 IPC 的创建需要使用 get 系列的函数:

其中void* msg_ptr 这个参数是有要求的,即该结构必须以 loog 类型开头:

1
2
3
4
5
6
7
struct mysmg{
long mtype;
char mtext[];
};

其中第一个参数表示这个消息的类型,直接使用数字表示即可,如123等等
第二个参数是就是消息的存储空间了,并且 mtext 的长度是可以为 0 的,此时对于接收进程来说可以知道某条消息是否存在。

msgget()

1
2
3
4
5
6
#include <sys/types>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

//Returns mesage queue identifier on success, or -1 on error

常用的 megflg 如下:

IPC_CREAT

如果没有与指定的 key对应的消息队列,那么就创建一个新队列。

IPC_EXCL

如果同时还指定了 IPC_CREAT并且与指定的 key对应的队列已经存在,那么调用就会失败并返回 EEXIST错误。

megsnd()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<sys/msg.h>

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

第一个参数msqid是由msgget函数返回的消息队列标识符;
第二个参数msg_ptr是一个指向准备发送消息的指针,消息必须像刚才说的那样以一个长整型成员变量开始;
第三个参数msg_sz是msg_ptr指向的消息的长度。这个长度不能包括长整型消息类型成员变量的长度;
第四个参数msgfig:  
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列。  
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回。  
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程


返回值: 成功:0; 失败:-1

msgrcv()

1
2
3
4
5
6
7
8
9
10
11
12
#include<sys/msg.h>

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
第一个参数msqid是由msgget函数返回的消息队列标识符;

第二个参数msg_ptr是一个指向准备接收消息的指针,消息必须像前面msgsnd函数中介绍的那样以一个长整型成员变量开始。

第三个参数msg_sz是msg_ptr指向的消息的长度,它不包括长整型消息类型成员变量的长度。

第四个参数msgtype是一个长整型,它可以实现一种简单形式的接收优先级。如果msgtype的值为0,就获取队列中的第一个可用消息。如果它的值大于零,将获取具有相同消息类型的第一个消息。如果它的值小于零,将获取消息类型等于或小于msgtype的绝对值的第一个消息。
这个函数看起来好像很复杂,但实际应用很简单。如果只想按照消息发送的顺序来接收它们,就把msgtype设置为0。如果只想获取某一特定类型的消息,就把msgtype设置为相应的类型值。如果想接收类型等于或小于n的消息,就把msgtype设置为-n。
第五个参数msgflg:  0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待。  IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG。  IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息。  IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部成功:实际读取到的消息数据长度。 出错:-1,错误原因存于error中。

作用:从队列中取用消息。

其中对第四个参数举例说明:消息队列中要求通过一个数据类型来索引指定的数据。也就是在msgsng发送数据前会对long int message_type变量进行复制。通过这个变量来标识数据。而且这个变量必须要大于0.

那么在msgrcv中,对第四个参数msgtype进行选值时。

如果等于0,则按照队列的顺序去取,先到先取的原则。

如果大于0,假如msgtype=1。就会取找队列中message_type等于1的队列的排在第一个的消息。

如果小于0,假如msgtype=-2。就会-2取绝对值,也就是等于2。再取队列中message_type等于或者小于2的排在第一个的消息。

msgrcv()解除阻塞的条件有以下三个:

① 消息队列中有了满足条件的消息。

② msqid代表的消息队列被删除。

③ 调用msgrcv()的进程被信号中断。

msgctl()

1
2
3
4
5
6
7
8
9
10
#include<sys/msg.h>

int msgctl(int msqid, int command, struct msqid_ds *buf);
第一个参数msqid是由msgget返回的消息队列标识符;
第二个参数command是将要采取的动作,它可以取3个值。
IPC_STAT 取出队列的msqid_ds结构,并将它存放在buf指向的结构中。
IPC_SET 如果进程有足够的权限,将buf指向的结构复制到与这个队列相关的msqid_ds结构中。
IPC_RMID 从系统中删除该消息队列以及仍在该队列中的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列操作,将得到EIDRM错误。

返回值: 成功:0; 失败:-1

Linux下的进程通信方式之System_V消息队列
https://ysc2.github.io/ysc2.github.io/2024/08/17/Linux下的进程通信方式之System_V消息队列/
作者
Ysc
发布于
2024年8月17日
许可协议