Linux 信号

发布于 2017-10-29 11:50:26

信号系统综述

信号相当于面向事件编程的一个例子。在计算机中,硬件层面有硬件中断,软件有软件中断。在此要讲的信号属于软件中断。

在计算机科学中,信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

信号的种类

  • 按可靠性分
    • 不可靠信号
      • 把那些建立在早期机制上的信号叫做"不可靠信号”,信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号
      • 为什么这么叫
        • 进程每次处理信号后,就将对信号的响应设置为默认动作。导致下次对信号的错误处理。
        • 信号可能丢失
    • 可靠信号
      • 信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,这些信号支持排队
      • 信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()
  • 在时间维度上分
    • 非实时信号
      • 早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63)
      • 前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作
      • 非实时信号都不支持排队,都是不可靠信号
    • 实时信号
      • 后32个信号表示实时信号,等同于前面阐述的可靠信号
      • 实时信号都支持排队,都是可靠信号

进程对信号的响应

  • 三种响应方式
    • 忽略,不能忽略SIGKILLSIGSTOP
    • 捕捉信号,执行相应处理函数
    • 执行缺省动作
      • Linux对每种信号都规定了默认操作
      • 进程对实时信号的默认操作是进程终止

常见信号

参见下面链接,下面只列出常见信号

Ctrl-C (DEL in older Unixes): SIGINT(interrupt) 终止进程
Ctrl-Z: SIGTSTP(terminal stop) 暂停执行
Ctrl-\: SIGQUIT 进程终止并dump core
Ctrl-T: not supported on all Unixes; SIGINFO 让操作系统显示正在运行命令的信息

名称     默认动作     说明
SIGHUP   终止进程     终端线路挂断
SIGINT   终止进程     中断进程
SIGQUIT  建立CORE文件 终止进程,并且生成core文件
SIGILL   建立CORE文件     非法指令
SIGTRAP  建立CORE文件     跟踪自陷
SIGBUS   建立CORE文件     总线错误
SIGSEGV  建立CORE文件     段非法错误
SIGFPE   建立CORE文件     浮点异常
SIGIOT   建立CORE文件     执行I/O自陷
SIGKILL  终止进程     杀死进程
SIGPIPE  终止进程     向一个没有读进程的管道写数据
SIGALARM 终止进程     计时器到时
SIGTERM  终止进程     软件终止信号
SIGSTOP  停止进程     非终端来的停止信号
SIGTSTP  停止进程     终端来的停止信号
SIGCONT  忽略信号     继续执行一个停止的进程
SIGURG   忽略信号     I/O紧急信号
SIGIO    忽略信号     描述符上可以进行I/O
SIGCHLD  忽略信号     当子进程停止或退出时通知父进程
SIGTTOU  停止进程     后台进程写终端
SIGTTIN  停止进程     后台进程读终端
SIGXGPU  终止进程     CPU时限超时
SIGXFSZ  终止进程     文件长度过长
SIGWINCH 忽略信号     窗口大小发生变化
SIGPROF  终止进程     统计分布图用计时器到时
SIGUSR1  终止进程     用户定义信号1
SIGUSR2  终止进程     用户定义信号2
SIGVTALRM 终止进程     虚拟计时器到时

信号的发送

  • 信号发送函数
    • kill()
      • int kill(pid_t pid,int signo)
      • 信号的接收者
        • pid > 0 进程ID为pid的进程
        • pid = 0 同一个进程组的进程
        • pid < 0
          • pid = -1 除发送进程自身之外,所有进程ID>1的进程
          • pid <= -2 进程组ID为abs(pid)的所有进程
      • signo 信号值
        • 0 空信号
      • 成功返回0,否则返回-1
    • raise():向进程本身发送信号
    • sigqueue():主要针对实时信号,也支持非实时信号,支持传递额外的数据“联合数据结构”
    • alarm()
      • 专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间
      • 进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。
      • 返回上一个闹钟时间的剩余时间,或者返回0
    • setitimer()
      • 支持绝对时间、相对时间、程序运行时间三种定时器
    • abort()
      • 向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。
      • 该函数无返回值

信号的安装

  • signal()
  • sigation()

信号的阻塞

未决信号集 与信号阻塞有关的几个函数

#include <signal.h>
int sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));
int sigpending(sigset_t *set));
int sigsuspend(const sigset_t *mask));

信号的生命周期

信号的诞生 --> 信号在进程中注册 --> 信号在进程中注销 --> 信号处理函数执行完毕

注意事项

  • 考虑到程序的可移植性,应该尽量采用POSIX信号函数
  • 为了增强程序的稳定性,在信号处理函数中应使用可重入函数(可被多个任务调用)

结论

  1. 安装信号(推荐使用sigaction())
  2. 实现三参数信号处理函数,handler(int signal,struct siginfo *info, void *)
  3. 发送信号,推荐使用sigqueue()
comments powered by Disqus