System V 消息队列概念以及相关函数(msgget、msgsnd、msgrcv、msgctl)介绍

System V 消息队列

  • 消息队列是半双工的通信方式
1.1 创建一个消息队列
  • 消息队列的特点:消息只能一条的读取,不能多读取,也不能少读取,每条消息有一个类型,可以按照消息的类型读取

  • 创建或者打开一条消息:

    /*************************
    函数功能:创建或者打开一条消息队列
    返回值: If successful, the return value will be the message queue identifier (a
           nonnegative integer), otherwise -1 with errno indicating the error.
    *************************/
    #include <sys/types.h>
    #include <sys/ipc.h>
    include <sys/msg.h>
    int msgget(key_t key, int msgflg);
    /*参数
    key:键值,用来生成标识符
    msgflag:可以指定IPC_CREATE, IPC_EXCL等标记用 | 符号连接起来
    */
    
    1. 通过key值与指定的键值对应的队列匹配,如果匹配成功,返回既有队列的标识符,如果指定了IPC_CREATE, IPC_EXCL等标记,返回相关错误,如果没有匹配成功,但是指定了IPC_CREATE标记,创建一个新的消息队列对象
1.2 交换消息
  • 消息的常规形式

    struct mymsg
    {
      	long mtype;       /*Message type*/
      	char mtext;       /*Message body*/
    }
    /*这个结构可以由程序员自己决定,只是一般情况下的消息结构体是这样*/
    
  • 发送消息

    /*****************************
    函数功能:发送一条消息
    返回值: On failure both functions return -1 with errno  indicating  the  error,
            otherwise  msgsnd()  returns 0 and msgrcv() returns the number of bytes
            actually copied into the mtext array.
    *****************************/
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    /*参数:
    msqid:消息队列的标识符
    msgp:指向一个消息结构
    msgsz:消息结构中消息本体的大小
    msgflg:位掩码的组合
    		IPC_NOWAIT:执行一个非阻塞的发送操作,通常消息队列满的时候,msgsnd调用会阻塞,如果设置了这个标记,不会阻塞,会直接返回一个EINTR错误,并且该系统调用被打断之后,不会自动重启,就算设置了SA_RESETART标记
    */
    
  • 接受消息

    /*****************************
    函数功能:接受一条消息
    返回值: On failure both functions return -1 with errno  indicating  the  error,
            otherwise  msgsnd()  returns 0 and msgrcv() returns the number of bytes
            actually copied into the mtext array.
    *****************************/
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                          int msgflg);
    /*参数:
    msqid:消息队列的标识符
    msgp:指向一个存放消息结构的缓冲区
    msgsz:指定了msgp指向的mtext字段(消息本体)最大能够容纳的大小,如果队列中消息大小超过这个字段,就不会从队列中读取消息,而是返回E2BIG错误
    msgtyp:消息的读取并非是先进先出,而是按照msgtype的类型进行选择性的读取
    1:msgtyp==0:删除队列中第一条消息,并且返回给调用进程
    2:msgtyp>0:将队列中第一个mtype等于msgtyp的消息删除并将其返回给调用进程,其它不相等不管,让它还是存在于队列中
    3:msgtyp<0:将等待队列中mtype值最小并且其值小于或者等于msgtyp的绝对值的第一条消息会被删除返回给调用进程
    msgflg:
    IPC_NOWAIT:执行一个非阻塞操作,没有匹配的消息类型,返回ENOMSG错误
    MSG_EXCEPT:当msgtyp>0时候,将队列中mtype不等于msgtyp的消息强制删除并且返回给调用者,这个标记linux特有的,需要定义_GNU_SOURCE之后才可以,等于msgtyp的消息忽略,让其存在于队列中
    MSG_NOERROR:如果消息中mtext字段大于msgsz,会截断mtext字段的数据大小为msgsz返回给调用者,截断剩余的数据会丢失,没有这个标志的话是不会发生截断的,而是直接报错
    */
    
