C语言posix风格mq_notify的经典示例

实验准备:

1 一个一直在运行的程序 

2 一个空的posix风格消息队列

3一个可以向消息队列中发送消息的程序 和 一个可以发送kill命令的控制台

实验描述:

进程a先试探性的获取消息队列中的消息 然后一直在运行

发现消息队列有消息进入,会由mq_notify发送sigusr1信号给宿主进程a,

宿主进程a捕获信号后,接受消息队列中的消息

进程a还可以捕获由kill -2(Ctrl+C)发送的信号 这将导致进程a在运行清理函数后退出

实验步骤:

开启新终端,编译运行第一篇代码

开启新终端,编译运行第二篇代码

观察现象:第一篇代码的终端有响应

可以循环运行第二篇代码,观察现象

使用第二篇代码的终端发送 kill -2 pid

观察现象:第一篇代码的进程终止

注意事项:

以下两个程序 必须编译后由独立终端开启 不可以在IDE直接运行,因为涉及到信号

此程序为linux gnu_c的一部分 在其他操作系统运行可能需要改动

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <time.h>
#include <limits.h>
#include <sys/sem.h>   //System V信号量
#include <semaphore.h> //POSIX信号量
#include <sys/shm.h>   //System V共享内存
#include <sys/mman.h>  //POSIX共享内存
#include <sys/msg.h>   //System V消息队列
#include <mqueue.h>    //POSIX消息队列
//慎重使用全局变量
mqd_t global_mqd;

void mq_open_init()
{
    //打开消息队列 能存10个消息每条大小64字节
    struct mq_attr attr = {0};
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 64;
    //读写模式,读写都非阻塞不用等,为notify提供条件
    mqd_t mqd = mq_open("/mq1", O_CREAT | O_RDWR | O_NONBLOCK, 0777, &attr);
    if (mqd == -1)
    {
        perror("mq_open");
    }
    //将消息队列描述符赋值给全局变量
    global_mqd = mqd;
}
void mq_receive_init(mqd_t mqd)
{
    char buffer[1024];
    unsigned int msg_prio;
    //把getattr的结果存到attr中,并打印当前消息队列中消息的数量
    struct mq_attr attr = {0};
    if (mq_getattr(mqd, &attr) == -1)
    {
        perror("mq_getattr");
    }
    printf("mq_curmsgs:%ld\n",attr.mq_curmsgs);
    //接收缓冲区必须比attr.mq_msgsize大,buffer装的是消息正文,msg_prio是已接收消息的优先级
    //mq_receive自动接收消息优先级大的消息,无法干预
    ssize_t r_bytes = mq_receive(mqd, buffer, 1024, &msg_prio);
    if (r_bytes == -1)
    {
        if (errno == EAGAIN)
        {
            perror("EAGAIN");
        }
        else
        {
            perror("mq_receive");
        }
    }
    //再看看消息队列里有几条消息
    if (mq_getattr(mqd, &attr) == -1)
    {
        perror("mq_getattr");
    }
    printf("mq_receive_bytes:%zd\nmq_context:%s\nmsg_prio:%d\nmq_curmsgs:%ld\n"
    , r_bytes, buffer, msg_prio, attr.mq_curmsgs);
}
void mq_notify_init(mqd_t mqd)
{   
    struct sigevent se;
    //如果有消息进入消息队列,使用信号通知
    se.sigev_notify = SIGEV_SIGNAL;
    //发送的通知信号为 SIGUSR1
    se.sigev_signo = SIGUSR1;
    //还需要携带mdq的值一起发走
    se.sigev_value.sival_int = mqd;
    if (mq_notify(mqd, &se) == -1)
    {
        perror("mq_notify");
    }
}
void sig_handler_receive_notify(int signo, siginfo_t *siginfo, void *a)
{
    printf("%s\n", strsignal(signo));
    mqd_t mqd = siginfo->si_value.sival_int;
    //接收
    mq_receive_init(mqd);
    //注册通知,实现循环接收功能
    mq_notify_init(mqd);
}
void sig_handler_exit_cleanup(int signo, siginfo_t *siginfo, void *a)
{
    //获取mqd的值 sigaction的处理函数是一个函数指针类型 不能修改函数头造型
    mqd_t mqd = siginfo->si_value.sival_int;
    //关闭与队列的链接
    if (mq_close(mqd) == -1)
    {
        perror("mq_close");
    }
    //删除队列
    if (mq_unlink("/mq1") == -1)
    {
        perror("mq_unlink");
    }
    printf("exit(EXIT_SUCCESS)\n");
    exit(EXIT_SUCCESS);
}
void sigaction_SIGUSR1()
{   //注册收到SIGUSR1的处理函数sig_handler_receive_notify
    struct sigaction sa = {0};
    sa.sa_sigaction = sig_handler_receive_notify;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGUSR1, &sa, NULL) == -1)
    {
        perror("sigaction_SIGUSR1");
    }
    
}
void sigaction_SIGINT()
{
    //注册收到SIGINT的处理函数sig_handler_exit_cleanup
    struct sigaction sa = {0};
    sa.sa_sigaction = sig_handler_exit_cleanup;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGINT, &sa, NULL) == -1)
    {
        perror("sigaction_SIGINT");
    }
}
int main()
{   //打开消息队列
    mq_open_init();
    //尝试接收消息
    mq_receive_init(global_mqd);
    //初始化并注册异步通知
    mq_notify_init(global_mqd);
    //注册信号函数SIGUSR1
    sigaction_SIGUSR1();
    //注册信号函数SIGINT
    sigaction_SIGINT();
    // 模仿程序运行
    while (1)
    {
        printf("%d doing...\n", getpid());
        sleep(1);
    }
    return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h>   //System V信号量
#include <semaphore.h> //POSIX信号量
#include <sys/shm.h>   //System V共享内存
#include <sys/mman.h>  //POSIX共享内存
#include <sys/msg.h>   //System V消息队列
#include <mqueue.h>    //POSIX消息队列

int main()
{
    //此程序用于向消息队列中增加一条消息
    printf("%d\n", getpid());
    struct mq_attr attr = {0};
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 64;
    mqd_t mq_d = mq_open("/mq1", O_CREAT | O_RDWR, 0777, &attr);
    if (mq_d == (mqd_t)-1)
    {
        perror("mq_open");
    }
    char buffer[1024] = {0};
    sprintf(buffer, "%d say hello", getpid());
    if (mq_send(mq_d, buffer, strlen(buffer) + 1, 1) == -1)
    {
        perror("mq_send");
    }
    mq_close(mq_d);
    return 0;
}

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

先天编程圣体

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值