Bootstrap

《Linux系统编程篇》消息队列(Linux 进程间通信(IPC))——基础篇

“山重水复疑无路,柳暗花明又一村。” ——陆游

引言

《Linux系统编程篇》——基础篇首页传送门
想象一下,你正在开发一个多任务处理的应用程序,其中需要不同的模块之间进行数据交换和协作。这时,消息队列就像是一个快递站,负责接收、存储和转发各个模块之间的信息。发送者将消息放入队列,接收者则可以从队列中取出消息并进行处理,实现了模块之间的解耦和异步通信。

消息队列(Message Queue)

消息队列是一种更高级的 IPC 方式,允许多个进程之间以消息的形式进行数据交换。消息队列提供了有序的数据传输,并且允许对消息进行优先级排序。

消息队列是存放消息的链表,他存在于Linux内核当中,每一个消息队列用一个标识符也就是队列id来标识。

消息队列的特点

1、消息队列可以独立发送与接受,成功创建后,如果进程结束,消息队列节点并不会消失,他的消失是由Linux内核 来管理的
2、消息队列是面向记录的,有特定格式及特点的优先级
3、消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序依次读取,也可以使用消息类型读取。

消息队列的特性

  • 异步通信:发送方和接收方不需要同时在线,可以分别发送和接收消息。

  • 消息缓存:消息队列可以缓存一定数量的消息,接收方可以按需处理。

  • 消息优先级:消息队列通常支持消息的优先级设定,确保重要消息被优先处理。

  • 消息持久性:消息队列通常支持消息的持久化,即使接收方不在线,消息也不会丢失。

函数原型及结构体

typedef struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
}msg;
//获取消息队列id号
int msgget(key_t key, int msgflg);
//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接受消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//控制队列(用来销毁)
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

一图带你了解消息队列
在这里插入图片描述

消息队列的操作

  • 创建消息队列:使用msgget系统调用创建消息队列,返回一个标识符(消息队列ID)。

  • 发送消息:使用msgsnd系统调用向消息队列发送消息。

  • 接收消息:使用msgrcv系统调用从消息队列接收消息。

  • 删除消息队列:使用msgctl系统调用删除不再需要的消息队列。

用法

  1. 使用 msgget() 创建或获取消息队列。
  2. 使用 msgsnd() 发送消息,msgrcv() 接收消息。
  3. 消息队列通过键值(key)来标识,且在内核中驻留。

示例代码:

本次的示例代码为单进程,可以分为两个文件,实现多进程的消息队列通讯,学员们可以自己动手敲一下感受一下。

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
} message;

int main() {
    key_t key = ftok("progfile", 65); // 生成消息队列键
    int msgid = msgget(key, 0666 | IPC_CREAT); // 创建消息队列

    // 向消息队列发送消息
    message.msg_type = 1;
    strcpy(message.msg_text, "Hello from sender!");
    msgsnd(msgid, &message, sizeof(message), 0);

    printf("Sent message: %s\n", message.msg_text);

    // 接收消息
    msgrcv(msgid, &message, sizeof(message), 1, 0);
    printf("Received message: %s\n", message.msg_text);

    msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
    return 0;
}

运行结果
在这里插入图片描述

ipcs -q 拓展

在Linux系统中,可以使用ipcs命令来查看系统中的消息队列信息。ipcs命令可以列出系统中当前存在的进程间通信(IPC)对象,包括消息队列、信号量和共享内存。

信号量和共享内存我们以后会介绍到。

ipcs -q

这将列出系统中所有的消息队列,包括它们的标识符(ID)、拥有者、权限、大小等信息。您可以通过这些信息了解系统中消息队列的使用情况。
在这里插入图片描述
如果在上述代码中不删除则会
在这里插入图片描述

ipcrm 拓展

在Linux系统中,您可以使用ipcrm命令手动删除消息队列。ipcrm命令用于删除System V IPC 对象(包括消息队列、信号量和共享内存)。

要手动删除消息队列,您需要知道消息队列的标识符(ID)。首先,您可以使用ipcs -q命令列出系统中的消息队列及其ID,然后选择要删除的消息队列的ID。

ipcrm -q <queue_id>

其中,<queue_id> 是要删除的消息队列的标识符(ID)。

例如,如果要删除标识符为12345的消息队列,您可以运行以下命令:

ipcrm -q 12345

比如我要手动删除我刚刚没有删除的的消息队列。
在这里插入图片描述

注意事项

  1. 消息格式一致性:发送和接收进程之间必须约定好消息的格式,包括消息的大小、结构和编码方式。确保发送的消息可以被接收进程正确解析和处理。

  2. 消息队列容量:消息队列有容量限制,当消息队列满时,发送进程可能被阻塞或消息被丢弃。需要根据实际需求设置合适的消息队列容量,避免消息丢失或系统阻塞。

  3. 进程同步:在使用消息队列进行通信时,需要考虑进程之间的同步和互斥,避免出现竞争条件和数据不一致的情况。可以使用信号量等机制来实现进程间的同步。


结论

消息队列通常用于进程间通信,特别是在需要解耦发送者和接收者、实现异步通信的情况下。

通过学习消息队列,希望学员们将能够更好地理解并应用进程间通信的技术,为构建复杂的软件系统打下坚实的基础。

;