linux系统编程之信号(五):实时信号与sigqueue函数

一、sigqueue函数

功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。 原型:int sigqueue(pid_t pid, int sig, const union sigval value); 参数:sigqueue的第一个参数是指定接收信号的进程id,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。 返回值:成功返回0,失败返回-1 

typedef union sigval  {  int sival_int;  void *sival_ptr;  }sigval_t; 

sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。

写两个小程序测试一下:

首先是接收信号:

/*************************************************************************
    > File Name: process_.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int, siginfo_t *, void *);


int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_sigaction = handler; //sa_sigaction与sa_handler只能取其一
    //sa_sigaction多用于实时信号,可以保存信息
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO; // 设置标志位后可以接收其他进程
    // 发送的数据,保存在siginfo_t结构体中

    if (sigaction(SIGINT, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    for (; ;)
        pause();

    return 0;

}

void handler(int sig, siginfo_t *info, void *ctx)
{
    printf("recv a sig=%d data=%d data=%d\n",
           sig, info->si_value.sival_int, info->si_int);

}

在前面的《信号捕捉与sigaction函数》中说过,sa_sigaction与SA_SIGINFO要配合使用,如上所示,siginfo_t 结构体也可以参见这篇文章。

然后是信号发送:

/*************************************************************************
    > File Name: process_.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)



int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage %s pid\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid_t pid = atoi(argv[1]); //字符串转换为整数
    union sigval val;
    val.sival_int = 100;
    sigqueue(pid, SIGINT, val); // 只可以发信号给某个进程,而不能是进程组

    return 0;

}

测试如下:

先运行recv程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_recv 

再ps出recv进程的pid,然后运行send程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_send 3323

则recv进程会输出一条recv语句,当然我们也可以ctrl+c 给自己发送信号,如下所示,结果是一样的。

recv a sig=2 data=100 data=100 ^Crecv a sig=2 data=100 data=100 ^Crecv a sig=2 data=100 data=100

......................................................

需要提醒一下的是siginfo_t 结构体的两个参数(int  si_int;   /* POSIX.1b signal */     void  *si_ptr;  /* POSIX.1b signal */)的值也会与si_value 一致,取决于发送的是sival_int 还是 sival_ptr。

二、实时信号与不可靠信号的区别

下面通过程序来说明区别,主要就是实时信号支持排队不会丢失。(实时信号还有一个特点,即到达的顺序是可以保证的)

先是recv程序:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int);

int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGRTMIN);
    sigprocmask(SIG_BLOCK, &s, NULL);
    if (sigaction(SIGINT, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    if (sigaction(SIGRTMIN, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    if (sigaction(SIGUSR1, &act, NULL) < 0)
        ERR_EXIT("sigaction error");
    for (;;)
        pause();
    return 0;
}

void handler(int sig)
{
    if (sig == SIGINT || sig == SIGRTMIN)
        printf("recv a sig=%d\n", sig);
    else if (sig == SIGUSR1)
    {
        sigset_t s;
        sigemptyset(&s);
        sigaddset(&s, SIGINT);
        sigaddset(&s, SIGRTMIN);
        sigprocmask(SIG_UNBLOCK, &s, NULL);
    }
}

在主函数中将SIGINT和SIGRTMIN信号加入信号屏蔽字,只有当接收到SIGUSR1信号时才对前面两个信号unblock。需要注意的是如《信号的未决与阻塞》中说的一样:如果在信号处理函数中对某个信号进行解除阻塞时,则只是将pending位清0,让此信号递达一次(同个实时信号产生多次进行排队都会抵达),但不会将block位清0,即再次产生此信号时还是会被阻塞,处于未决状态。

接着是send程序:

/*************************************************************************
    > File Name: sigrtime_send.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)



int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage %s pid\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid_t pid = atoi(argv[1]); //字符串转换为整数
    union sigval val;
    val.sival_int = 100;
    sigqueue(pid, SIGINT, val); // 不可靠信号不会排队,即会丢失
    sigqueue(pid, SIGINT, val);
    sigqueue(pid, SIGINT, val);
    sigqueue(pid, SIGRTMIN, val); //实时信号会排队,即不会丢失
    sigqueue(pid, SIGRTMIN, val);
    sigqueue(pid, SIGRTMIN, val);
    sleep(3);
    kill(pid, SIGUSR1);

    return 0;

}

先是运行recv程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_recv2

接着ps出recv进程的pid,运行send程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_send 4076

在send程序中连续各发送了SIGINT和SIGRTMIN信号3次,接着睡眠3s后使用kill函数发送SIGUSR1信号给recv进程,此时recv进程会输出如下:

recv a sig=34 recv a sig=34 recv a sig=34 recv a sig=2

即实时信号支持排队,3个信号都接收到了,而不可靠信号不支持排队,只保留一个信号。

参考:《APUE》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木木玲

Netty in action ——— 事件循环 和 线程模式

27530
来自专栏代码拾遗

SpringMVC 教程 - 异步请求

在Servlet容器中启动异步支持之后,controller的方法可以通过DeferredResult包装返回值来支持异步处理。例如:

25230
来自专栏蓝天

深入多线程编程

非阻塞型同步(Non-blocking Synchronization)

11230
来自专栏LanceToBigData

SpringBoot(二)Web整合开发

Spring Boot (二):Web 综合开发 本篇文章接着上篇内容继续为大家介绍spring boot的其它特性(有些未必是spring boot体系桟的功...

34770
来自专栏软件工程师成长笔记

SSM框架——干净详细的整合学习教程(Spring+SpringMVC+MyBatis)

熟悉MVC的同学都知道,MVC即model(模型)、view(视图)、controller(控制),用一种业务逻辑,数据,界面显示分离的方式使得开发更加的便捷高...

1.4K20
来自专栏恰童鞋骚年

.NET Core微服务之基于Steeltoe使用Zipkin实现分布式追踪

  在 SpringCloud 之中提供的 Sleuth 技术可以实现微服务的调用跟踪,也就是说它可以自动的形成一个调用连接线,通过这个连接线使得开发者可以轻松...

20310
来自专栏微信公众号:Java团长

Java Web现代化开发:Spring Boot + Mybatis + Redis二级缓存

Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一。Mybatis是一个十分轻量好用的ORM框架。Red...

32020
来自专栏java思维导图

教你十分钟构建好 SpringBoot + SSM 框架

目前最主流的 java web 框架应该是 SSM,而 SSM 框架由于更轻便与灵活目前受到了许多人的青睐。而 SpringBoot 的轻量化,简化项目配置, ...

15540
来自专栏龙首琴剑庐

OpenJDK8 Windows编译

前言 Open JDK 是开源的JDK。通过对开源JDK的编译调试,我们可以深入了解JVM的运行原理。 以下是我的环境信息: 操作系统: Microsoft W...

856110
来自专栏Java帮帮-微信公众号-技术文章全总结

spring框架复习大纲【面试+提高】

spring框架复习大纲【面试+提高】 简介 创立于 2003年 , 是为了解决企业级开发的复杂性的! 是一个分层的se/ee(javase和java ee...

47160

扫码关注云+社区

领取腾讯云代金券