进程通信之消息队列


消息队列

消息队列是一种进程间通信(IPC)机制,允许进程以消息的形式传递数据。它通过在内核中维护的队列实现,提供了异步通信的能力。消息队列由键值唯一标识,支持一个或多个进程之间的数据传输。

相关函数

msgget

  • 功能:创建或获取一个消息队列
  • 函数原型:
1
int msgget(key_t key, int msgflg);
  • 参数:
    • key:消息队列的键即标识,可以通过 ftok 生成
    • msgflg:访问权限。
      • IPC_CREAT —— 如果 key 不存在,则创建
      • IPC_EXCL —— 如果 key 存在,则返回失败
  • 返回值:成功返回消息队列标识符(非负整数),失败返回 -1

msgsnd

  • 功能:向消息队列发送消息
  • 原型:
1
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • 参数:
    • msqid:消息队列标识符,也就是上述创建的返回值
    • msgp:指向消息的指针,是个结构体,但需要自己手动定义(放下面了)
    • msgsz:消息正文的大小(不包括消息类型)
    • msgflg:操作标志(如 IPC_NOWAIT 非阻塞发送,即如果包含此选项,则消息队列满时,不发送该消息,立即返回 -1。反之,如果不包含此选项,则消息队列满时,挂起本进程,直到消息队列由空间可用。)
1
2
3
4
5
// 结构体的定义
struct message {
long msg_type; // 消息类型,必须为 long 类型即 long int
char msg_text[100]; // 消息正文,数组大小自定义
};
  • 返回值:成功返回 0,失败返回 -1

msgrcv

  • 功能:从消息队列接收消息
  • 函数原型:
1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
  • 参数:
    • msqid:消息队列标识符,即创建消息队列时候的函数返回值
    • msgp:存储接收到的消息的指针(上面 msgsnd 已介绍)
    • msgsz:期望接收的消息正文大小
    • msgtyp:消息类型(0 表示接收任意类型)
      • 0:忽略消息类型,从消息队列中接收队列中的第一条消息(按照队列的 FIFO 顺序)
      • 大于 0:从消息队列中接收类型为 msg_type 的第一条消息,如果队列中没有匹配的消息,进程会阻塞,直到符合条件的消息到达(或设置了非阻塞标志)
      • 小于 0:从消息队列中接收类型值小于等于 |msg_type|(绝对值)的消息,选择类型值小于等于 |msg_type| 且优先级最高的消息(类型值越小,优先级越高)
    • msgflg:操作标志(如 IPC_NOWAIT 非阻塞接收,上述 msgsnd 已经介绍)
  • 返回值:成功返回消息队列标识符(非负整数),失败返回 -1

msgctl

  • 功能:控制和管理消息队列
  • 原型:
1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);`
  • 参数:
    • msqid:消息队列标识符,即创建消息队列时候的函数返回值
    • cmd:控制命令,指定要对消息队列执行的操作
      • IPC_STAT:获取消息队列的当前状态
      • IPC_SET:设置消息队列的属性
      • IPC_RMID:从系统中删除消息队列
    • buf:指向一个 msqid_ds 结构的指针,用于传递或接收消息队列的信息
      • **IPC_STAT**:由内核填充 buf,包含消息队列的当前状态,然后作为传出参数,用户可以进行读取数据查看消息队列状态
      • **IPC_SET**:用户手动填充 buf 中的部分字段,将其作为传入参数,修改消息队列的属性
      • **IPC_RMID**:此参数可以为 NULL,因为该操作不需要 buf
  • 返回值:成功返回 0,失败返回 -1

示例

发送方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// 定义消息结构
struct msgbuf {
long msg_type; // 消息类型
char msg_text[256]; // 消息内容
};

int main()
{
key_t key;
int msqid;
struct msgbuf msg;

// 生成唯一键值
key = ftok(".", 123);
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}

// 创建或获取消息队列
msqid = msgget(key, IPC_CREAT | 0666);
if (msqid == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
printf("Message queue created with ID: %d\n", msqid);

// 循环发送消息
for (int i = 1; i <= 5; ++i)
{
msg.msg_type = 1; // 消息类型为 1
snprintf(msg.msg_text, sizeof(msg.msg_text), "Message %d from sender", i);

if (msgsnd(msqid, &msg, strlen(msg.msg_text) + 1, 0) == -1) {
perror("msgsnd");
exit(EXIT_FAILURE);
}
printf("Sent: %s\n", msg.msg_text);
sleep(1); // 模拟延时
}
return 0;
}

接收方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// 定义消息结构
struct msgbuf {
long msg_type; // 消息类型
char msg_text[256]; // 消息内容
};

int main()
{
key_t key;
int msqid;
struct msgbuf msg;

// 生成唯一键值
key = ftok(".", 123);
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}

// 获取消息队列
msqid = msgget(key, 0666);
if (msqid == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
printf("Message queue opened with ID: %d\n", msqid);

// 循环接收消息
while (1)
{
if (msgrcv(msqid, &msg, sizeof(msg.msg_text), 1, 0) == -1) {
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("Received: %s\n", msg.msg_text);

// 接收到特定内容时退出
if (strcmp(msg.msg_text, "exit") == 0) {
printf("Received exit signal. Exiting...\n");
break;
}
}
return 0;
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 发送方
Message queue created with ID: 12345
Sent: Message 1 from sender
Sent: Message 2 from sender
Sent: Message 3 from sender
Sent: Message 4 from sender
Sent: Message 5 from sender

// 接收方
Message queue opened with ID: 12345
Received: Message 1 from sender
Received: Message 2 from sender
Received: Message 3 from sender
Received: Message 4 from sender
Received: Message 5 from sender