在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞。
阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情,而是一直处于阻塞状态。
为了避免出现这样的低效问题,POSIX消息队列推出如下接口实现异步通信机制
- 函数头文件
#include <mqueue.h>
- 函数使用:
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
- 函数功能:允许调用者注册或者注销一个异步信号,当进行消息传输的时候利用该信号可以执行异步操作
具体如下:
当一个空的消息队列接收到消息时给进程发送一个通知;
当执行完相关处理,通知机制结束,可以重新调用mq_notify
注册 - 函数参数:
mqdes
: 消息队列的id
sevp
: 通知方式设置
其中关于结构体sigevent
描述如下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_THREAD/SIGEV_THREAD_ID
代码演示如下:
mq_notify.c
异步读
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>
#include <string.h>
mqd_t mq_id;
char buf[8192];
struct sigevent sigev;
static void signal_handler(int signo) {
ssize_t receive_len;
//再次进行消息队列和异步通知方式注册的结构体进行绑定
mq_notify(mq_id, &sigev);
//尝试从mq_id中获取消息
receive_len = mq_receive(mq_id, buf,8192, NULL);
if (receive_len == -1) {
printf("mq_receive failed \n");
_exit(-1);
}
printf("read %ld bytes: %s\n",(long) receive_len, buf);
return ;
}
int main() {
//创建消息队列
mq_id = mq_open("/notify",O_RDONLY | O_CREAT, 0666, NULL);
if (mq_id == -1)
{
printf("mq_open failed \n");
_exit(-1);
}
//信号处理逻辑,注册SIGUSR1用户信号,并设置回调函数
signal(SIGUSR1, signal_handler);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
//将消息队列的id和注册的异步通信结构体进行绑定
mq_notify(mq_id, &sigev);
int count = 0;
while(1) {
printf("while loop %d \n", count ++);
sleep(1);
}
mq_close(mq_id);
return 0;
}
mq_snd.c
同步写
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <string.h>
int main () {
mqd_t mq_id;
//打开创建好的消息队列
mq_id = mq_open("/notify",O_WRONLY | O_CREAT, 0666, NULL);
if (mq_id == -1) {
printf("mq_open failed\n");
_exit(-1);
}
//持续向消息队列中每隔两秒写入数据
while(1) {
if (mq_send(mq_id, "hello world", sizeof("hello world"), 1) == -1) {
printf("mq_send failed \n");
_exit(-1);
}
printf("msg send success -----\n");
sleep(2);
}
return 0;
}
编译以上两个文件:gcc mq_notify.c -o mq_notify -lrt
gcc mq_snd.c -o send -lrt
输出如下:
我们可以看到异步读并不会受消息队列为空的影响,即使消息队列为空,异步读进程仍然继续处理自己的事情;当同步写进程运行的时候,异步读进程收到一个使用mq_id注册的信号,开始执行信号回调函数,函数执行完,异步读进程继续处理自己的事情,直到再次收到相同的信号从而再次执行回调函数中的读消息队列。