欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux进程信号——信号处理

程序员文章站 2022-07-12 12:34:23
...

阻塞信号:

一、信号相关概念

1.实际执行信号的处理动作称为信号递达(Delivery).

2.信号从产生到递达之间的状态称为信号未决(Pending)。(不一定会立即Delivery)

3.进程可以选择阻塞(Block)某个信号。(不会递达)

4.被阻塞的信号产生时将保持在未决状态,知道进程解除对此信号 的阻塞,才会执行递达。

5.阻塞和忽略时不同的,信号被阻塞就不会递达,忽略是在递达之后,可选的一种处理动作。

二、在内核中的表示

Linux进程信号——信号处理

该图中,每一行代表着一种信号,每一个进程PCB中都有一个信号的指针,分别由3个表控制一个信号,

1.block位图:表示每种信号是否被阻塞,0表示没有被阻塞,1表示被阻塞。

2.pending位图:表示每种信号的未决状态,1表示该信号已经已经产生,但还未递达。0表示信号没有产生,或者产生后已经递达了,此时该标志位也变为0。

3.handler表:实际是一函数指针数组,数组每个元素对应处理该信号的函数指针。如果是SIG_DEL表示自行默认处理动作,如果是SIG_IGN表示忽略该信号,如果是用户自定义处理动作,则保存的是自定义函数的指针。

所以,发送信号就可以描述为:操作系统修改进程PCB中pending表中将0变为1.

三、信号集与操作函数

1.sigset_t:未决和阻塞标志可以用相同的数据类型sigset_t来存储。这个类型可以表示每个信号有效或者无效状态。在阻塞信号中有效、无效代表着是否被阻塞,未决也是同样额,同时,阻塞信号集也称为当前进程的信号屏蔽字。

2.信号集操作函数:

#include<signal.h>

int sigemptyset(sigset_t* set);

该函数的功能是使set所指向的信号集变量的所有比特位清零,比如set所指向的是当前进程的阻塞信号集,所以在该进程中的所有信号都处于未屏蔽状态。

int sigfillset(sigset_t* set);  

  该函数的功能是使set所指向的信号集变量的所有比特位均变为1。比如set所指向的是当前进程的阻塞信号集,所以在该进程中的所有信号都处于屏蔽状态。

int sigaddset(sigset_t *set,int signo);  

该函数的作用是在set所指向的信号集中使signo信号变为有效信号。

int sigdelset(sigset_t* set,int signo);  

        该函数的作用是在set所指向的信号集中使signo信号变为无效信号。

int sigismember(const sigset_t* set,int signo);  

        该函数的作用是判断set所指向的信号集中signo信号是否有效。

以上都是成功返回0,失败返回-1。

四、sigprocmask

调用函数sigpromask可以读取进程或者更改进程的信号屏蔽字。

int sigprocmask(int how,const sigset_t* set,sigset_t* oset);  

参数:

    oset:如果该参数非空,相当于保存该进程原本的信号屏蔽字。

    set:如果该参数非空,则将根据how和set修改该进程的信号屏蔽字

  how:有以下三种

  SIG_BLOCK:此时set中包含的是我们希望添加到当前信号屏蔽字中的信号,相当于mask = mask|set

  SIG_UNBLOCK:此时set中包含的是我们希望从当前信号屏蔽字中解除的信号,相当于mask = mask&~set

  SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,即mask = set。

返回值:成功返回0,失败返回-1

五、sigpending

        sigprocmask告诉我们如何对阻塞信号字进行读取和修改,那sigpending将会使我们读取进程的未决信号集。

int sigpending(sigset_t* set);  

读取进程的未决信号集,通过set参数传出。成功为0,失败-1.

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void printfsigset(sigset_t *set)
{
    int i = 0;
    for (;i<32;++i)
    {
       if (sigismember(set,i))//判断信号是否在目标集合中
        {
            putchar('1');
        }
        else
        {
            putchar('0');
        }
    }
    printf("\n");
}

int main()
{
    sigset_t s,p;
    sigemptyset(&s);//定义信号集,并清空初始化
    sigaddset(&s,SIGINT);//Ctrl+c
    sigprocmask(SIG_BLOCK,&s,NULL);//阻塞SIGINT信号
    while(1)
    {
        sigpending(&p);//获取未决信号集
        printfsigset(&p);
        sleep(1);
    }
    return 0;
}

如果执行了这个代码,发现用Ctrl+c 不能发送信号退出进程,想要退出的话,找到这个进程的pid,kill掉就行。