linux进程间通信之消息队列
摘要
本文旨在深入探讨Linux进程间通信中的消息队列机制,包括其工作原理、系统调用接口以及实际应用场景。通过理论分析和示例代码的解读,本文将帮助读者更好地理解消息队列在多进程环境中的作用和应用。
一、引言
在Linux操作系统中,进程间通信(IPC)是一种常见的需求。为了实现进程间的同步和协调,Linux提供了多种IPC机制,包括管道、消息队列、共享内存和信号量等。其中,消息队列是一种灵活且高效的进程间通信方式,它允许进程之间发送和接收消息。本文将重点介绍消息队列的工作原理、系统调用接口以及一个简单的示例代码。
二、消息队列工作原理
消息队列是一种在进程之间传递消息的数据结构。它由一系列消息组成,每个消息都有一个特定的优先级和内容。当一个进程需要发送消息时,它可以将消息添加到队列中。接收进程可以从队列中获取消息并根据其优先级进行处理。
三.系统调用接口
1.mq_open
用于创建或打开一个消息队列。它接受四个参数:消息队列的名称、打开方式、消息的属性和一个指向mq_attr
结构体的指针。
2.mq_send
用于向消息队列发送消息。它接受三个参数:消息队列的描述符、要发送的消息和消息的优先级。
3.mq_receive
用于从消息队列接收消息。它接受三个参数:消息队列的描述符、存放接收消息的缓冲区和消息的最大长度。
4.mq_close
用于关闭一个消息队列。它接受一个参数:消息队列的描述符。
5.mq_unlink
用于删除一个消息队列。它接受一个参数:消息队列的名称。
四.消息队列的特点
五.代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <unistd.h>
#include <sys/wait.h>
#define QUEUE_NAME "/my_queue" // 消息队列名称
#define MAX_MSG_SIZE 1024 // 消息的最大长度
#define MAX_MSG_NUM 10 // 队列中最多存储的消息数
#define PRIORITY 1 // 消息的优先级
int main() {
mqd_t mq; // 消息队列描述符
char buf[MAX_MSG_SIZE]; // 存放接收到的消息的缓冲区
struct mq_attr attr; // 消息队列的属性
pid_t pid; // 子进程ID
int status; // 子进程状态
// 初始化消息队列属性,使用默认值
attr.mq_flags = 0;
attr.mq_maxmsg = MAX_MSG_NUM;
attr.mq_msgsize = MAX_MSG_SIZE;
attr.mq_curmsgs = 0;
attr.mq_perm.uid = getuid(); // 设置拥有者的用户ID
attr.mq_perm.gid = getgid(); // 设置拥有者的组ID
attr.mq_perm.mode = S_IRUSR | S_IWUSR; // 设置访问权限
// 创建或打开一个名为QUEUE_NAME的消息队列,使用attr属性结构体进行初始化,不阻塞状态打开方式,返回一个描述符mq给当前线程使用该对象进行后续操作,使用后必须调用mq_close关闭该对象,如果不再使用该对象,需要调用mq_unlink删除该对象。如果该对象不存在,则创建该对象。如果该对象已经存在,则打开该对象。如果返回值为-1,则表示打开失败,如果返回值为非负数,则表示成功打开对象并返回对象的描述符。 -O_CREAT表示创建对象,-O_RDONLY表示以只读方式打开对象,-O_WRONLY表示以只写方式打开对象,-O_RDWR表示以读写方式打开对象。attr指定了对象的最大消息数、每个消息的最大字节长度以及对象的初始状态(0表示非阻塞状态)。如果对象不存在,则创建该对象;如果对象已经存在,则打开该对象并返回对象的描述符。如果返回值为-1,则表示打开失败;如果返回值为非负数,则表示成功打开对象并返回对象的描述符。 -O_CREAT表示创建对象;-O_RDONLY表示以只读方式打开对象;-O_WRONLY表示以只写方式打开对象;-O_RDWR表示以读写方式打开对象。
mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0664, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(EXIT_FAILURE);
}
// 创建子进程并等待其结束
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid > 0) { // 父进程
// 父进程向消息队列发送消息
if (mq_send(mq, "Hello from parent", MAX_MSG_SIZE, PRIORITY) == -1) {
perror("mq_send");
exit(EXIT_FAILURE);
} else {
printf("Parent process sent message.\n");
}
// 等待子进程结束并获取其状态
waitpid(pid, &status, 0);
printf("Child process exited with status %d.\n", status);
} else { // 子进程
// 子进程从消息队列接收消息并打印出来
if (mq_receive(mq, buf, MAX_MSG_SIZE, NULL) == -1) {
perror("mq_receive");
exit(EXIT_FAILURE);
} else {
printf("Child process received message: %s\n", buf);
}
// 子进程正常结束并释放资源
exit(EXIT_SUCCESS);
}
// 关闭消息队列并删除该对象(当且仅当队列中没有消息时才删除)如果成功删除则返回0;如果删除失败,则返回-1。
// 关闭消息队列并删除该对象(当且仅当队列中没有消息时才删除)如果成功删除则返回0;如果删除失败则返回-1。
if (mq_close(mq) == -1) {
perror("mq_close");
exit(EXIT_FAILURE);
}
if (mq_unlink(QUEUE_NAME) == -1) {
perror("mq_unlink");
exit(EXIT_FAILURE);
}
return 0;
}
六.代码分析
使用了mq_open
函数来创建或打开一个名为QUEUE_NAME
的消息队列,并使用attr
属性结构体进行初始化。其中,O_CREAT
表示创建对象,O_RDWR
表示以读写方式打开对象,0664
是文件权限的掩码,表示拥有者具有读写权限,组用户和其他用户具有读权限。attr
结构体中的mq_maxmsg
表示队列中最多存储的消息数,mq_msgsize
表示每个消息的最大长度,mq_curmsgs
表示队列中当前的消息数,mq_perm
表示访问权限和拥有者信息。
在父进程中,我们使用mq_send
函数向消息队列发送一条消息,并指定了消息的内容、最大长度和优先级。在子进程中,我们使用mq_receive
函数从消息队列中接收消息,并指定了缓冲区和最大长度。如果接收成功,则打印出消息的内容。
最后,我们使用mq_close
函数关闭消息队列,并使用mq_unlink
函数删除该对象。需要注意的是,只有当队列中没有消息时才能删除对象。
需要注意的是,实际应用中可能需要更多的错误处理和异常情况处理。同时,还需要注意消息队列的使用方式和限制,例如消息的最大长度和优先级等参数需要根据实际需求进行设置。