• Linux系统编程系列之进程间通信-消息队列


     Linux系统编程系列(16篇管饱,吃货都投降了!)

            1、Linux系统编程系列之进程基础

            2、Linux系统编程系列之进程间通信(IPC)-信号

            3、Linux系统编程系列之进程间通信(IPC)-管道

            4、Linux系统编程系列之进程间通信-IPC对象

            5、Linux系统编程系列之进程间通信-消息队列

            6、Linux系统编程系列之进程间通信-共享内存

            7、Linux系统编程系列之进程间通信-信号量组

            8、Linux系统编程系列之守护进程

            9、Linux系统编程系列之线程

            10、Linux系统编程系列之线程属性 

            11、Linux系统编程系列之互斥锁和读写锁

            12、Linux系统编程系列之线程的信号处理

            13、Linux系统编程系列之POSIX信号量

            14、Linux系统编程系列之条件变量

            15、Linux系统编程系列之死锁

            16、 Linux系统编程系列之线程池

    一、什么是消息队列

            消息队列是system-V三种IPC对象之一,是进程间通信的一种方式。

    二、消息队列的特性

            允许发送的数据携带类型(指定发送给谁),具有相同类型的数据在消息队列内部排队,读取的时候也要指定类型,然后依次读出数据。可以理解为消息队列就是一个多管道集合

    三、消息使用场景

            由于每个消息都携带有类型,相同的类型自成一队,因此读取方向可以根据类型来挑选不同的队列,所以说MSG适用于“多对一”的场景。

            (1)系统日志。

            (2)多个不同的,不相关的进程向同一管道输入数据。

    四、函数API接口

            1、指定消息队列功能

    1. // 指定消息队列功能
    2. int msgget(key_t key, int msgflg);
    3. // 接口说明:
    4. 返回值:消息队列MSG对象ID
    5. 参数key:键值,全局唯一标识,可由ftok()产生
    6. 参数msgflg:操作模式与读写权限,与文件操作函数open类似。

             2、向MSG对象发送消息

    1. // 向MSG对象发送消息
    2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    3. // 接口说明
    4. msggid:MSG对象的ID,由msgget()获取
    5. msgp:一个指向等待被发送的消息的指针,由于MSG中的消息最大的特点是必须有一个整数标识,用以区分MSG中的不同的消息,因此MSG的消息会使用一个特别的结构体来表达:
    6. struct msgbuf
    7. {
    8. long mtype; // 消息类型固定,必须大于0
    9. char text[100]; // 消息正文(可变,自己定义)
    10. };
    11. msgsz:消息正文的长度(单位字节),注意不含类型长度
    12. msgflg:发送选项
    13. 0:默认发送模式,在MSG缓冲区已满的情形下阻塞,直到缓冲区变为可用状态
    14. IPC_NOWAIT:非阻塞发送模式,在MSF缓冲区已满的情形下直接退出函数并设置错误码为EAGAIN

            3、从MSG对象接收消息

    1. // 从MSG对象接收消息
    2. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    3. // 接口说明
    4. msgqid:MSG对象ID,由msgget()获取
    5. msgp:存放消息的内存入口
    6. msgsz:存放消息的内存大小
    7. msgtyp:欲接收消息的类型
    8. 0:不区分类型,自己读取MSG中的第一个消息
    9. 大于0:读取类型为指定msgtyp的第一个消息(若msgfla被配置了MSG_EXCEPT则读取除了类型msgtyp的第一个消息)
    10. 小于0:读取类型小于等于msgtyp绝对值的第一个具有最小类型的消息。
    11. msgflg:接收选项
    12. 0:默认接收模式,在MSG中无指定类型消息时阻塞
    13. IPC_NOWAIT:非阻塞接收模式,在MSG中无指定类型消息时直接退出函数并设置错误码为ENOMSG
    14. MSG_EXCEPT:读取出msgtyp之外的第一个消息
    15. MSG_NOERROR:如果待读取的消息尺寸比msgsz大,直接切割消息并返回msgsz部分,读不了的部分直接丢弃。若没有设置该项,则函数将出错返回并设置错误码为E2BIG

            4、删除消息队列对象

            在前一篇博客IPC对象中有讲,可以使用命令来删除,同时也可以采用函数的方式来删除

    1. // 删除MSG对象
    2. int msgctl(int msqid, int cmd, struct msgqid_ds *buf);
    3. // 接口说明
    4. msqid:MSG对象ID
    5. cmd:控制命令字
    6. IPC_STAT:获取该MSG的信息,储存在结构体msgqid_ds中
    7. IPC_SET: 设置该MSG的信息,储存在结构体msgqid_ds中
    8. IPC_RMID:立即删除该MSG,并且唤醒所有阻塞在该MSG上的进程,同时忽略第三个参数

    五、消息队列使用步骤

            1、使用ftok()获取key值

            2、调用msgget(),指定为消息队列

            3、调用msgsnd()或者msgrcv()来发送或者接收信息。

            4、使用命令或者函数删除消息队列(可选)。

    六、案例

            使用消息队列来实现两个进程间进行相互收发信息

    1. // 消息队列的案例
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. // 编译版本,一个直接编译,另外一个把0x01和0x02位置互换后编译
    9. #define SEND_MTYPE 0x01
    10. #define RECV_MTYPE 0x02
    11. struct msgbuf
    12. {
    13. long mtype; // 消息类型,固定的,必须大于0
    14. char mtext[32]; // 可变的,自己定义
    15. };
    16. int main(int argc, char *argv[])
    17. {
    18. // 1、获得KEY值
    19. key_t msg_key = ftok("./", 1);
    20. if(msg_key == -1)
    21. {
    22. perror("ftok fail");
    23. return -1;
    24. }
    25. // 2、指定消息队列功能
    26. int msg_id = msgget(msg_key, IPC_CREAT | 0666);
    27. if(msg_id == -1)
    28. {
    29. perror("msgget fail");
    30. return -1;
    31. }
    32. // 3、消息队列的读写操作
    33. struct msgbuf Buf = {0};
    34. int ret = 0;
    35. pid_t pid = fork();
    36. // 父进程
    37. if(pid > 0)
    38. {
    39. Buf.mtype = SEND_MTYPE; // 指定消息类型
    40. while(1)
    41. {
    42. printf("please input data:\n");
    43. // fgets("%s", Buf.mtext);
    44. fgets(Buf.mtext, sizeof(Buf.mtext), stdin);
    45. // 发送消息
    46. ret = msgsnd(msg_id, &Buf, sizeof(Buf.mtext), 0);
    47. if(ret == -1)
    48. {
    49. perror("msgsnd fail");
    50. }
    51. else if(ret == 0)
    52. {
    53. printf("send data success: %s\n", Buf.mtext);
    54. }
    55. memset(Buf.mtext, 0, sizeof(Buf.mtext));
    56. }
    57. }
    58. // 子进程
    59. else if(pid == 0)
    60. {
    61. Buf.mtype = RECV_MTYPE; // 指定消息类型
    62. while(1)
    63. {
    64. // 接收消息
    65. ret = msgrcv(msg_id, &Buf, sizeof(Buf.mtext), Buf.mtype, 0);
    66. if(ret > 0)
    67. {
    68. printf("read data success: %s\n", Buf.mtext);
    69. }
    70. }
    71. }
    72. else
    73. {
    74. perror("fork fail");
    75. return -1;
    76. }
    77. return 0;
    78. }

            注:编译时,编译两个版本,一个直接编译,另外一个把两个宏定义的数值互换位置再编译

    七、总结

            消息队列可以理解为一个多管道的集合,通常用于多对一的场景,操作消息队列需要遵循一定的步骤,可以结合案例来加深对消息队列的理解。

  • 相关阅读:
    day18 代码回想录 二叉树05 找树左下角的值&路径总和&从中序与后序遍历序列构造二叉树
    Spark参数配置不合理的情况
    超市的执行者-《软件方法》自测题解析024
    基于ASPNET+SQL的医院在线预约平台系统的设计与开发
    服务器远程管理
    python爬虫
    AtomicBoolean简介说明
    mysql 启动报错 Can t change dir to xxx, No such file or directory 配置错误或挂载导致
    首周聚焦百度智能云千帆大模型平台使用,《大模型应用实践》实训营11月16日开讲!
    redis的事务、锁机制、秒杀
  • 原文地址:https://blog.csdn.net/AABond/article/details/133418776