• linux进程间通信之消息队列


    摘要

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

    一、引言

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

    二、消息队列工作原理

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

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

    三.系统调用接口

    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 对象

     

    五.代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define QUEUE_NAME "/my_queue" // 消息队列名称
    9. #define MAX_MSG_SIZE 1024 // 消息的最大长度
    10. #define MAX_MSG_NUM 10 // 队列中最多存储的消息数
    11. #define PRIORITY 1 // 消息的优先级
    12. int main() {
    13. mqd_t mq; // 消息队列描述符
    14. char buf[MAX_MSG_SIZE]; // 存放接收到的消息的缓冲区
    15. struct mq_attr attr; // 消息队列的属性
    16. pid_t pid; // 子进程ID
    17. int status; // 子进程状态
    18. // 初始化消息队列属性,使用默认值
    19. attr.mq_flags = 0;
    20. attr.mq_maxmsg = MAX_MSG_NUM;
    21. attr.mq_msgsize = MAX_MSG_SIZE;
    22. attr.mq_curmsgs = 0;
    23. attr.mq_perm.uid = getuid(); // 设置拥有者的用户ID
    24. attr.mq_perm.gid = getgid(); // 设置拥有者的组ID
    25. attr.mq_perm.mode = S_IRUSR | S_IWUSR; // 设置访问权限
    26. // 创建或打开一个名为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表示以读写方式打开对象。
    27. mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0664, &attr);
    28. if (mq == (mqd_t)-1) {
    29. perror("mq_open");
    30. exit(EXIT_FAILURE);
    31. }
    32. // 创建子进程并等待其结束
    33. pid = fork();
    34. if (pid == -1) {
    35. perror("fork");
    36. exit(EXIT_FAILURE);
    37. } else if (pid > 0) { // 父进程
    38. // 父进程向消息队列发送消息
    39. if (mq_send(mq, "Hello from parent", MAX_MSG_SIZE, PRIORITY) == -1) {
    40. perror("mq_send");
    41. exit(EXIT_FAILURE);
    42. } else {
    43. printf("Parent process sent message.\n");
    44. }
    45. // 等待子进程结束并获取其状态
    46. waitpid(pid, &status, 0);
    47. printf("Child process exited with status %d.\n", status);
    48. } else { // 子进程
    49. // 子进程从消息队列接收消息并打印出来
    50. if (mq_receive(mq, buf, MAX_MSG_SIZE, NULL) == -1) {
    51. perror("mq_receive");
    52. exit(EXIT_FAILURE);
    53. } else {
    54. printf("Child process received message: %s\n", buf);
    55. }
    56. // 子进程正常结束并释放资源
    57. exit(EXIT_SUCCESS);
    58. }
    59. // 关闭消息队列并删除该对象(当且仅当队列中没有消息时才删除)如果成功删除则返回0;如果删除失败,则返回-1。
    60. // 关闭消息队列并删除该对象(当且仅当队列中没有消息时才删除)如果成功删除则返回0;如果删除失败则返回-1。
    61. if (mq_close(mq) == -1) {
    62. perror("mq_close");
    63. exit(EXIT_FAILURE);
    64. }
    65. if (mq_unlink(QUEUE_NAME) == -1) {
    66. perror("mq_unlink");
    67. exit(EXIT_FAILURE);
    68. }
    69. return 0;
    70. }

    六.代码分析

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

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

  • 相关阅读:
    ubuntu 虚拟机扩容
    2022/8/11
    Spring Session原理解析
    无胁科技-TVD每日漏洞情报-2022-7-27
    .NET Core使用 CancellationToken 取消API请求
    linux下进程的理解(1)
    Linux发展史和Linux系统安装
    ideaSSM在线商务管理系统VS开发mysql数据库web结构java编程计算机网页源码maven项目
    springcloud14:gateway总结
    数据库及程序日常开发命名实践【四期】
  • 原文地址:https://blog.csdn.net/weixin_66634995/article/details/134517744