2019独角兽企业重金招聘Python工程师标准>>>

相比于System V消息队列的问题之一是无法通知一个进程何时在某个队列中放置了一个消息,POSIX消息队列允许异步事件通知(asyschronous event notification),有两种方式进行选择:

1,产生一个信号

2,创建一个线程来执行一个指定的函数

指定队列建立和删除异步事件通知:

#include <mqueue.h>

int mq_notify(mqd_t mqdes, const struct sigevent *sevp);

struct sigevent

{

int sigev_notify; //notification type    SIGEV_{NONE, SIGNAL, THREAD}

int sigev_signo; //signal number if SIGEV_SIGNAL

union sigval   sigev_value; // passed to signal handler or thread

// following two is SIGEV_THREAD

void (*sigev_notify_function)(union sigval);

pthread_attr_t *sigev_notify_attributes;

}

union sigval

{

int sival_int; //integer value

void *sival_ptr; //pointer value

}

运用此函数的若干规则:

(1),如果notification为非空,那么此进程被注册为接收该队列的通知

(2),如果notification为空,那么此进程已存在的注册将被撤销

(3),任何时刻只有一个进程可以被注册为接收某个队列的通知

(4),如果当一个消息到达队列时,此时正好有个mq_receive阻塞在该队列上,那么通知不会发出

(5),当通知到达注册进程时,其注册即被撤销

注意:通知只有在一个消息被放置到一个空队列上时才会发出,如果这个消息队列不是空队列,则通知无效。

例子:简单通知

代码:

//mqnotifysig1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>mqd_t mqd;
void *buff;
struct mq_attr attr;
struct sigevent sigev;
static void sig_usr1(int);int main(int argc, char **argv)
{if (argc != 2) {printf("usage: mqnotifysig1 <name>");exit(0);}if ((mqd = mq_open(argv[1], O_RDONLY)) == -1) {printf("open error");exit(0);}mq_getattr(mqd, &attr);buff = malloc(attr.mq_msgsize);signal(SIGUSR1, sig_usr1);sigev.sigev_notify = SIGEV_SIGNAL;sigev.sigev_signo = SIGUSR1;mq_notify(mqd, &sigev);for  ( ; ; )pause();exit(0);
}static void sig_usr1(int signo)
{ssize_t n;mq_notify(mqd, &sigev);n = mq_receive(mqd, buff, attr.mq_msgsize, NULL);printf("SIGUSR1 received, read %ld bytes\n", (long) n);return;
}

异步信号安全函数:

这里涉及到一个异步信号安全(async-signal-safe)的概念:就是在信号处理的函数中有些函数是不能用的,这么讲,可重入的函数就是信号安全。

满足下列条件的函数是不可重入的:

1) 函数体内使用了静态的数据结构;

2) 函数体内调用了malloc() 或者free() 函数;

3) 函数体内调用了标准I/O 函数。 
如何编写可重入的函数: 
    1) 不要使用全局变量。因为别的代码很可能覆盖这些变量值。 
    2) 不能调用其它任何不可重入的函数。 
    3) 在和硬件发生交互的时候,切记执行类似disinterrupt() 之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“ 进入/ 退出核心” 。

4) 谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL 。

例子:信号通知,让处理程序仅仅设置一个全局标志,有某个线程检查该标志以确定何时接收到一个信息

代码:

//mqnotifysig2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>volatile sig_atomic_t mqflag;
static void sig_usr1(int);int main(int argc, char **argv)
{mqd_t mqd;void *buff;ssize_t n;sigset_t zeromask, newmask, oldmask;struct mq_attr attr;struct sigevent sigev;if (argc != 2) {mq_notify(mqd, &sigev);printf("usage: mqnotifysig2 <name>");exit(0);}mqd = mq_open(argv[1], O_RDONLY);mq_getattr(mqd, &attr);buff = malloc(attr.mq_msgsize);sigemptyset(&zeromask);sigemptyset(&newmask);sigemptyset(&oldmask);sigaddset(&newmask, SIGUSR1);signal(SIGUSR1, sig_usr1);sigev.sigev_notify = SIGEV_SIGNAL;sigev.sigev_signo = SIGUSR1;mq_notify(mqd, &sigev);for( ; ; ) {sigprocmask(SIG_BLOCK, &newmask, &oldmask);while (mqflag == 0)sigsuspend(&zeromask);mqflag = 0;mq_notify(mqd, &sigev);n = mq_receive(mqd, buff, attr.mq_msgsize, NULL);printf("read %ld bytes\n",(long) n);sigprocmask(SIG_UNBLOCK, &newmask, NULL);}exit(0);
}static void sig_usr1(int signo)
{mqflag = 1;printf("functin sig_usr1 has been called\n");return;
}

