• Linux 进程信息 system V-IPC 之消息队列


    消息队列 (MSG)

    忆前面所述的管道,这种通信机制的一个弊端是:你无法在管道中读取一个“指定” 的数据,尽管使用有名管道时信息之间不会相互踩踏,但是在多个进程同时往同一个管道发送数据的情况下,无法直接提取自己想要的某个进程发的数据,因为这些数据没有做任何标记,读者进程只能按次序地挨个读取,因此多对进程间的相互通信,除非使用多条管道分别处理,否则无法使用一条管道来完成。

    消息队列的特点

    消息队列提供一种带有数据标识的特殊管道,使得每一段被写入的数据都变成带标识 消息,读取该段消息的进程要指定这个标识就可以正确地读取,而不会受到其他消息的干 扰,从运行效果来看,一个带标识的消息队列,就像多条并存的管道一样。

    消息队列的使用方法一般是:

    1 ,发送者:

    A)  获取消息队列的 ID

    B)  将数据放入一个附带有标识的特殊的结构体,发送给消息队列。

     2 ,接收者:

    A)  获取消息队列的 ID

    B)  将指定标识的消息读出。

    当发送者和接收者都不再使用消息队列时,及时删除它以释放系统资源。 面详细解剖消息队列 (MSG) 的 API

    取消息队列的 ID

    文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    int msgget(key_t key, int msgflg);

    参数

    key

    消息队列的键值

    msgflg

    IPC CREAT

    _

    如果 key 对应的 MSG 不存在,则创建该对象

    IPC  EXCL

    _

    果该 key 对应的 MSG 已经存在,则报错

    mode

    MSG 的访问权 (八进制,如 0644)

    回值

    成功

    息队列的 ID

    - 1

     key 指定为为 IPC_PRIVATE,则会自动产生一个随机未用的新键值

     函数 msgget()的接口规范

    使用该函数需要注意的以下几点:

    1,选项 msgflg 是一个位屏蔽字,因此 IPC_CREATIPC_EXCL 和权限 mode 可以用位 或的方式叠加起来,比如:msgget(key, IPC_CREAT | 0666); 表示如果 key 对应的消息队 列不存在就创,且权限指定为 0666,若已存在则直接获取 ID

    2,权限只有读和写,执行权限是无效的,例如 0777 0666 是等价的。

    3,当 key 被指定为 IPC_PRIVATE 时,系统会自动产生一个未用的 key 来对应一个新的 消息队列对象。一般用于线程间通信。

    发送、接收消息

    文件

    #include ys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    int msgsnd(int msqid, const void *msgp,   size_t msgsz, intmsgflg); ssize_t msgrcv(int msqid, void *msgp,size_t msgsz, long msgtyp, int msgflg);

    参数

    msqid

    送、接收消息的消息队列 ID

    msgp

    要发送的数据、要接收的数据的存储区域指针

    msgsz

    要发送的数据、要接收的数据的大小

    msgtyp

    这是 msgrcv 独有的参数,代表要接收的消息的标识

    msgflg

    IPC  NOWAIT

    _

    非阻塞读出、写入消息

    MSG  EXCEPT

    _

    读取标识不等于 msgtyp 的第一个消息

    MSG  NOERROR

    _

    消息寸比 msgsz 大时,截断消息而不报错

    回值

    成功

    msgsnd( )

    0

    msgrcv( )

    真正读取的字节数

    - 1

    使用这两个收、发消息函数需要注意以下几点:

    1,发送消息时,消息必须被组织成以下形式:

    struct msgbuf

    {

    long mtype;  // 消息的标识

    char mtext[1];    // 消息的正文

    };

    就是说:

    发送出去的消息必须以一个 long 型数据打头,作为该消息的标识,后面的数据则没有要求

    2,消息标识可以是任意长整型数值,但不能是 0L

    3,参数 msgsz 是消息中正文的大小,不包含消息的标识。

    设置或者获取消息队列的相关属性

    文件

    #include ys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    int msgctl(int msqid, int cmd, struct msqid_ds *buf);

    参数

    msqid

    息队列 ID

    cmd

    IPC  STAT

    _

    获取该 MSG 的信息,储存在结构体 msqid_ds 

    IPC SET

    _

    设置该 MSG 的信息,储存在结构体 msqid_ds

    IPC  RMID

    _

    即删除该MSG,并且唤醒所有阻塞在该 MSG 上的进程, 时忽略第三个参数

    IPC INFO

    _

    获得关于当前系统中 MSG 的限制值信息

    MSG INFO

    _

    获得于当前系统中 MSG 的相关资源消耗信息

    MSG STAT

    _

    IPC_STAT,但 msgid 为该消息队列在内核中记录所有 队列信息的数组的下标,因此通过迭代所有的下标 可以获得系统中所有消息队列的相关信息

    buf

    相关信息结构体缓冲区

    回值

    成功

    IPC  STAT

    _

    0

    IPC  SET

    _

    IPC  RMID

    _

    IPC  INFO

    _

    内核中记录所有消息队列信息的数组的下标最大值

    MSG  INFO

    _

    MSG  STAT

    _

    消息队列的 ID

    - 1

    示例代码

    创建一个消息队列

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct msgbuf {
    6. long mtype; /* message type, must be > 0 */
    7. char mtext[128];//mtext[0] /* message data */
    8. };
    9. int main()
    10. {
    11. //创建KEY键值
    12. key_t key = ftok("./", 1);
    13. //创建消息队列
    14. int msgid = msgget(key, IPC_CREAT | 0777);//open('',O_CREAT|O_)
    15. if(-1 == msgid)
    16. {
    17. perror("creat msg failed");
    18. return -1;
    19. }
    20. struct msgbuf buf;
    21. buf.mtype = 1;
    22. while(1)
    23. {
    24. fgets(buf.mtext,128,stdin);
    25. msgsnd(msgid, &buf, sizeof(buf), 0);
    26. }
    27. }

    创建接收队列 

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct msgbuf {
    6. long mtype; /* message type, must be > 0 */
    7. char mtext[128];//mtext[0] /* message data */
    8. };
    9. int main()
    10. {
    11. //创建KEY键值
    12. key_t key = ftok("./", 1);
    13. //创建消息队列
    14. int msgid = msgget(key, IPC_CREAT | 0777);//open('',O_CREAT|O_)
    15. if(-1 == msgid)
    16. {
    17. perror("creat msg failed");
    18. return -1;
    19. }
    20. struct msgbuf msg;
    21. while(1)
    22. {
    23. msgrcv(msgid, &msg, sizeof(msg), 1, 0);
    24. printf("%s\n",msg.mtext);
    25. }
    26. }

    消息队列总结

    消息队列使用简单,老少咸宜,但是他跟管道一样,都是需要“代理人”的进程通信机

    制:内核充当了这个代人,内核为使用者分配内存,检查边界,设置阻塞,以及各种权限

    监控,使得我们用起来常省心省力,但是任何事情都是有代价的:代理人机制使得他们的 效率都不高,因为两个进程的数据传递并不是直接了当的,而是要经过内核的辗转接力的, 因而他们都不适合用来传输海量数据。

    而能解决这个问题的,就是下面要解剖的共享内存,欲知后事如何,且听下节分解。

  • 相关阅读:
    云栖大会丨桑文锋:打造云原生数字化客户经营引擎
    Linux下安装Mysql5.7,超详细完整教程,以及云mysql连接
    PVE虚拟机下如何安装自定义备份Win系统镜像文件
    更加高效的为新项目添加 eslint 和 prettier
    译:零信任对 Kubernetes 意味着什么
    C语言——冒泡排序
    自动化测试框架(pytest)&附学习视频
    Ubuntu 22.04 开机后在登陆界面循环问题解决
    redis(主从复制、哨兵模式、集群模式)
    深度解析Sora的核心技术
  • 原文地址:https://blog.csdn.net/qq_66337990/article/details/132631897