消息队列(Message Queue)简介
消息队列是一种进程间通信(IPC)机制,允许不同进程通过发送和接收消息进行 异步通信。它的核心特点包括:
-
解耦:消息队列解耦了生产者和消费者,简化了系统设计。
-
持久化存储:支持将消息存储在队列中,保证数据不会丢失。
-
流量控制:适用于分布式系统或需要流量控制的场景。
在 Linux 系统中,常用的消息队列实现包括 System V 消息队列 和 POSIX 消息队列。这里将以 System V 消息队列 为例,介绍其 C 语言 API 的使用方法。
核心 API 函数 💻
1. 创建/获取消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
-
key
:队列的唯一标识符,通常通过ftok
生成。 -
msgflg
:权限标志(如IPC_CREAT | 0666
)。 -
返回值:消息队列的 ID,失败返回
-1
。
2. 发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
-
msqid
:消息队列 ID。 -
msgp
:指向消息结构体的指针。 -
msgsz
:消息数据部分的长度。 -
msgflg
:标志(如0
或IPC_NOWAIT
)。
3. 接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
-
msgtyp
:消息类型(0
表示任意类型)。 -
其他参数与
msgsnd
类似。
4. 控制消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
-
cmd
:控制命令(如IPC_RMID
删除队列)。
消息结构体 📜
消息结构体包含一个 long
类型的消息类型字段,后跟消息数据部分:
struct msgbuf {
long mtype; // 消息类型(必须 > 0)
char mtext[64]; // 消息数据(可自定义长度)
};
示例代码 📑
发送端(sender.c
)
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype;
char mtext[64];
};
int main() {
key_t key = ftok(".", 'a'); // 生成唯一 key
int msqid = msgget(key, IPC_CREAT | 0666);
if (msqid == -1) {
perror("msgget");
exit(1);
}
struct msgbuf msg;
msg.mtype = 1; // 消息类型
strcpy(msg.mtext, "Hello from sender!");
if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
exit(1);
}
printf("Message sent: %s\n", msg.mtext);
return 0;
}
接收端(receiver.c
)
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
struct msgbuf {
long mtype;
char mtext[64];
};
int main() {
key_t key = ftok(".", 'a');
int msqid = msgget(key, 0666);
if (msqid == -1) {
perror("msgget");
exit(1);
}
struct msgbuf msg;
if (msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0) == -1) { // 接收类型为 1 的消息
perror("msgrcv");
exit(1);
}
printf("Received: %s\n", msg.mtext);
// 删除消息队列
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
return 0;
}
编译与运行 ⚙️
-
编译:
gcc sender.c -o sender gcc receiver.c -o receiver
-
运行:
-
先运行 发送端:
./sender
-
再运行 接收端:
./receiver
-
关键注意事项 ⚠️
-
权限问题:确保
msgget
的权限标志(如0666
)允许进程访问队列。 -
消息类型:
mtype
必须为正整数,用于区分不同类型的消息。 -
队列持久性:即使进程退出,消息队列仍会保留,需显式调用
msgctl(IPC_RMID)
删除。 -
错误处理:所有 API 调用后应检查返回值,避免资源泄漏。
通过消息队列,可以实现 可靠的进程间通信,特别适用于生产者和消费者速度不匹配的场景。🚀