Posix消息队列
程序示例可见参考链接:Posix消息队列_ZX714311728的博客-CSDN博客
消息队列
消息队列可以认为是一个消息链表,消息队列是随内核持续的。队列中每个消息的属性有:
-
一个无符号整数优先级(Posix)或一个长整数类型(System V);
-
消息的数据部分长度(可以为0);
-
数据本身(如果长度大于0)。
链表头含有当前队列的两个属性:
- 队列中运行的最大消息数
mq_maxmsg
; - 每个消息的最大大小
mq_msgsize
。
消息队列的可能布局如下:
Posix 消息队列与System V消息队列主要区别:
- Posix 消息队列的读总是返回「最高优先级的最早消息」;System V 消息队列的读则可以返回任一指定优先级消息。
- 往空队列放置消息时,Posix 消息队列允许产生一个信号或启动一个线程;System V则不提供类似机制。
Posix消息队列与管道或FIFO的主要区别:
-
在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。对管道和FIFO来说,除非读出者已存在,否则先有写入者是没有意义的。
-
管道和FIFO是字节流模型,没有消息边界。消息队列则指定了数据长度,有边界。
mq_open 、 mq_close和 mq_unlink
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;// 当前消息数
};
mq_close和mq_unlink
// 关闭已打开的消息队列
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消息队列允许异步事件通知,以告知何时有一个消息放置到了某个空消息队列中。这种通知有两种方式可供选择:
- 产生一个信号
- 创建一个线程来执行指定的函数
// 为指定队列建立或删除异步事件通知
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实时信号
信号可划分为两个大组:
- 值在 SIGRTMIN(值34) 和 SIGRTMAX(值64) 之间(包括两者在内)的实时信号;
- 所有其他信号:SIGALRM、SIGINT、SIGKILL等
实时信号中 “实时” 的意思是:
- 信号是排队的,是按先进先出(FIFO)顺序排队的。同一信号产生3次,则递交3次
- 当有多个 SIGRTMIN 到 SIGRTMAX 范围内的解阻塞信号排队时,值较小的信号先与较大的信号递交
- 实时信号能比非实时信号携带更多信息