当前位置:   article > 正文

Linux-信号3_sigaction、volatile与SIGCHLD

Linux-信号3_sigaction、volatile与SIGCHLD


前言

本章内容主要对之前的内容做一些补充。


一、sigaction

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

之前我们学过signal来对信号进行捕捉,sigaction也是一个对信号进行捕捉的系统接口函数,不过sigaction要相对复杂一些。

参数 int signum 是要捕捉的信号编号。

参数struct* sigaction 在这里作为输入型参数,是提供给我们的一个结构体指针类型,这里的结构体名和函数名相同。

参数struct sigaction *oldact 在这里作为输出型参数。

那么struct sigaction 里面有什么呢?

struct sigaction
  {
    /* Signal handler.  */
#ifdef __USE_POSIX199309
    union
      {
	/* Used if SA_SIGINFO is not set.  */
	__sighandler_t sa_handler;
	/* Used if SA_SIGINFO is set.  */
	void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler	__sigaction_handler.sa_handler
# define sa_sigaction	__sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif

    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;

    /* Special flags.  */
    int sa_flags;

    /* Restore handler.  */
    void (*sa_restorer) (void);
  };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我们今天主要对函数体内部的sa_handler和sa_mask进行讨论

__sighandler_t sa_handler;

typedef void __signalfn_t(int);
typedef __signalfn_t *__sighandler_t;
  • 1
  • 2

根据__sighandler_t的定义,我们可以知道其本质是一个函数指针,所以这里的我们就可以知道其实本质也是像signal一样使用回调函数来进行信号的捕捉。

__sigset_t sa_mask;

typedef __sigset_t sigset_t;
  • 1

之前我们在学习sigprocmask和sigaddset等信号集接口函数的时候有过接触sigset_t,那么这里的sa_mask是什么呢?

先提出一个观点,在一个信号被处理(递达)过程中,如果同一个信号再次被发送且进入pending表,那么OS是怎样处理的? OS的处理方式是block(阻塞)相同信号,不再重复递达,等到处理完正在被处理的信号再根据情况决定。 而sa_mask在这里的作用就是可以根据其信号集的有效信号,在signum信号正在被处理时,同时阻塞sa_mask的有效信号和其本身信号。

示例代码如下

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>

void ShowPending()
{
    sigset_t pending;
    sigemptyset(&pending);
    for (int i = 1; i <= 31; i++)
    {
        sigpending(&pending);
        // 通过sigismember来打印我们的pending信号集
        std::cout << sigismember(&pending, i);
    }
    std::cout << std::endl;
}

void catchSig(int signum)
{
    std::cout << "捕捉到" << signum << "信号!" << std::endl; 

    int count = 0;
    while(1)
    {
        ShowPending();
        count++;
        if(count == 50) break;
        sleep(5);
    }
}
int main()
{
    std::cout << "pid: " << getpid() << std::endl;

    //1.定义struct sigaction类型
    struct sigaction act , oldact;

    //2.mask信号集初始化
    sigset_t mask;
    sigemptyset(&mask);

    //3.mask信号集添加1号,2号,3号, 4号,5号,6号作为有效信号
    sigaddset(&mask,1);
    sigaddset(&mask,2);
    sigaddset(&mask,3);
    sigaddset(&mask,4);
    sigaddset(&mask,5);
    sigaddset(&mask,6);

    //4.修改act中的数据
    act.sa_handler = catchSig;
    act.sa_mask = mask;

    //5.调用sigaction
    sigaction(2, &act , &oldact);

    while(1) sleep(1);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

运行结果
在这里插入图片描述

二、volatile关键字

我们之前的学习过程中,也提到过编译器会进行优化,例如我们之前讲的拷贝构造和右值引用都有提到过,而volatile主要解决关于编译器优化所导致的问题。

是的,编译器优化在少数情况下是会造成一些问题的。

而Linux中的gcc编译器是有几种不同程度的优化方案的

-O -O0 -O1 -O2 -O3 -Os -Ofast -Og

在使用gcc或g++命令时,上面的选项从左到右,编译时优化程度依次变大。

示例代码如下

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>

int flag = 0;

void catchSig(int signum)
{
    std:: cout << flag ;
    flag = 1;
    std::cout << "->" << flag <<std::endl; 
}

int main()
{
    signal(2, catchSig);
    while(1)
    {
        if(flag == 1) break;
        ;
    }
    std::cout << "程序正常退出" << std::endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这段代码如果使用

g++ -o mysignal mysignal.cc -std=c++11

进行编译

结果则是
在这里插入图片描述

这段代码如果使用

g++ -o mysignal mysignal.cc -std=c++11 -O3

进行编译

结果则是
在这里插入图片描述
程序不会自动退出。

这是因为在-O3的优化程度下,编译器检测默认执行流不会修改flag的数据,所以这里的cpu寄存器一直储存着原有的flag值0,导致在判断flag时,一直使用寄存器中的0在判断,导致循环无法退出。

现在我们使用volatile来试试

#include<iostream>
#include<cstdio>
#include<signal.h>
#include<unistd.h>

volatile int flag = 0;

void catchSig(int signum)
{
    std:: cout << flag ;
    flag = 1;
    std::cout << "->" << flag <<std::endl; 
}

int main()
{
    signal(2, catchSig);
    while(1)
    {
        if(flag == 1) break;
        ;
    }
    std::cout << "程序正常退出" << std::endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述
这个时候程序就正常推出了,所以这里volatile的意思就是让告诉编译器不要对flag进行优化,要让寄存器看到内存中的数据!


三、SIGCHLD

SIGCHLD 在子进程停止或者退出时可能收到。

所以我们再学习几种进程等待的方法。

方法一

#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>

int main()
{
    signal(SIGCHLD, SIG_IGN);
    pid_t id = fork();
    if(id == 0)
    {
        //子进程
        sleep(10);
        exit(0);
    }

    //父进程
    while(1);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

将SIGCHLD信号的处理方式变为忽略。
在这里插入图片描述
子进程没有僵尸,而是成功回收。

方法二

#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void catchCHLD(int signum)
{
    std::cout << "捕捉到SIGCHLD信号!" << std::endl;
    int pid = 0;
    while((pid = waitpid(0,nullptr,WNOHANG)) > 0)
    {
        std::cout << "成功等待" << pid << "号进程" << std::endl;
    }
}

int main()
{
    signal(SIGCHLD, catchCHLD);
    pid_t id = fork();
    if(id == 0)
    {
        //子进程
        sleep(10);
        exit(0);
    }

    //父进程
    while(1);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在这里插入图片描述
也同样可以成功回收!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/213004?site
推荐阅读
相关标签
  

闽ICP备14008679号