11-12 - 信号发送与处理

---- 整理自狄泰软件唐佐林老师课程

查看所有文章链接:(更新中)Linux系统编程训练营 - 目录

文章目录

  • 1. 信号的概念及分类
    • 1.1 问题
    • 1.2 什么是信号
    • 1.3 信号的分类
      • 1.3.1 硬件异常信号
      • 1.3.2 终端相关信号
      • 1.3.3 软件相关信号
    • 1.4 内核与信号
    • 1.5 System V vs BSD
  • 2. 信号发送与处理
    • 2.1 信号处理
      • 2.1.1 信号的默认处理
      • 2.1.2 自定义信号处理
      • 2.1.3 信号处理示例
    • 2.2 信号发送
      • 2.2.1 自定义信号发送
      • 2.2.2 信号发送示例
    • 2.3 编程实验:信号发送与处理
  • 3. 信号处理三大特性
    • 3.1 问题
    • 3.2 信号的OneShot 性
    • 3.3 信号的自身屏蔽特性
    • 3.4 系统调用重启特性
    • 3.5 默认signal函数的特性
    • 3.6 编程实验:三种特性实验
      • 3.6.1 oneshot 实验
      • 3.6.2 信号的自身屏蔽特性(信号重入)实验
      • 3.6.3 系统调用重启实验
      • 3.6.4 处理A收到B
    • 3.7 注意事项
    • 3.8 小结
  • 4. 初探现代信号处理
    • 4.1 现代信号处理注册函数
      • 4.1.1 现代信号处理语义分析
      • 4.1.2 信号状态小知识
      • 4.1.3 信号屏蔽 vs 信号阻塞
    • 4.2 现代信号处理注册示例
    • 4.3 编程实验:现代信号处理
      • 4.3.1 oneshot
      • 4.3.2 屏蔽&阻塞相关
      • 4.3.3 系统调用重启
      • 4.3.4 处理A收到B
      • 4.3.5 signal实际调用sigaction
  • 5. 现代信号发送与处理
    • 5.1 sigqueue
    • 5.2 现代信号处理函数的关键参数
    • 5.3 现代信号发送处理示例
    • 5.4 编程实验:现代信号发送
    • 5.5 思考

1. 信号的概念及分类

1.1 问题

  • 按下ctrl+c后,命令行的前台进程会被终止,why?

1.2 什么是信号

  • 信号是一种 “软件中断”,用来处理异步事件
    • 内核发送信号到某个进程,通知进程事件的发生
    • 事件可能来自硬件,可能来自用户输入,可能来自程序自身错误(如除零错误等)
  • 信号是一种类型的 进程间通信方式一个进程向另一个进程发送信号
    • A进程发生事件T,向B进程发送信号,B进程执行动作响应事件
    • 进程可以对接收到的 不同信号 进行 不同动作响应 (信号,处理)

1.3 信号的分类

  • 硬件异常
    内核检测到硬件错误,发送相应信号给相关进程
  • 终端信号(用户交互信号)
    在终端输入“特殊字符”等价于向前台进程组发送相应的信号
  • 软件信号
    在软件层面(进程代码中)触发的信号(发送给自身或其它进程)

1.3.1 硬件异常信号

11-12 - 信号发送与处理_第1张图片

1.3.2 终端相关信号

  • SIGINT(ctrl+c)
    • 程序终止信号,用于通知前台进程组终止进程
  • SIGQUIT(ctrl+\)
    • 与SIGINT类似,进程收到该信号退出时可产生coredump文件
  • SIGTSTP(ctrl+z)
    • 停止进程的运行,进程收到该信号后可以选择处理和忽略
    • 进程收到该信号后停止运行(状态发生转换),后续可恢复运行状态

1.3.3 软件相关信号

  • 子进程退出:父进程收到SIGCHLD信号
  • 父进程退出:子进程可能收到信号(什么信号?)
  • 定时器到期:alarm(),ualarm(),timer_create(),……
  • 主动发送信号:kill(),raise(),……
  • ……

1.4 内核与信号

11-12 - 信号发送与处理_第2张图片

1.5 System V vs BSD

  • System V:也被称为AT&T SystemV,是Unix操作系统众多版本中的一支
  • BSD:加州大学伯克利分校开创,Unix衍生系统,代表由此派生出的各种套件组合

Linux之所以被称为类Unix操作系统,部分原因就是Linux的操作风格是介于上述二者之间,且不同厂商为了照顾不同的用户,其发行版本的操作风格存在差异
11-12 - 信号发送与处理_第3张图片

2. 信号发送与处理

2.1 信号处理

2.1.1 信号的默认处理

11-12 - 信号发送与处理_第4张图片

