【操作系统】信号知识

目录

信号

信号介绍

信号的机制

信号四要素

信号的状态

阻塞信号集和未决信号集

信号集概述

信号捕捉

信号处理方式

如何避免僵尸进程


信号

信号介绍

信号的概念

信号是信息的载体,Linux/UNIX 环境下,古老、经典的通信方式, 现下依然是主要的通信手段。

信号在我们的生活中随处可见,例如:

² 古代战争中摔杯为号;

² 现代战争中的信号弹;

² 体育比赛中使用的信号枪......

信号的特点

² 简单

² 不能携带大量信息

² 满足某个特点条件才会产生

信号的机制

进程A给进程B发送信号,进程B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕后再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。每个进程收到的所有信号,都是由内核负责发送的。

进程A给进程B发送信号示意图:

 

信号四要素

每个信号必备4要素,分别是:

1)编号

2)名称

3)事件

4)默认处理动作

可通过man 7 signal查看帮助文档获取:

 

在标准信号中,有一些信号是有三个“Value”,第一个值通常对alpha和sparc架构有效,中间值针对x86、arm和其他架构,最后一个应用于mips架构。一个‘-’表示在对应架构上尚未定义该信号。

不同的操作系统定义了不同的系统信号。因此有些信号出现在Unix系统内,也出现在Linux中,而有的信号出现在FreeBSD或Mac OS中却没有出现在Linux下。这里我们只研究Linux系统中的信号。

Action为默认动作:

  • Term:终止进程

  • Ign: 忽略信号 (默认即时对该种信号忽略操作)

  • Core:终止进程,生成Core文件。(查验死亡原因,用于gdb调试)

  • Stop:停止(暂停)进程

  • Cont:继续运行进程

注意通过man 7 signal命令查看帮助文档,其中可看到 : The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

这里特别强调了9) SIGKILL 和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞。

另外需清楚,只有每个信号所对应的事件发生了,该信号才会被递送(但不一定递达),不应乱发信号!!

信号的状态

1) 产生

a) 当用户按某些终端键时,将产生信号。

终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT

终端上按“Ctrl+\”键通常产生中断信号 SIGQUIT

终端上按“Ctrl+z”键通常产生中断信号 SIGSTOP 等。

b) 硬件异常将产生信号。

除数为 0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内核产生适当的信号发送给相应的进程。

c) 软件异常将产生信号。

当检测到某种软件条件已发生(如:定时器alarm),并将其通知有关进程时,产生信号。

d) 调用系统函数(如:kill、raise、abort)将发送信号。

注意:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。

e) 运行 kill /killall命令将发送信号。

此程序实际上是使用 kill 函数来发送信号。也常用此命令终止一个失控的后台进程。

2) 未决状态:没有被处理

3) 递达状态:信号被处理了

阻塞信号集和未决信号集

信号的实现手段导致信号有很强的延时性,但对于用户来说,时间非常短,不易察觉。

Linux内核的进程控制块PCB是一个结构体,task_struct, 除了包含进程id,状态,工作目录,用户id,组id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集

6.1 阻塞信号集(信号屏蔽字)

将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(处理发生在解除屏蔽后)。

6.2 未决信号集

信号产生,未决信号集中描述该信号的位立刻翻转为1,表示信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。

信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。

信号集概述

在PCB中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为“未决信号集”。

这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对其进行位操作。而需自定义另外一个集合,借助信号集操作函数来对PCB中的这两个信号集进行修改。

 

信号捕捉

信号处理方式

一个进程收到一个信号的时候,可以用如下方法进行处理:

1)执行系统默认动作

对大多数信号来说,系统默认动作是用来终止该进程。

2)忽略此信号(丢弃)

接收到此信号后没有任何动作。

3)执行自定义信号处理函数(捕获)

用用户定义的信号处理函数处理该信号。

【注意】:SIGKILL 和 SIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法。

内核实现信号捕捉过程:

 

如何避免僵尸进程

  1. 最简单的方法,父进程通过 wait() 和 waitpid() 等函数等待子进程结束,但是,这会导致父进程挂起。

  2. 如果父进程要处理的事情很多,不能够挂起,通过 signal() 函数人为处理信号 SIGCHLD , 只要有子进程退出自动调用指定好的回调函数,因为子进程结束后, 父进程会收到该信号 SIGCHLD ,可以在其回调函数里调用 wait() 或 waitpid() 回收。

示例程序:

 ​
 void sig_child(int signo)
 {
     pid_t  pid;
 ​
     //处理僵尸进程, -1 代表等待任意一个子进程, WNOHANG代表不阻塞
     while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
     {
         printf("child %d terminated.\n", pid);
     }
 }
 ​
 int main()
 {
     pid_t pid;
 ​
     // 创建捕捉子进程退出信号
     // 只要子进程退出,触发SIGCHLD,自动调用sig_child()
     signal(SIGCHLD, sig_child);
 ​
     pid = fork();   // 创建进程
     if (pid < 0)
     { // 出错
         perror("fork error:");
         exit(1);
     }
     else if (pid == 0)
     { // 子进程
         printf("I am child process,pid id %d.I am exiting.\n", getpid());
         exit(0);
     }
     else if (pid > 0)
     { // 父进程
         sleep(2);   // 保证子进程先运行
         printf("I am father, i am exited\n\n");
         system("ps -ef | grep defunct"); // 查看有没有僵尸进程
     }
 ​
     return 0;
 }
 ​

3)如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,父进程忽略此信号,那么子进程结束后,内核会回收, 并不再给父进程发送信号。

示例程序:

int main()
{
    pid_t pid;

    // 忽略子进程退出信号的信号
    // 那么子进程结束后,内核会回收, 并不再给父进程发送信号
    signal(SIGCHLD, SIG_IGN);

    pid = fork();   // 创建进程

    if (pid < 0)
    { // 出错
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    { // 子进程
        printf("I am child process,pid id %d.I am exiting.\n", getpid());
        exit(0);

    }
    else if (pid > 0)
    { // 父进程
        sleep(2);   // 保证子进程先运行
        printf("I am father, i am exited\n\n");
        system("ps -ef | grep defunct"); // 查看有没有僵尸进程
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值