1 消息队列(Message Queue)
在应用开发中,生产者,消费者的模型非常常见,一方产生数据并把数据放入队列中,而另一方从队列中取数据,先进先出。
同样,在操作系统内核中,也实现了类似的功能,队列中存放的是“消息”。称之为消息队列,消息也可理解为数据。
主要用途是进程间通信(IPC),所谓通信,就是进行数据交互。
2 接口标准(POSIX和 System V)
关于标准接口,Linux系统中提供了POSIX和 System V这两种不同的接口,POSIX为可移植的操作系统接口。System V 是 AT&T 的第一个商业UNIX版本(UNIX System III)的加强。
System V 时期的不同系统接口不一样,给移植带来了一定的麻烦,而POSIX将不同操作系统之间的上层API进行了统一,更换平台时便于移植应用程序。目前Linux中使用POSIX较多,但System V同样也存在。
自Linux kernel 2.6.6 ,开始支持POSIX 的消息队列API。
3 POSIX版的MQ API
函数接口
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);
int mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio);
int mq_unlink(const char *name);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
int mq_close(mqd_t mqdes);
测试实例:两个终端进行通信,并在dev/mqueue/ 目录中可以看到存在的消息队列
mq3.c 发送消息:"boots"
#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <string.h>
int
main(int argc, char *argv[])
{
mqd_t mq_cmd;
struct mq_attr attr;
char msgbuffer[5];
memcpy(msgbuffer,"boots",5);
/* Open the message queue for reading */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 20;
attr.mq_curmsgs = 0;
mq_cmd = mq_open("/mq_test", O_WRONLY|O_CREAT, 0666, &attr); //为什么要加 / ,否则打开失败
if (mq_cmd < 0){
printf("mq_open error: %d n",mq_cmd);
}else{
printf("mq_open success: %d n",mq_cmd);
}
int nbytes = mq_send(mq_cmd, (char *)msgbuffer, sizeof(msgbuffer), 0);
if (nbytes < 0){
printf("mq_send error: %d n",nbytes);
}else{
printf("mq_send success: %d n",nbytes);
}
if (mq_close(mq_cmd) < 0){
printf("mq_close error! n");
}else{
printf("mq_close success! n");
}
//mq_unlink("/mq_test");
}
mq4.c 接收消息:
#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
int
main(int argc, char *argv[])
{
mqd_t mq_cmd;
struct mq_attr attr;
char msgbuffer[5];
/* Open the message queue for reading */
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 20;
attr.mq_curmsgs = 0;
mq_cmd = mq_open("/mq_test", O_RDONLY|O_CREAT, 0666, &attr); //为什么要加 / ,否则打开失败
if (mq_cmd < 0){
printf("mq_open error: %d n",mq_cmd);
}else{
printf("mq_open success: %d n",mq_cmd);
}
int nbytes = mq_receive(mq_cmd,msgbuffer, 20, NULL); //这里的20, 表示长度大于或等于mq_msgsize,否则返回 -1
if (nbytes < 0){
printf("mq_receive error: %d n",nbytes);
}else{
printf("mq_receive success: %sn",msgbuffer);
}
if (mq_close(mq_cmd) < 0){
printf("mq_close error! n");
}else{
printf("mq_close success! n");
}
}
编译:-lrt ,-l 指的是链接库文件,rt为库的名字rt。也即是librt.a/librt.so
若不加-lrt,链接过程会报错。
gcc mq3.c -o mq3 -lrt
gcc mq4.c -o mq4 -lrt
执行:
设备查看:QSIZE=0表示消息队列中的数据为空,这时如果再去mq_recive()的调用进程 将被阻塞。
cat /dev/mqueue/mq_test
4 System V 版的MQ API
APUE(Unix 环境高级编程)中是以 System V 的API为例进行讲解的。
函数接口
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
测试实例:两个终端进行通信
mq1.c 发送消息:"hello, message queue test..."
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct msgbuf {
long mtype;
char mtext[255];
};
int main() {
// 1. 创建一个消息队列,用 key = 10 来唯一表示这个队列
int msg_id = msgget(10, IPC_CREAT | 0666);
if (msg_id != -1) {
// 2. 初始化要发生的消息
struct msgbuf mybuf;
mybuf.mtype = 1;
strcpy(mybuf.mtext, "hello, message queue test...n");
// 3. 发送消息
int ret = msgsnd(msg_id, &mybuf, sizeof(mybuf.mtext), 0);
if(ret < 0)
printf("error: %d n",ret);
else
printf("success: %d n",ret);
} else {
perror("msgget:");
}
return 0;
}
mq2.c 接收消息:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct msgbuf {
long mtype;
char mtext[255];
};
int main() {
// 1. 获取消息队列
int msg_id = msgget(10, IPC_CREAT | 0666);
if (msg_id != -1) {
struct msgbuf mybuf;
// 2. 接收第一条消息,存到 mybuf 中
// IPC_NOWAIT 表示不阻塞
if (msgrcv(msg_id, &mybuf, sizeof(mybuf.mtext), 0, IPC_NOWAIT) != -1) {
printf("read success: %sn", mybuf.mtext);
} else {
perror("msgsnd:");
}
} else {
perror("msgget:");
}
return 0;
}
编译:
gcc mq3.c -o mq3 -lrt
gcc mq4.c -o mq4 -lrt
执行:
查看:可以查看到key值为10(0x0A)的MQ,包含id,权限,字节数,消息数
ipcs -q