Loading

Posix消息队列

程序示例可见参考链接:Posix消息队列_ZX714311728的博客-CSDN博客

消息队列

消息队列可以认为是一个消息链表,消息队列是随内核持续的。队列中每个消息的属性有:

  1. 一个无符号整数优先级(Posix)或一个长整数类型(System V);

  2. 消息的数据部分长度(可以为0);

  3. 数据本身(如果长度大于0)。

链表头含有当前队列的两个属性:

  1. 队列中运行的最大消息数 mq_maxmsg
  2. 每个消息的最大大小 mq_msgsize

消息队列的可能布局如下:

Posix 消息队列与System V消息队列主要区别:

  1. Posix 消息队列的读总是返回「最高优先级的最早消息」;System V 消息队列的读则可以返回任一指定优先级消息。
  2. 往空队列放置消息时,Posix 消息队列允许产生一个信号或启动一个线程;System V则不提供类似机制。

Posix消息队列与管道或FIFO的主要区别:

  1. 在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。对管道和FIFO来说,除非读出者已存在,否则先有写入者是没有意义的。

  2. 管道和FIFO是字节流模型,没有消息边界。消息队列则指定了数据长度,有边界。

mq_xxx() 函数不是标准库函数,所以链接时需指定库,通过在最后加上 -lrt 选项来指定

mq_open

// 「创建」新消息队列或「打开」已存在的消息队列
mqd_t mq_open(const char *name, int oflag, .../*mode_t mode, struct mq_attr *attr*/);

返回值:成功,消息队列描述符; 出错,-1

mode 和 attr 仅创建时使用

创建的 “文件” 存放在 /dev/mqueue/

name 规则:必须以一个/ 开头,并且不能再包含任何其他斜杠符;

oflag :O_RDONLY、O_WRONLY、O_RDWR三者之一,按位或上O_CREAT、O_EXCL

mode :S_ISRUSR、S_ISWUSR、S_ISRGRP、S_ISWGRP、S_ISROTH、S_ISWOTH

attr :mq_open 只能设置 mq_maxmsg 和 mq_msgsize 属性,并且两个必须要同时设置;

struct mq_attr {
	long mq_flags;// 阻塞标志, 0或O_NONBLOCK
	long mq_maxmsg;// 最大消息数
	long mq_msgsize;// 每个消息最大大小
	long mq_curmsgs;// 当前消息数
};
// 关闭已打开的消息队列
int mq_close(mqd_t mqd);

返回值:成功,0;出错,-1

close 函数类似:调用进程可以不再使用该描述符,但消息队列并不从系统中删除。

一个进程终止时,它的所有打开的消息队列都关闭,如同调用了mq_close。

// 删除消息队列的name
int mq_unlink(const char *name);

返回值:成功,0;出错,-1

每个消息队列有一个保存其当前打开的描述符数的引用计数,只有当引用计数为0时,才删除该消息队列。当一个消息队列的引用计数仍大于0时,其 name 就能删除,但是该队列的析构(这与从系统中删除其名字不同)要等到最后一个 mq_close 发生时才进行。

mq_getattr、 mq_setattr

//获取、设置消息队列属性
int mq_getattr(mqd_t mqd, struct mq_attr *attr);
int mq_setattr(mqd_t mqd, const struct mq_attr *attr, struct mq_attr *oattr);

返回值:成功,0;出错,-1

mq_setattr 只能设置 mq_flags 属性;mq_getattr 返回全部4个属性。

mq_send、 mq_receive

// 往消息队列中放置一个消息
int mq_send(mqd_t mqd, const char *ptr, size_t len, unsigned int prio);

//从消息队列中取走一个消息
ssize_t mq_receive(mqd_t mqd, char *ptr, size_t len, unsigned int *prio);

说明:

  • mq_send 中优先级 prio 要小于 MQ_PRIO_MAX
  • mq_send 的 prio 参数:
    • 为非空指针,优先级通过该指针存放;
    • 为空指针,指定优先级值为 0 。
  • mq_receive 总是返回指定队列中 最高优先级的最早消息;
  • mq_receive 的 len 参数值不能小于能加到所指队列中消息的最大大小(mq_msgsize)。若小于该值则立即返回 EMSGSIZE 错误。

消息队列限制

  • mq_maxmsg:消息队列中最大消息数
  • mq_msgsize:给定消息队列的最大字节数
  • MQ_OPEN_MAX:一个进程能够同时拥有的打开着的消息队列的最大数目(Posix要求它至少为8)
  • MQ_PRIO_MAX:任意消息的最大优先级值加1

mq_notify

Posix消息队列允许异步事件通知,以告知何时有一个消息放置到了某个空消息队列中。这种通知有两种方式可供选择:

  1. 产生一个信号
  2. 创建一个线程来执行指定的函数
// 为指定队列建立或删除异步事件通知
int mq_notify(mqd_t mqd, const struct sigevent *notification);

返回值:成功,0;出错-1

  • notification 参数:
    • 非空,该进程被注册为接收该消息队列的通知
    • 空指针,当前进程被注册为接收该消息队列的通知,已存在的注册将被撤销
  • 对一个消息队列来说,任一时刻只有一个进程可以被注册
  • 当通知被发送给注册进程时,注册即被撤销,该进程若要重新注册,则必须重新调用 mq_notify() (一次注册,接收一次)
union sigval {          /* Data passed with notification */
    int sival_int;         /* Integer value */
    void *sival_ptr;         /* Pointer value */
};

struct sigevent {
    int sigev_notify; /* Notification method */
    int sigev_signo;  /* Notification signal */
    union sigval sigev_value;  /* Data passed with
                                         notification */
    void (*sigev_notify_function) (union sigval);  /* Function used for thread notification (SIGEV_THREAD) */
    void *sigev_notify_attributes;   /* Attributes for notification thread (SIGEV_THREAD) */
    pid_t sigev_notify_thread_id;   /* ID of thread to signal (SIGEV_THREAD_ID) */
};
  • sigev_notify 取值:
    • SIGEV_NONE:事件发生时,什么也不做;
    • SIGEV_SIGNAL:事件发生时,将sigev_signo指定的信号发送给指定的进程;
    • SIGEV_THREAD:事件发生时,内核会(在此进程内)以 sigev_notify_attributes 为线程属性创建一个线程,并让其执行 sigev_notify_function,并以 sigev_value 为其参数
  • sigev_signo:在 sigev_notify=SIGEV_SIGNAL 时使用,指定信号类别
  • sigev_value:sigev_notify=SIGEV_SIGEV_THREAD 时使用,作为 sigev_notify_function 的参数
  • sigev_notify_function:在 sigev_notify=SIGEV_THREAD 时使用,其他情况下置NULL

Posix实时信号

信号可划分为两个大组:

  1. 值在 SIGRTMIN(值34) 和 SIGRTMAX(值64) 之间(包括两者在内)的实时信号
  2. 所有其他信号:SIGALRM、SIGINT、SIGKILL等

实时信号中 “实时” 的意思是:

  1. 信号是排队的,是按先进先出(FIFO)顺序排队的。同一信号产生3次,则递交3次
  2. 当有多个 SIGRTMIN 到 SIGRTMAX 范围内的解阻塞信号排队时,值较小的信号先与较大的信号递交
  3. 实时信号能比非实时信号携带更多信息
posted @ 2021-02-06 16:52  JakeLin  阅读(208)  评论(0编辑  收藏  举报