linux进程间通信之消息队列

摘要

        本文旨在深入探讨Linux进程间通信中的消息队列机制,包括其工作原理系统调用接口以及实际应用场景。通过理论分析和示例代码的解读,本文将帮助读者更好地理解消息队列在多进程环境中的作用和应用。

一、引言

        在Linux操作系统中,进程间通信(IPC)是一种常见的需求。为了实现进程间的同步协调,Linux提供了多种IPC机制,包括管道、消息队列、共享内存和信号量等。其中,消息队列是一种灵活高效的进程间通信方式,它允许进程之间发送和接收消息。本文将重点介绍消息队列的工作原理、系统调用接口以及一个简单的示例代码。

二、消息队列工作原理

        消息队列是一种在进程之间传递消息的数据结构。它由一系列消息组成,每个消息都有一个特定的优先级内容。当一个进程需要发送消息时,它可以将消息添加到队列中。接收进程可以从队列中获取消息并根据其优先级进行处理。 

linux进程间通信之消息队列_第1张图片

        这些 IPC 对象存在于内核空间,应用层使用 IPC 通信的步骤为:

linux进程间通信之消息队列_第2张图片

三.系统调用接口

1.mq_open

  用于创建或打开一个消息队列。它接受四个参数:消息队列的名称、打开方式、消息的属性和一个指向mq_attr结构体的指针。

2.mq_send

  用于向消息队列发送消息。它接受三个参数:消息队列的描述符、要发送的消息和消息的优先级。

3.mq_receive

  用于从消息队列接收消息。它接受三个参数:消息队列的描述符、存放接收消息的缓冲区和消息的最大长度。

4.mq_close

  用于关闭一个消息队列。它接受一个参数:消息队列的描述符。

5.mq_unlink

  用于删除一个消息队列。它接受一个参数:消息队列的名称。

四.消息队列的特点

1. 发出的消息以链表形式存储,相当于一个列表,进程可以根据 id 向对应的“列表”增加和获取消息。
2. 进程接收数据时可以按照类型从队列中获取数据。
消息队列的使用步骤:
1. 创建 key
2. msgget() 通过 key 创建(或打开)消息队列对象 id
3. 使用 msgsnd()/msgrcv() 进行收发;
4. 通过 msgctl() 删除 ipc 对象

linux进程间通信之消息队列_第3张图片 

五.代码

#include   
#include   
#include   
#include   
#include   
#include   
#include   
  
#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函数删除该对象。需要注意的是,只有当队列中没有消息时才能删除对象。

        需要注意的是,实际应用中可能需要更多的错误处理和异常情况处理。同时,还需要注意消息队列的使用方式和限制,例如消息的最大长度和优先级等参数需要根据实际需求进行设置。

你可能感兴趣的:(linux,运维,服务器,c语言,驱动开发)