Posix 消息队列总结

取自第五章“Posix Message Queues”,对几个消息队列相关的函数做一下说明,简单介绍通过消息队列做进程间通信的几种方法的流程和利弊。

Posix 消息队列函数

mq_open

用于创建或打开消息队列,可设置读写模式,当设置为非阻塞模式(O_NONBLOCK)时,读或写操作会立即返回。

mq_close 和 mq_unlink

mq_close() 用于关闭打开的消息队列,但不会删除该消息队列,需要使用 mq_unlink() 函数删除消息队列。

mq_getattr 和 mq_setattr

用于获取或设置消息队列的属性,消息队列有两个重要属性:队列的最大长度、每个消息的最大长度。

mq_send 和 mq_receive

mq_send() 将消息放置到消息队列,mq_receive() 则是从消息队列中取消息,其中可以设置消息的优先级。需要注意的是用于接收消息的缓冲区大小必须大于等于消息队列中消息的最大长度。另外,消息的长度可以为零。

mq_notify

可以通过该函数注册一个信号或新线程的“回调”,当空的消息队列收到消息时,会触发信号处理函数,或者新线程的启动,让它们来 mq_receive() 接收消息做后续处理。每次处理完消息后必须重新注册。

消息队列做 IPC 方法

同步方法

不使用 mq_notify() 函数,默认使用阻塞方法一直等待新消息,或者设置非阻塞模式频繁检查是否有新消息。这种方法最简单,但也是最浪费 CPU 的方法。

异步方法

通过 mq_notify() 函数注册空消息队列的新消息到来时的信号处理或者新线程处理,这种方法稍复杂,通过异步事件通知的方法避免了无脑阻塞,在消息到来之前程序可以继续做其他的事情。

信号处理函数

若注册信号处理函数,需要注意的是在信号处理函数中必须使用 async-signal-safe 异步信号安全函数,不能调用 mq_notify()、mq_receive()、printf();这样的话,系统接收到新消息后发出某个特定信号中断,然后进入信号处理函数,可以设置一个全局变量;中断返回后继续当前处理,可以通过该全局变量获悉是否有新消息。

考虑这么一种场景:空的消息队列收到第一个新消息时,系统发出中断我们的程序进入信号处理函数但还没有调用 mq_receive() 读取该消息的这个时间间隔内,消息队列又收到了第二个、第三个消息。注意的是第二个、第三个消息不会触发信号中断了,所以在调用 mq_receive() 时需要把消息队列的消息耗干,因此可以使用非阻塞模式循环读取消息直到返回 EAGAIN 错误。

进一步地讲,我们注册了信号处理函数只是为了设置一个全局变量,每次都要中断、保护现场、恢复现场,看起来这种异步处理没有那么高效;书中提到了,可以通过 synchronously waiting for an asynchronous event 同步等待一个异步消息,通过 sigwait() 函数同步阻塞当前进程等待信号量的触发,该方法常用于多线程的处理。

另一种思路,在信号处理函数中往某个管道内写一字节(write() 函数是安全的),然后主程序 select() 阻塞等待。

启用新线程

通过 pthread 实现新消息的处理。

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理