上面的例子有个小小的bug,就是在一个通知来了之后一段时间,即使有消息发送到队列中,也不会发送出通知来的。这时我们可以通过将mq_receive非阻塞读取来避免。同时,在上面的例子中,主线程被阻塞,信号处理程序执行,主线程再次执行,这样效率很不高,我们可以通过阻塞在某个函数里,仅仅等待该信号的递交,而不是让内核执行一个只为设置一个标志的信号处理程序。所以我们用到sigwait函数。

#include <signal.h>

int sigwait(const sigset_t *set, int *sig);

此函数阻塞的等待信号集set中信号的到来,一旦某个信号到来,则继续执行,同时将这个信号存入sig中。
例子:
代码:
//mqnotifysig4.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>
#include <errno.h>int main(int argc, char **argv)
{int signo;mqd_t mqd;void *buff;ssize_t n;sigset_t newmask;struct mq_attr attr;struct sigevent sigev;if (argc != 2) {printf("usage: mqnotifysig3 <name>");exit(0);}mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK);mq_getattr(mqd, &attr);buff = malloc(attr.mq_msgsize);sigemptyset(&newmask);sigaddset(&newmask, SIGUSR1);sigprocmask(SIG_BLOCK, &newmask, NULL);sigev.sigev_notify = SIGEV_SIGNAL;sigev.sigev_signo = SIGUSR1;mq_notify(mqd, &sigev);for ( ; ; ) {sigwait(&newmask, &signo);if (signo == SIGUSR1) {mq_notify(mqd, &sigev);while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) {printf("reda %ld bytes\n",(long) n);}if (errno != EAGAIN) {printf("mq_receive error");exit(0);}}}exit(0);
}

消息队列描述符并不是普通的文件描述符,所以我们无法使用select机制来高效率的处理,但是我们可以模仿出来,通过在信号处理函数中,往一个管道写入一个字符,那么在主线程中select此管道描述就OK 了。

例子:

代码:

//mqnotifysig5.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>
#include <errno.h>int pipefd[2];
static void sig_usr1(int);int main(int argc, char **argv)
{int nfds;char c;fd_set rset;mqd_t mqd;ssize_t n;void *buff;struct mq_attr attr;struct sigevent sigev;if (argc != 2) {printf("usage: mq_notifysig5 <name>");exit(0);}mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK);mq_getattr(mqd, &attr);buff = malloc(attr.mq_msgsize);pipe(pipefd);signal(SIGUSR1, sig_usr1);sigev.sigev_notify = SIGEV_SIGNAL;sigev.sigev_signo = SIGUSR1;mq_notify(mqd, &sigev);FD_ZERO(&rset);for ( ; ; ) {FD_SET(pipefd[0], &rset);nfds = select(pipefd[0] + 1, &rset, NULL, NULL, NULL);if (FD_ISSET(pipefd[0], &rset)) {read(pipefd[0], &c, 1);mq_notify(mqd, &sigev);while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) {printf("read %ld bytes\n", (long) n);}if (errno != EAGAIN) {printf("mq_receive error");exit(0);}}}exit(0);
}static void sig_usr1(int signo)
{write(pipefd[1], "", 1);return;
}

异步事件通知的另一种方式是把sigev_notify设置成SIGEV_THREAD,这样当一消息放到一个空的队列上,会创建一个线程。

例子:

代码:

//mqnotifysigthread1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <signal.h>
#include <errno.h>
mqd_t mqd;
struct mq_attr attr;
struct sigevent sigev;static void notify_thread(union sigval);int main(int argc, char **argv)
{if (argc != 2) {printf("usage: mqnotifythread1 <name>");exit(0);}mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK);mq_getattr(mqd, &attr);sigev.sigev_notify = SIGEV_THREAD;sigev.sigev_value.sival_ptr = NULL;sigev.sigev_notify_function = notify_thread;sigev.sigev_notify_attributes = NULL;mq_notify(mqd, &sigev);for ( ; ; ) pause;exit(0);
}static void notify_thread(union sigval arg)
{ssize_t n;void *buff;printf("notify_thread started\n");buff = malloc(attr.mq_msgsize);mq_notify(mqd, &sigev);while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) {printf("read %ld bytes\n", (long) n);}if (errno != EAGAIN) {printf("mq_receive error\n");exit(0);}free(buff);pthread_exit(NULL);
}