2.1.2 自定义信号处理

11-12 - 信号发送与处理_第5张图片

#include 
#include 
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
sighandler_t sysv_signal(int signum, sighandler_t handler);
sighandler_t bsd_signal(int signum, sighandler_t handler);

2.1.3 信号处理示例

11-12 - 信号发送与处理_第6张图片

2.2 信号发送

2.2.1 自定义信号发送

11-12 - 信号发送与处理_第7张图片

#include 
#include 
int kill(pid_t pid, int sig); 
int raise(int sig); // 信号处理完毕后返回

标准信号是Unix系统中的信号,编号范围从1到31
实时信号是Linux独有的信号,编号范围从32到64

  • kill和raise是用来发送信号的:
    • raise把信号发送给(进程)自身
    • kill把信号发送给进程或进程组
      • pid > 0,将信号传给进程识别码为pid 的进程。
      • pid == 0,将信号传给和目前进程相同进程组的所有进程
      • pid == -1,将信号广播传送给系统内所有的进程
      • pid < 0,将信号传给进程组识别码为pid绝对值的所有进程

2.2.2 信号发送示例

11-12 - 信号发送与处理_第8张图片

2.3 编程实验:信号发送与处理

【参看链接】:11-12 - 信号发送与处理 / 11 / 00信号处理 / main.c

11-12 - 信号发送与处理_第9张图片
11-12 - 信号发送与处理_第10张图片

【参看链接】:11-12 - 信号发送与处理 / 11 / 01信号发送

11-12 - 信号发送与处理_第11张图片
11-12 - 信号发送与处理_第12张图片

3. 信号处理三大特性

3.1 问题

  • 三种注册信号与处理函数的方法有什么区别?

3.2 信号的OneShot 性

  • System V风格的signal函数,注册的 信号处理是 一次性的
    • 进程收到信号后,调用由signal注册的处理函数
    • 处理函数一旦执行之后,进程通过默认的方式处理后续相同信号
    • 如果想要重复触发,那么必须再次调用signal注册处理函数
  • BSD风格的signal函数不存在OneShot,能够自动反复触发处理函数的调用
  • 默认的signal函数和BSD风格的signal函数一致

3.3 信号的自身屏蔽特性

  • 在信号处理函数执行期间(还未处理结束),很可能再次收到当前信号
    即:处理 A 信号的时候,再次收到 A 信号

  • 对于 System V 风格的 signal 函数,会引起信号处理函数的重入
    即:调用处理函数的过程中,再次收到同个信号触发信号处理函数的调用
    11-12 - 信号发送与处理_第13张图片

  • 在注册信号处理函数时:

    • System V风格的signal不屏蔽任何信号
    • BSD风格的signal会屏蔽当前注册的信号,即:再次收到同个信号时,等待第一次收到的信号的处理函数执行完之后,再触发第二次的信号处理函数的调用
  • 思考:BSD风格的signal函数,处理A信号期间,如果收到B信号会发生什么?

3.4 系统调用重启特性

  • 系统调用期间,可能收到信号,此时进程必须从系统调用中返回
  • 对于执行时间较长的系统调用( write / read),被信号中断的可能性很大
  • 如果希望信号处理之后,被中断的系统调用能够重启,则:
    可以通过条件errno == EINTR判断手动重启系统调用
  • 系统调用重启示例代码(wait)

11-12 - 信号发送与处理_第14张图片

  • 系统调用重启特性:
    • System V 风格的 signal 函数:(手工重启)
      系统调用被信号中断后,直接返回 -1,并且 errno == EINTR
    • BSD 风格的 signal 函数:(自动重启)
      系统调用被中断,内核在信号处理函数结束后,自动重启系统调用

3.5 默认signal函数的特性

对于大多数的Linux发行版本来说,默认signal函数的行为和BSD风格的signal函数一致

3.6 编程实验:三种特性实验

3.6.1 oneshot 实验

【参看链接】:11-12 - 信号发送与处理 / 12 / 00oneshot

11-12 - 信号发送与处理_第15张图片
11-12 - 信号发送与处理_第16张图片

3.6.2 信号的自身屏蔽特性(信号重入)实验

【参看链接】:11-12 - 信号发送与处理 / 12 / 01信号重入

11-12 - 信号发送与处理_第17张图片
11-12 - 信号发送与处理_第18张图片
11-12 - 信号发送与处理_第19张图片

3.6.3 系统调用重启实验

【参看链接】:11-12 - 信号发送与处理 / 12 / 02系统调用重启

11-12 - 信号发送与处理_第20张图片
11-12 - 信号发送与处理_第21张图片

