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. AI先驱、A*算法发明者Nils Nilsson去世
  3. JavaScript设计模式学习——builder pattern(建造者模式)
  4. Android中用GridView实现九宫格的两种方法
  5. synchronousqueue场景_java并发队列之SynchronousQueue
  6. 游戏开发--开源软件11--Firefly(python 服务端分布式框架)||pygame
  7. LeetCode 433. 最小基因变化(广度优先搜索)
  8. linux路由内核实现分析(二)---FIB相关数据结构(3)
  9. Windows8在激烈竞争的平板电脑市场的优势与特色---移动3G时代失落的windows开发人员的福音...
  10. 「洛谷P2397」 yyy loves Maths VI (mode) 解题报告
  11. 系统学习深度学习(二十一)--GoogLeNetV4与Inception-ResNet V1,V2
  12. iOS开发之iOS11、iPhone X、Xcode9 适配指南
  13. phar打包thinkphp5项目
  14. 线性代数及其应用(part2)--特征方程
  15. 大华相机SDK调用——主动采图、外触发、参数
  16. win10-64位-汇编环境配置
  17. 区块链开发金融交易平台
  18. 阄阄乐-IOS抓阄抽签工具
  19. 海致大数据京信_2018华为全联接大会|海致网聚提出公安大数据个人计算新理念...
  20. 高考英语50分学计算机,高三英语50分到100分学习窍门

热门文章

  1. 对进入单用户进行加密
  2. PHP array_key_exists() 函数(判断某个数组中是否存在指定的 key)
  3. vsftp421问题
  4. SD-WAN — 应用场景
  5. 架构师之路 — API 经济 — Swagger OpenAPI Specification
  6. Service Mesh — Istio
  7. 蚂蚁金服自研的OceanBase升级到2.0了,据说性能……
  8. python_day21面向对象的进阶(反射,内置方法,)
  9. PLSQL_解析过程及硬解析和软解析的区别(案例)
  10. JS中使用EL表达式