消息队列

  • 消息队列亦称报文队列,也叫做信箱。是Linux的一种通信机制,这种通信机制传递的数据具有某种结构,而不是简单的字节流。
  • 消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了一个数据结构
  • 向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;从消息队列汇总读数据,实际上是从这个数据结构中删除一个结点
  • 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
  • 消息队列也有管道一样的不足,就是每个数据块的最大长度是有上限的,系统上全体队列的最大总长度也有一个上限

创建消息队列

API

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

参数

  • key: 某个消息队列的名字

0(IPC_PRIVATE):会建立新的消息队列
大于0的32位整数:视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值

  • msgflg:由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的,这里举两个来说明

IPC_CREAT
如果消息队列对象不存在,则创建之,否则则进行打开操作
IPC_EXCL
如果消息对象存在则打开,否则出现 No such file or directory
msgflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限

返回值

  • 成功msgget将返回一个非负整数,即该消息队列的标识码;
  • 失败则返回“-1”

key = IPC_PRIVATE时

使用IPC_PRIVATE创建的IPC对象, key属性为0,和IPC对象的编号就没有了对应关系。这样毫无关系的进程,就不能通过key值来得到IPC对象的编号(因为这种方式创建的IPC对象的key值都是0)。因此,这种方式产生的IPC对象,和无名管道类似,不能用于毫无关系的进程间通信。但也不是一点用处都没有,仍然可以用于有亲缘关系的进程间通信

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>

int main(void)
{
    
    int msgid;
    if((msgid = msgget(IPC_PRIVATE,IPC_CREAT| IPC_EXCL| 0666)) == -1){
        perror("msgget error");
    }

    printf("msgget success msgid %d\n",msgid);
    return 0;
}

在这里插入图片描述

错误代码

EACCES:指定的消息队列已存在,但调用进程没有权限访问它
EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志
ENOMEM:需要建立消息队列,但内存不足
ENOSPC:需要建立消息队列,但已达到系统的限制

添加消息队列信息

API

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数

  • msgid: 由msgget函数返回的消息队列标识码
  • msgp:是一个指针,指针指向准备发送的消息,
  • msgsz:是msgp指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
  • msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
  • 0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
  • IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回 EAGAIN 错误
  • IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。

消息队列的数据格式:

struct Msg{
    long type; // 消息类型。这个是必须的,而且值必须 > 0,这个值被系统使用
    // 消息正文,多少字节随你而定
    // ...
};

返回值:

  • 成功: 返回0
  • 失败: 返回-1

错误代码

EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满
EIDRM:标识符为msqid的消息队列已被删除
EACCES:无权限写入消息队列
EFAULT:参数msgp指向无效的内存地址
EINTR:队列已满而处于等待情况下被信号中断
EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0

msgsnd()解除阻塞

msgsnd()解除阻塞的条件有以下三个条件:

① 不满足消息队列满个数满两个条件,即消息队列中有容纳该消息的空间。
② msqid代表的消息队列被删除。
③ 调用msgsnd函数的进程被信号中断。

从消息队列中读取消息

#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);

参数

  • msgid: 由msgget函数返回的消息队列标识码
  • msgp: 是一个指针,指针指向准备接收的消息,
  • msgsz: 是msg_ptr指向的消息长度,消息缓冲区结构体中mtext的大小,不包括数据的类型
    msgtype: 它可以实现接收优先级的简单形式
    msgtype = 0 返回队列第一条信息
    msgtype > 0 返回队列第一条类型等于msgtype的消息 
    msgtype < 0 返回队列第一条类型小于等于msgtype绝对值的消息
  • msgflg(可以用|符号): 控制着队列中没有相应类型的消息可供接收时将要发生的事
  • msgflg = 0,表示没有消息就阻塞待.
  • msgflg = IPC_NOWAIT,如果指定类型的消息不存在就立即返回,同时设置 errno 为 ENOMSG。
  • msgflg = MSG_NOERROR,消息大小超过msgsz时被截断
  • msgflg = MSG_EXCEPT:仅用于 msgtyp > 0 的情况。表示获取类型不为 msgtyp 的消息

返回值

  • 成功:返回实际放到接收缓冲区里去的字符个数
  • 失败: 返回-1

错误代码

E2BIG:消息数据长度大于msgsz而msgflag没有设置MSG_NOERROR
EIDRM:标识符为msqid的消息队列已被删除
EACCES:无权限读取该消息队列
EFAULT:参数msgp指向无效的内存地址
ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
EINTR:等待读取队列内的消息情况下被信号中断

msgrcv()解除阻塞的条件

① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。

消息队列的控制函数

API

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

参数

  • msqid: 由msgget函数返回的消息队列标识码
  • cmd :是将要采取的动作,(有三个可取值)分别如下
命令 说明
IPC_STAT 设定 消息队列的属性
IPC_SET 读取消息队列的属性
IPC_RMID 删除消息队列,buf设置为NULL

注意:若选择删除队列,第三个参数传NULL

msqid_ds 结构体

 struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */
               time_t          msg_stime;    /* Time of last msgsnd(2) */
               time_t          msg_rtime;    /* Time of last msgrcv(2) */
               time_t          msg_ctime;    /* Time of last change */
               unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };
 
 struct ipc_perm {
               key_t          __key;       /* Key supplied to msgget(2) */
               uid_t          uid;         /* Effective UID of owner */
               gid_t          gid;         /* Effective GID of owner */
               uid_t          cuid;        /* Effective UID of creator */
               gid_t          cgid;        /* Effective GID of creator */
               unsigned short mode;        /* Permissions */
               unsigned short __seq;       /* Sequence number */
           };

返回值:

如果操作成功,返回“0”;如果失败,则返回“-1”

错误代码

EACCES:参数cmd为IPC_STAT,确无权限读取该消息队列
EFAULT:参数buf指向无效的内存地址
EIDRM:标识符为msqid的消息队列已被删除
EINVAL:无效的参数cmd或msqid
EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行

查看消息队列限制

可以使用ipcs -l -q 查看消息队列限制
请添加图片描述

max queues system wide = 3200 : 系统最多的消息队列数量(最多支持同时3200个消息队列)
max size of message (bytes) = 8192 : 单个消息的最大字节数
default max size of queue (bytes) = 16384 : 默认的单个队列的大小16384

查看消息队列使用情况

ipcs -u -q

在这里插入图片描述