Linux 进程信号

Linux 进程信号

信号的概念

程序在正常执行过程中出现的异常情况, Linux可以响应这些异常的情况,当这些异常情况发生时,我们的程序就要做出相应的动作

产生信号的来源

用户按键
非法的内存访问
硬件故障
浮点数溢出
软件条件产生

处理信号的方式

缺省处理
忽略(SIGKILL SIGSTOP 不能忽略)
捕捉信号(SLGKILL SIGSTOP 不能被捕获处理)

常见的信号

Linux 进程信号_第1张图片
13号信号 SIGPIPE 管道破裂信号
18号信号 SIGCONT 继续执行信号
29号信号 SIGIO 异步IO信号

注册信号

函数原型:

void (*signal(int signum, void (*handler)(int)))(int);
void (*signal(int signum, //要进行处理的信号
     void (*handler)(int)) //信号处理函数
                           //SIG_IGN 忽略
                           //SIG_DEF 缺省处理动作
                           //SIG_ERR 
     )(int);

例子:先让Ctrl+C不执行中断功能,当按Ctrl+\后让Ctrl+C恢复原功能

#include
#include
#include

void handler1(int s)
{
    printf("收到%d号信号,但是我不死\n", s);
}

void (*handler_t)(int) = NULL;


void handler_quit(int s)
{
    printf("收到%d号信号\n", s);
    signal(SIGINT, handler_t);
}


int main()
{
    handler_t = signal(SIGINT, handler1);
    signal(SIGQUIT, handler_quit);
    for(; ;)
    {
        printf(".");
        sleep(1);
        fflush(stdout);
    }
    return 0;
}

信号的分类

不可靠信号(1~31):
1. 会出现丢失
2. 执行完信号处理函数之后,会恢复到缺省动作(Linux解决)

可靠信号(34~64):
1. 不会出现信号丢失
2. 不会出现恢复到缺省动作

非实时信号:不可靠信号都是非实时信号,在信号递达之前产生多次只计一次

实时信号:可靠信号都是实时信号,在递达之前产生多次可以依次放在一个队列里

操作系统发送信号的原理

对于不可靠信号,在进程的PCB中有一个32bit的位图,位图的每一位代表了一个相应编号的信号。操作系统在向某个发送信号时,首先找到该进程的PCB,然后将PCB中位图的相应位置值1。信号对于进程的控制流来说是异步的。

信号在内核中的示意图

产生信号的方法

  1. 键盘组合按键
  2. bash中的kill命令
  3. 系统调用 kill函数 、raise函数、abort函数
  4. 由软件条件产生信号 alarm函数

alarm函数:

#include
unsigned int alarm(unsigend int seconds);

当seconds秒后向当前进程发送闹钟信号SIGALARM。

阻塞信号

信号递达 delvery:实际执行信号的处理动作
信号未决 pending:信号从产生到递达的中间状态
信号阻塞 block:被阻塞的信号将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达动作。

三张表:
block(位图) 为1表示信号被屏蔽,不会被抵达
pending(位图) 为1表示产生该信号
handler(函数指针数组) 对应位置表示对信号的处理方法
Linux 进程信号_第2张图片
如上图所示,在task_struct结构体(PCB)中有两张位图表和一个函数指针数组,每个位图的低32位中的每一位都代表一种信号;相应的,函数指针数组中的每一个函数指针都对应着一种信号。在图示中,1号信号SIGHUP没有产生也没有被阻塞,该信号响应默认的处理动作;2号信号SIGINT产生了但是被阻塞了,该信号响应的处理动作为忽略;3号信号SIGOUT产生了,没有被阻塞,该信号响应的处理动作为用户自定义的处理动作。

进程什么时候处理信号?

当进程从内核态切换为用户态时检查是否有信号(查看pending表)

信号捕捉过程

如果信号的处理函数为自定义的,在信号递达时就会调用这个函数,这称为捕捉信号。

Linux 进程信号_第3张图片
如上图所示,用户态的进程在运行过程中遇到异常(收到信号),进入内核态保存上下文,由于定义了信号捕捉函数,此时再转去用户态执行自定义的函数,自定义函数执行完后,返回到内核态去,再调用内核中的函数来返回到原来的位置。

函数重入

函数被不同的控制流程调用,发生在一个调用还没返回时就再次进入该函数的现象。

可重入的条件:
函数只访问自己的局部变量或参数

符合下列条件之一就是不可重入:
1. 调用了malloc或free,因为它们是用全局链表来管理的,有可能因为重入造成错乱。
2. 调用了标准I/O库。因为标准I/O库的很多实现都是以不可重入的方式在使用全局数据结构。

竞态条件

程序在运行过程中由于时序问题而导致的错误,称为竞态条件

你可能感兴趣的:(Linux,Linux,信号)