消息队列
消息队列的核心特点
特性 |
说明 |
结构化消息 |
消息有类型和内容 |
异步通信 |
发送和接收可以不同步 |
类型过滤 |
可以只接收特定类型的消息 |
持久化 |
消息会保留直到被读取 |
消息队列的四个核心函数
函数 |
作用 |
比喻 |
msgget() |
创建/获取消息队列 |
租用邮局信箱 |
msgsnd() |
发送消息 |
投递信件 |
msgrcv() |
接收消息 |
收取信件 |
msgctl() |
控制(删除等) |
退租信箱 |
例子:
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
// 定义消息结构
struct msgbuf {
long mtype;
char mtext[100];
};
int main() {
int msgid;
struct msgbuf msg;
// 1. 创建消息队列
msgid = msgget(IPC_PRIVATE, 0666);
printf("1. 创建消息队列,ID = %d\n", msgid);
pid_t pid = fork();
if (pid == 0) {
// 子进程:发送消息
sleep(1);
// 准备消息
msg.mtype = 1; // 消息类型1
strcpy(msg.mtext, "Hello from Child!");
// 2. 发送消息
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
printf("2. 子进程发送: type=%ld, content=%s\n", msg.mtype, msg.mtext);
} else {
// 父进程:接收消息
printf("3. 父进程等待接收消息...\n");
// 3. 接收类型为1的消息
msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);
printf("4. 父进程收到: type=%ld, content=%s\n", msg.mtype, msg.mtext);
wait(NULL);
// 4. 删除消息队列
msgctl(msgid, IPC_RMID, NULL);
printf("5. 消息队列已删除\n");
}
return 0;
}
int msgid = msgget(IPC_PRIVATE, 0666);
// IPC_PRIVATE: 创建新的消息队列
// 0666: 读写权限
struct msgbuf msg;
msg.mtype = 1; // 设置消息类型
strcpy(msg.mtext, "Hello"); // 设置消息内容
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
// msgid: 消息队列ID
// &msg: 消息结构体指针
// sizeof(msg.mtext): 消息内容长度(不包括mtype)
// 0: 标志(0=阻塞)
struct msgbuf msg;
msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);
// msgid: 消息队列ID
// &msg: 接收缓冲区
// sizeof(msg.mtext): 缓冲区大小
// 1: 接收类型为1的消息(0=接收任何类型)
// 0: 标志(0=阻塞)
多类型消息:
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
struct msgbuf {
long mtype;
char mtext[100];
};
int main() {
int msgid = msgget(IPC_PRIVATE, 0666);
struct msgbuf msg;
pid_t pid = fork();
if (pid == 0) {
// 子进程:发送多种类型的消息
msg.mtype = 1; // 普通消息
strcpy(msg.mtext, "普通通知");
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
msg.mtype = 10; // 紧急消息
strcpy(msg.mtext, "紧急警报!");
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
msg.mtype = 5; // 警告消息
strcpy(msg.mtext, "系统警告");
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
printf("子进程发送了3条消息\n");
} else {
// 父进程:按优先级接收消息
sleep(1);
// 先接收紧急消息(类型10)
msgrcv(msgid, &msg, sizeof(msg.mtext), 10, 0);
printf收到紧急消息: %s\n", msg.mtext);
// 再接收警告消息(类型5)
msgrcv(msgid, &msg, sizeof(msg.mtext), 5, 0);
printf("收到警告消息: %s\n", msg.mtext);
// 最后接收普通消息(类型1)
msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0);
printf("收到普通消息: %s\n", msg.mtext);
wait(NULL);
msgctl(msgid, IPC_RMID, NULL);
}
return 0;
}
消息队列的阻塞特性
发送阻塞:
// 如果消息队列已满,msgsnd会阻塞
msgsnd(msgid, &msg, sizeof(msg.mtext), 0); // 阻塞直到有空间
// 非阻塞发送(队列满立即返回错误)
msgsnd(msgid, &msg, sizeof(msg.mtext), IPC_NOWAIT);
接收阻塞:
// 如果没有消息,msgrcv会阻塞
msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0); // 阻塞直到有消息
// 非阻塞接收(没有消息立即返回)
msgrcv(msgid, &msg, sizeof(msg.mtext), 1, IPC_NOWAIT);
消息队列 vs 其他IPC
特性 |
消息队列 |
管道 |
共享内存 |
数据结构 |
结构化消息 |
字节流 |
原始内存 |
同步 |
内置同步 |
需要额外同步 |
需要额外同步 |
持久化 |
消息持久保存 |
数据不持久 |
数据持久 |
复杂度 |
中等 |
简单 |
复杂 |