转载于:https://my.oschina.net/u/178323/blog/32310

POSIX消息队列信号通知相关推荐

  1. linux进程间通信:POSIX 消息队列 ----异步通信

    在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞. 阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情, ...

  2. Linux IPC POSIX 消息队列

    模型: #include<mqueue.h> #include <sys/stat.h> #include <fcntl.h> mq_open() //创建/获取消 ...

  3. Linux进程间通信四 Posix 消息队列简介与示例

    目录 1. Posix 消息队列简介 2. API接口 2.1 创建或打开消息队列 2.2 发送消息 2.3 接收消息 2.4 获取.设置消息队列属性 2.5 关闭消息队列 2.6 删除消息队列 2. ...

  4. Linux IPC实践(7) --Posix消息队列

    1. 创建/获取一个消息队列 #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For m ...

  5. linux进程间通信:POSIX 消息队列

    文章目录 基本介绍 相关编程接口 编程实例 消息队列通信实例 消息队列属性设置实例 基本介绍 关于消息队列的基本介绍,前面在学习system V的消息队列时已经有过了解,linux进程间通信:syst ...

  6. posix消息队列和systemV消息队列

    一.概述: 消息队列可认为是一个消息链表.有足够写权限的线程可往队列中放置消息,有足够读权限的 线程可从队列中取走消息.posix消息队列和systemV消息队列主要如下差异: 1.一般来说posix ...

  7. POSIX 消息队列基础知识复习,以及相关例程

    1.1        Posix消息队列 1.1.1       消息队列的创建和删除 1.1.1.1     mq_open( ) #include<mqueue.h> mqd_tmq_ ...

  8. 【学习随手记】POSIX消息队列执行报Permission denied的问题。

    最近学习UNIX环境网络编程POSIX消息队列的时候发现一个问题,在mq_open的时候总会报Permission denied的错误. 查看mq_overview后才得以解决. mq_overvie ...

  9. POSIX 消息队列相关问题

    一.查看和删除消息队列 要想看到创建的posix消息队列,需要在root用户下执行以下操作: # mkdir /dev/mqueue # mount -t mqueue none /dev/mqueu ...

最新文章

  1. 安卓复杂的首页布局_到底该如何快速进入安卓编程?
  2. Docker系列 三. Docker安装mysql
  3. 将不确定变为确定~异常被抛出的顺序
  4. Nginx中worker connections问题的解决方法
  5. 关掉magiskhide_Magisk v20.4 – The Magic Mask for Android 安裝流程
  6. 深度学习框架YOLOv3的C++调用
  7. 速卖通新手入驻必须了解的“9大知识点”
  8. python 测试端口连通_Python语言 实现端口连通性检测
  9. 干货满满!10分钟看懂Docker和K8S(转)
  10. Android自定义view之ViewPager指示器——1
  11. 取消button的点击效果_(Vue动效)6.Vue中列表过渡效果
  12. React 页面渲染后自动执行onClick事件问题
  13. Java中native方法的使用
  14. L. Collecting Diamonds
  15. 接收灵敏度和等效噪声带宽(ENBW)
  16. win10系统如何设置局域网服务器,win10系统如何设置局域网共享
  17. 什么是转义字符?为什么用转义字符?常见转义字符使用情况?
  18. [业界资讯]Ubuntu 2010“雪地猞猁”最新进展
  19. php opcode 启用,Drupal8安装提示PHP OPCODE CACHING未启用的解决方法
  20. Iphone攻与防-一

热门文章

  1. I2C总线学习(二)--数据传送格式
  2. Windows 7/Windows 8都有上帝模式
  3. android 输入锁屏密码错误,安卓系统手机锁屏密码输错被停用了如何解决
  4. java数字图像处理开题报告,数字图像处理开题报告.doc
  5. LTE: 系统内移动性知识点总结
  6. Python Module — asyncio 协程并发
  7. Oracle 用户概念与基本操作
  8. NanoPi NEO Air使用一:介绍
  9. 学会使用函数式编程的程序员(第2部分)
  10. MySQL的恢复脚本