进程信号

本文详细介绍了进程信号的基本概念、底层机制、相关API函数,特别是`sigaction`函数的使用,以及如何借助`SIGCHLD`信号处理僵尸进程。信号作为一种异步事件处理方式,涉及信号产生、处理方式、信号屏蔽与递达状态等关键点。通过实例解析了`signal`和`sigaction`的区别,并展示了自定义睡眠函数避免竞态条件的技巧。
摘要由CSDN通过智能技术生成

信号时一种从软件层面上对中断的模拟,很多重要的程序都需要处理信号,信号提供了一种处理异步事件的方法。比如,用户在终端按下 ctrl C 会终止一个进程,或者通过 kill 命令来给特定的进程发送信号。

信号基本概念

每个信号都有一个名字,这些名字以SIG 开头,在头文件 <signal.h> 中,这些信号名被定义为正整数常量(信号编号)。我们可以通过 kill -l 查看系统定义的信号列表。如下:

这里写图片描述

如上:ctrl C 对应 SIGINT 信号,信号编号是2。

产生信号的条件:

  • 当用户按下某些终端按键时,会产生终端信号。如上面提到的 ctrl C
  • 硬件异常产生信号:如除 0 异常, 无效的地址访问(对NULL指针解引用、内存越界访问),这些信号通常有计算机硬件检测到,并通知操作系统内核,内核再给触发异常的进程发送合适的信号,如:对无效内存的解引用的进程将受到 SIGSEGV 信号;
  • 通过系统调用函数: int kill(pid_t id, int sig);,下面是一个例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>


int main()
{
    int id = fork();
    int count = 0;
    if(id < 0){
        perror("fork");
        return -1;
    }else if(id == 0){
  //child
        for(;;){
            printf("%d\n", count++);
            usleep(1000*200);
        }
    }else{
  //father
        sleep(3);
        kill(id, SIGKILL);
        wait(0);
    }
    return 0;
}

上面这段代码中,子进程做死循环打印动作,父进程睡眠 3 秒后给子进程发送信号,将其终止。

  • 命令行中通过 kill [opt] <pid> 命令给指定进程发送 指定命令;
  • 当检测到某些软件条件产生时,应将其通知有关进程并产生信号。比如:SIGALRM(进程设置的定时器超时),SIGPIPE(管道的读进程终止后,一个进程写管道)。

信号的处理方式:

  • 忽略此信号。有两种信号不能忽略,它们是 SIGKILLSIGSTOP ,原因是:它们像内核和超级用户提供了是进程终止的可靠方法。
  • 扑捉信号。通知内核在某种信号发生时,调用一个用户函数,在该函数中,执行用户希望对该信号处理的动作。如 一个子进程终止时,会想其父进程发送 SIGCHLD 信号,所以我们可以自定义信号的捕捉函数,在该函数中调用 waitpid 以获取子进程的 ID 和退出状态;
  • 执行默认动作,大多数系统默认动作是终止该进程。
信号的底层机制

我们来看看,当我们在键盘上敲下 ctrl C 时操作系统都发生了什么。
这里写图片描述

可以看到,对某一个进程发送信号实际上是,将该进程 PCB 的某个字段设置一个值,那么这一点在 PCB 中更是怎样展现的呢?看下面这张图:
这里写图片描述

执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

可以将信号在 PCB 中的标识看为上图中的三张表:

  • block 又称为信号屏蔽状态字,它是个 64bit 的位图,每个位代表一个信号,对应位为1,则表示信号被阻塞;
  • pending 又称为信号未决状态字, 它也是个 64bit 的位图,每个位代表一个信号,对应位为1代表未决,0代表信号可以递达了;
  • handler 表中记录了每种信号的处理方式,对于用户自定义的处理函数,该表对应位上为信号处理函数的地址。

注意:

  • 比如向进程发送进程 SIGINT 信号,内核首先判断信号屏蔽状态字是否阻塞,如果该信号被设为为了阻塞的,那么信号未决状态字(pending)相应位制成1;若该信号阻塞解除,信号未决状态字(pending)相应位制成0;表示信号此时可以抵达了,也就是可以接收该信号了;
  • 信号设计的机制是:用户可以读写信号屏蔽状态字 ,但只能读信号未决状态字。

信号的捕捉:
信号的处理动作为用户自定义函数,在信号递达是就调用该函数,这个动作称为信号的捕捉。用户自定义的信号处理函数在用户空间内,所以其相应的处理就要涉及到 “用户态——内核态” 之间的相互切换,过程比较复杂。比如用户注册了 SIGINT 信号的处理函数 myHandler(),那么当进程发生中断或者异常后,就切换到内核态,在处理完中断异常,在返回用户态之前,内核检查到有信号 SIGINT 递达,那么内核就返回至用户态执行 myHandler() 信号处理函数&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值