linux进程信号

理解进程信号

OS向进程传递信号,进程会根据不同的信号,做出不同的反映。
linux输入kill -l可以查看所有信号类型:
在这里插入图片描述

产生方式
  1. 通过终端按键产生信号(ctrl+c)
  2. 调用系统函数向进程发信号(int kill(pid_t pid, int signo))
  3. 由软件条件产生信号(管道没有读端,alarm函数)
  4. 硬件异常产生信号(除0发生溢出,硬件向操作系统传递消息,操作系统传递信号)

但所有信号都是OS向进程传递产生的。

捕捉信号

其实所有信号的处理操作都是一个**void handler(int)**类型的函数,我们可以通过signal函数修改对应信号的操作
在这里插入图片描述
第一个参数是对应的信号的编号,第二个参数是一个函数指针,就是修改后的处理信号操作

int count=0;
void handler(int s)
{
    std::cout<<"signal is "<<s<<" count is "<<count<<std::endl;
}

int main()
{
    signal(SIGALRM,handler);
    alarm(1);
    while(true)
    {
        count++;
        sleep(20);
        alarm(1);
        
    }
    return 0;
}

这个程序就会一直循环打印count的值
在这里插入图片描述

三个概念和三张表

  1. 实际执行信号的处理动作称为信号递达(Delivery)。
  2. 信号从产生到递达之间的状态,称为信号未决(Pending)。
  3. 进程可以选择阻塞 (Block )某个信号。

上述我们处理信号的函数的调用就是信号递达,而所有递达的函数都是存储在一个函数指针数组当中,一个 void ( [ ])(int)* 数组中,这就是第一张表。
而我们可以操作系统向进程传递信号,信号被进程接收并存储在另一张表中,这个表的信号表示需要处理的信号,这个表中的信号处于未决状态。
当信号处于未决状态时,表示需要有递达动作,但这时进程可以选择不处理这个信号,这个信号就会被阻塞而一直处于未决状态,而判断是否需要阻塞,那就需要最后一张表,这个表里标注需要被阻塞的信号编号。

在这里插入图片描述

操作

每个信号有一个bit位决定一个信号,其中block和pending都是位图结构,linux专门自定义了个sigset_t类型的数据在用户层面表示这两张表,我们可以通过sigset_t类型数据的修改,在将修改后的传入系统调用函数来修改内核的block表,pending表不需要修改,因为OS传递信号就是在修改pending表。

#include <signal.h>
int sigemptyset(sigset_t *set);//置空
int sigfillset(sigset_t *set);//填满
int sigaddset (sigset_t *set, int signo);//设置一个信号
int sigdelset(sigset_t *set, int signo);//取消一个
int sigismember(const sigset_t *set, int signo);//检测某个信号是否被设置

这些函数都是用户层面上的操作
下面看一个系统层面操作的函数(设置block)

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//how 是设置的方式
//set 是输入的表,是输入型参数
//oldset 是对原始表的备份,是输出型参数

在看一个函数

int sigpending(sigset_t *set);//获取pending表内容

验证一下

void printpendingmap(sigset_t* pendingmap)
{
    for(int i =31;i>=1;i--)
    {
        if(sigismember(pendingmap,i))std::cout<<1;
        else std::cout<<0;
    }
    std::cout<<std::endl;
}

int main()
{
    sigset_t newset,oldset;
    sigemptyset(&newset);
    sigaddset(&newset,2);
    sigprocmask(SIG_SETMASK,&newset,&oldset);
    while(true)
    {
        sigset_t pendingmap;
        sigpending(&pendingmap);
        printpendingmap(&pendingmap);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
但并不是所有信号都可以阻塞,比如kill 9信号就不行。

信号捕捉

当某一个信号正处于递达状态时,则相同的信号会阻塞,即相同的信号不会递达而一直处于未决状态。
而其它信号在默认状态下不会阻塞,我们可以通过下述函数设置要阻塞的信号

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
//signo是处理的信号编号
//act是输入型参数,是要处理的内容
//oact是输出型的参数,是备份
struct sigaction {
               void     (*sa_handler)(int);//信号对应的函数
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;//设置当前信号调度时要阻塞的信号,调度完后会移除
               int        sa_flags;
               void     (*sa_restorer)(void);
               };

这个函数和signal函数类似,但它功能更强大。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值