Linux中的消息队列是进程间通信的一种方式,通过创建一个消息队列可以完成一个或者多个进程的信息交汇。因此,首先我们要了解消息队列是什么。
消息队列的定义
消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了一个数据结构,并且通过维护这个数据结构来维护这个消息队列。向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;从消息队列汇总读数据,实际上是从这个数据结构中删除一个结点。
消息队列的创建
消息队列的创建通过调用msgget()
函数来创建,函数原型为:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数key
相当于文件系统的文件名,msgflg
是一个操作指令,当想要创建一个消息队列时可以填IPC_CREAT | 0644
,前面指的是创建的指令,后面的0644
指的是消息队列的执行权限,因为在Linux下一切皆文件,是文件就有执行权限,因此在创建消息队列时要加上执行权限,这里要注意的是0644
这个操作参数不受umask的影响。因此你设置的是多少,得到的操作权限就是多少。
消息队列的发送和接受
值得注意的是,不管是接受消息队列还是在发送消息队列时,都需要用户自己定义一个结构体,用来存储数据,并作为函数参数传入。结构体如下(由自己定义):
struct msgbuf{
long mtype;
char mtext[1];
}
这个结构体存在两个成员,一个是long
型的变量,表示该消息的类型,因为消息队列的特性,在发送或者接受消息是要明确消息的类型,这样在就可以指定的读取某一个或者或者某一类的消息,同时可以改变消息接受的优先级。
至于为什么需要这个数据结构,是因为消息队列是面向数据块的,读取数据和发送数据都是以结构体的形式进行传递,因此需要我们自己定义数据结构,面向数据块也是消息队列的一个重要特性。
- 发送消息
向消息队列中添加数据,使用到的是msgsnd()
函数,函数原型如下:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
总共有4个参数,首先要有一个msqid
即消息队列的id,通过msgget(key, 0)
可以得到;第二个参数便是我们定义的结构体,要将地址传递过去;第三个参数是发送数据的大小,即struct msgbuf
结构体中mtext
的大小,不包括数据的类型;最后一个参数并不重要,我们可以不必了解,在使用时一律填0即可。
我们在发送数据时要将数据的类型填入mtype
,将数据填入mtext
,然后这个结构体作为参数传入消息队列,这样便可以实现数据的优先级接受。
- 接受消息
从消息队列中读取消息,我们使用msgrcv()
函数,函数原型如下:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msgrcv()
函数有5个参数,比msgsnd()
函数多了一个参数,即第4个参数数据类型,因为发送消息时,数据的类型在结构体中保存着,因此不需要再次传入,可是在读消息时我们需要读取特定类型的消息,因此我们要将指定的数据类型作为参数传入。以满足我们的优先级读取。当msgtyp==0
时即按照先进先出的顺序读取当前消息队列中第一个消息。
注意点
msgsnd
函数的返回值:成功返回0,失败返回-1;msgrv
函数的返回值:成功返回读取的数据大小,失败返回-1数据队列有三个上限:每个数据最大长度上限(MSGMAX),每个数据队列的总字节数上限(MSGMNB),系统上消息队列的总数的上限(MSGMNI)。
每个数据最大长度上限(MSGMAX) /proc/sys/kernel/msgmax 每个数据队列的总字节数上限(MSGMNB) /proc/sy/kernel/msgmnb 系统上消息队列的总数的上限(MSGMNI) /proc/sys/kernel/msgmni
消息队列的销毁
通过调用 msgctl()
函数可以,销毁消息队列。函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数参数有三个,第一个是打开或者创建消息队列得到的id,第二个是操作数,销毁的操作数为IPC_RMID
,第三个操作数我们不关心一般填0。
查看消息队列
我们可以通过 ipcs -q
命令查看已经创建的消息队列,包括他的key值信息,id信息,拥有者信息,文件权限信息,已使用的字节数,和消息条数。
最后我们也可以使用 ipcrm -Q
加消息队列的key值,来删除一个消息队列。