1.3 消息队列控制操作
  • 函数功能:在标识符msqid的消息队列上执行控制操作

  • 返回值:Return 0 On success,or -1 on error

  • 函数原型:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    
  • 参数:

    1. msqid:消息队列的标识符
    2. cmd:在消息队列上操作的相关命令
      1. IPC_RMID:立即删除消息队列对象及其关联的msqid_ds数据结构,队列中所有消息会丢失,所有被阻塞的读者和写者进程立即醒来,msgsnd和msgrcv会失败返回EIDRM,这个操作会忽略第三个参数
      2. IPC_STAT:将这个消息队列关联的msgid_ds数据结构的副本保存在buf指向的缓冲区
      3. IPC_SET:将buf指向的缓冲区提供的值更新这个消息队列关联的msqid_ds数据结构中的被选中的字段
1.4 消息队列关联数据结构
  • 每个icp对象的关联数据结构中共有的一部分:

    在这里插入图片描述

    1. 该部分的讲解在第45章中已经完成,这里主要对消息队列关联数据结构独有的字段进行分析
  • 消息队列的关联数据结构

在这里插入图片描述

  1. msgqnum、msglen_t属于无符号整型
  2. msg_stime:队列创建之后,初始化为0,每次msgsnd调用会将这个字段设置为当前时间,新纪元到现在的秒数
  3. msg_stime:队列创建之后,初始化为0,每次msgsrcv调用会将这个字段设置为当前时间,新纪元到现在的秒数
  4. msg_ctime:队列创建之后,初始化为当前时间,每次IPC_SET操作会将这个字段设置为当前时间,新纪元到现在的秒数
  5. __msg_cbytes:队列创建之后,初始化为0,后续每个msgsnd或者msgsrcv调用都会把字段更新为队列中所有mtext字段包含的字节的总和
  6. msg_qnum:队列创建之后,初始化为0,后续每个msgsnd调用递增这个字段,后续每个msgrcv调用递减这个字段,以反映出队列中消息的总数
  7. msg_qbytes:这个字段为队列中所有消息的mtext字段包含的字节的总和设置了一个上限,队列创建之后,初始化为MSGMNB,特权进程可以使用IPC_SET操作更改该字段的值在0到INT_MAX之间,也可以在/proc/sys/kernel/msgmnb文件中做修改,以保证后续队列创建初始值以及非特权进程所能进行的修改
  8. msg_lspid:队列创建之后,初始化为0,后续每个msgsnd调用,会把该字段的值设置为调用进程的pid
  9. msg_lrpid:队列创建之后,初始化为0,后续每个msgrcv调用,会把该字段的值设置为调用进程的pid
1.5 消息队列的限制
  • 大多数UNIX实现会对System V消息队列操作施加各种各样的限制,比如系统级创建消息队列的数量、单条消息可以写入的字节数、一个消息队列一次最多可以保存的字节数、系统中消息队列能存放的消息总数,linux下可以在/proc/sys/kernel里面查看并且修改这些限制 ,具体可以查阅书籍本章节

在这里插入图片描述

1.6 使用消息队列实现客户端-服务器应用程序
  • 服务器和客户端之间可以采用一个消息队列,但是这里会存在一些问题:
    1. 注意区分消息类型,这里一般用进程ID作为消息类型
    2. 多个并行的写入可能会造成死锁等问题
  • 所以我们这里最好采用多个消息队列,每一个客户端一个,每一个服务器一个,但死锁的问题还是需要自己解决
1.7 使用System V 消息队列的缺点:
  • 优点:可以根据消息类型选择性的读取队列中的消息
  • 缺点:
    1. 消息队列的引用采用标识符,而不是文件描述符
    2. 使用键而不是文件名来标识消息队列会增加额外的程序设计复杂性
    3. 内核不会对使用消息队列的进程引用计数
    4. 消息队列有很多种限制
  • 总体上来讲最好避免使用System V 消息队列,应该尽量考虑其它方案,比如POSIX 消息队列,更加有难度的是基于多文件描述符的通信信道
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值