3.6.4 处理A收到B

  • BSD风格的signal函数,处理A信号期间,如果收到B信号会发生什么?

【参看链接】:11-12 - 信号发送与处理 / 12 / 03bsd_处理A时再收到B

11-12 - 信号发送与处理_第22张图片
11-12 - 信号发送与处理_第23张图片

3.7 注意事项

  • 并非所有的系统调用对信号中断都表现同样的行为
    • 一些系统调用支持信号中断后自动重启
      read() , write() , wait() , waitpid() , ioctl() , …
    • 一些系统调用完全不支持信号中断后自动重启
      poll() , select() , usleep() , …

3.8 小结

  • 三种方法的区别:

11-12 - 信号发送与处理_第24张图片

在信号处理上,Linux系统更接近BSD风格的操作;默认的signal函数在不同的Linux发行版上语义可能不同,从代码移植性角度,避免直接使用signal(…)函数。

4. 初探现代信号处理

4.1 现代信号处理注册函数

11-12 - 信号发送与处理_第25张图片

4.1.1 现代信号处理语义分析

11-12 - 信号发送与处理_第26张图片

  • 信号屏蔽与标记:
    • sigset_t sa_mask
      • 信号屏蔽:sa_mask = SIGHUP | SIGINT | SIGUSR1;
      • 注意:并不是所有信号都可以被屏蔽,如:SIGKILL , SIGSTOP
    • int sa_flags
      • 信号特性:sa_flags = SA_ONESHOT | SA_RESTART;
      • 特殊特性(SA_SIGINFO),信号处理时能够收到额外的附加信息

4.1.2 信号状态小知识

  • 信号 产生
    • 信号来源,如:SI_KERNEL , SI_USER , SI_TIMER , …
  • 信号 未决
    • 从信号产生到信号被进程接受的状态(处于未决状态的信号必然已存在)
  • 信号 递达
    • 信号送达进程,被进程接收(忽略,默认处理,自定义处理

4.1.3 信号屏蔽 vs 信号阻塞

  • 信号屏蔽 ==> 信号未决
    • 信号处理函数执行期间,被屏蔽的信号不会被递送给进程(针对多个信号)
    • sa_mask = SIGHUP | SIGINT | SIGUSR1;
  • 信号阻塞 ==> 信号未决
    • 信号处理函数执行期间,当前信号不会递送给进程(当前信号)
    • act.sa_flags = SA_RESTART | SA_NODEFER;

11-12 - 信号发送与处理_第27张图片

4.2 现代信号处理注册示例

11-12 - 信号发送与处理_第28张图片

4.3 编程实验:现代信号处理

【参看链接】:11-12 - 信号发送与处理 / 12 / 04现代信号处理

4.3.1 oneshot

11-12 - 信号发送与处理_第29张图片
11-12 - 信号发送与处理_第30张图片
11-12 - 信号发送与处理_第31张图片
11-12 - 信号发送与处理_第32张图片

4.3.2 屏蔽&阻塞相关

11-12 - 信号发送与处理_第33张图片
11-12 - 信号发送与处理_第34张图片
11-12 - 信号发送与处理_第35张图片
11-12 - 信号发送与处理_第36张图片

4.3.3 系统调用重启

11-12 - 信号发送与处理_第37张图片
11-12 - 信号发送与处理_第38张图片
11-12 - 信号发送与处理_第39张图片
11-12 - 信号发送与处理_第40张图片

4.3.4 处理A收到B

11-12 - 信号发送与处理_第41张图片
11-12 - 信号发送与处理_第42张图片
11-12 - 信号发送与处理_第43张图片
11-12 - 信号发送与处理_第44张图片

4.3.5 signal实际调用sigaction

11-12 - 信号发送与处理_第45张图片
11-12 - 信号发送与处理_第46张图片

5. 现代信号发送与处理

5.1 sigqueue

11-12 - 信号发送与处理_第47张图片

  • sigqueue(…)的黄金搭档是sigaction(…)
  • sa_flags设置 SA_SIGINFO 标志位,可使用 三参数 信号处理函数

11-12 - 信号发送与处理_第48张图片

5.2 现代信号处理函数的关键参数

11-12 - 信号发送与处理_第49张图片

5.3 现代信号发送处理示例

11-12 - 信号发送与处理_第50张图片

5.4 编程实验:现代信号发送

【参看链接】:11-12 - 信号发送与处理 / 12 / 05现代信号发送

11-12 - 信号发送与处理_第51张图片

11-12 - 信号发送与处理_第52张图片

5.5 思考

  • 利用信号搞进程间通信靠谱吗???

你可能感兴趣的:(Linux系统编程训练营,linux,Linux系统编程,unix,c语言,shell)