Linux系统编程之消息队列
概述
消息队列是一种进程间通信机制,允许不同的进程通过发送和接收消息来进行数据交换。实际上,消息队列是一种基于队列的数据结构,用于存储多个消息。每个消息由一个类型和相应的数据组成,类型可以用来区分不同类型的消息或优先级。
消息队列的主要优势包括如下三点。
1、灵活性。支持多种消息类型,可以根据需要定义不同类型的消息。
2、可靠性。消息不会丢失,即使发送者和接收者不在同一时间运行,消息也会被保存在队列中直到被处理。
3、异步通信。发送者和接收者不需要同时在线,消息可以在任何时间点被发送或接收。
在Linux系统中,消息队列主要通过POSIX标准接口和System V IPC接口来实现。
POSIX消息队列
POSIX消息队列基于POSIX标准,提供了比System V消息队列更简单的接口,并且可以在文件系统中作为一个特殊的文件节点来访问。POSIX消息队列可以通过一个路径名来标识,这使得不同的进程可以使用这个路径名来访问同一个消息队列。当消息队列的状态改变时(比如:消息队列从空变为非空),可以向指定的进程发送异步通知。POSIX消息队列适用于那些需要进程间高效、可靠通信的应用场景,由于它支持优先级排队和异步通知等特性,特别适合实时系统中的应用。
POSIX消息队列主要通过以下函数来实现。
mq_open:创建一个新的消息队列,或打开一个已经存在的消息队列。
mq_send:将一条消息放入到消息队列中。
mq_receive:从消息队列中接收消息。
mq_close:关闭消息队列。
mq_unlink:删除消息队列的名字,使其他进程无法再访问该消息队列。
在下面的实战代码中,我们通过POSIX消息队列在两个进程之间发送和接收消息。
首先,我们创建了一个接收者进程,用于打开消息队列,并接收消息。
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define QUEUE_NAME "/queue_hope"int main()
{// 初始化属性struct mq_attr attr;attr.mq_flags = 0;attr.mq_maxmsg = 10;attr.mq_msgsize = 1024;attr.mq_curmsgs = 0;// 打开消息队列mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDONLY, 0644, &attr);if (mq == (mqd_t)-1){printf("mq_open failed\n");return -1;}// 接收消息char pszBuf[1024] = {0};unsigned int prio = 0;ssize_t bytes_read = mq_receive(mq, pszBuf, sizeof(pszBuf), &prio);if (bytes_read >= 0){printf("Recv message: '%s' with priority %u\n", pszBuf, prio);}else{printf("mq_receive failed\n");}// 关闭并删除消息队列mq_close(mq);mq_unlink(QUEUE_NAME);return 0;
}
接下来,我们创建了一个发送者进程,用于发送消息到之前定义的消息队列。
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define QUEUE_NAME "/queue_hope"int main()
{// 打开已存在的消息队列mqd_t mq = mq_open(QUEUE_NAME, O_WRONLY);if (mq == (mqd_t)-1){printf("mq_open failed\n");return -1;}// 发送消息char pszBuf[] = "Hello, Hope_Wisdom";unsigned int prio = 1;if (mq_send(mq, pszBuf, strlen(pszBuf), prio) != 0){printf("mq_send failed\n");}// 关闭消息队列mq_close(mq);return 0;
}
System V消息队列
由于其设计较早,System V消息队列可能在某些方面不如POSIX消息队列高效,特别是在现代实时应用中。System V消息队列提供了较为复杂的API,这些函数提供了对消息队列更细致的控制,但也意味着更高的学习成本和实现复杂度。System V消息队列支持发送不同类型的消息,并允许接收方根据消息类型选择性地接收消息。每个消息都必须指定一个类型(大于0的整数),这为消息过滤提供了灵活性。
System V消息队列主要通过以下函数来实现。
msgget:获取一个消息队列的标识符,如果不存在则创建一个新的。
msgsnd:向指定的消息队列发送一条消息。
msgrcv:从指定的消息队列接收一条消息。
msgctl:执行多种控制操作,如获取消息队列属性、设置消息队列属性、删除消息队列等。
在下面的实战代码中,我们通过System V消息队列在两个进程之间发送和接收消息。
首先,我们创建了一个接收者进程,用于打开消息队列,并接收消息。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>struct message
{long mtype;char mtext[100];
};int main()
{// 创建唯一的键key_t key = 0;if ((key = ftok("progfile", 66)) == -1){printf("ftok failed\n");return -1;}// 获取消息队列IDint msgid;if ((msgid = msgget(key, 0666 | IPC_CREAT)) == -1){printf("msgget failed\n");return -1;}// 接收消息struct message msg;if (msgrcv(msgid, &msg, sizeof(msg), 1, 0) == -1){printf("msgrcv failed\n");return -1;}printf("Recv Message: %s\n", msg.mtext);return 0;
}
接下来,我们创建了一个发送者进程,用于发送消息到之前定义的消息队列。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>struct message
{long mtype;char mtext[100];
};int main()
{// 创建唯一的键key_t key;if ((key = ftok("progfile", 66)) == -1){printf("ftok failed\n");return -1;}// 获取消息队列IDint msgid;if ((msgid = msgget(key, 0666 | IPC_CREAT)) == -1){printf("msgget failed\n");return -1;}// 设置消息类型和内容struct message msg;msg.mtype = 1;strcpy(msg.mtext, "Hello, Hope_Wisdom");// 发送消息if (msgsnd(msgid, &msg, sizeof(msg), 0) == -1){printf("msgsnd failed\n");return -1;}printf("Message Sent\n");return 0;
}