AI智能
改变未来

linux 信号与处理


一、linux信号是什么

  1. 基本概念
    信号是事件发生时对进程的通知机制,也就是所谓的软件中断。信号和硬件的中断类似,是软件层对中断机制的模拟,在多数情况下是无法预测信号产生的时间,所以软件层提供了一种处理异步事件的方法。

二、 信号来源

信号的来源分为硬件来源和软件来源。

  1. 硬件来源:
  • 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程,如除数为0、无效的内存引用等。
  • 用户按终端键,引起终端产生的信号(比如Ctrl + C键产生SIGINT)。
  1. 软件来源:
  • 用户通过指令杀死,如kill指令。
  • 发生软件事件, 如程序执行raise, alarm、setitimer、sigqueue等函数。

三、 信号处理

信号通常是发送给对应的进程,当信号到达后,该进程需要做出相应的处理措施,通常进程会视具体信号执行相应的操作,有三种操作方式。

  1. 忽略信号:
      信号到达后、直接忽略,就好像是没有出该信号,信号对该进程不会产生任何影响。事实上,大多数信号都可以使用这种方式进行处理,但有两种信号却决不能被忽略,分别是SIGKILL 和 SIGSTOP。
  2. 捕获信号:
      当信号到达进程后,执行signal()绑定好的信号处理函数。
  3. 执行系统默认操作:
      进程不对该信号事件作出处理,而是交由系统进行处理,每一种信号都会有其对应的系统默认的处理方式。

四、常见信号

在linux系统中通过

kill -l

命令可以查看到相应的信号。信号编号是从 1 开始,不存在编号为 0 的信号,事实上 kill()函数对信号编号 0 有着特殊的应用。

注意:括号\” ) \”前面的数字对应该信号的编号,编号 1~31 所对应的是不可靠信号,编号 34~64 对应的是可靠信号,从图中可知,可靠信号并没有一个具体对应的名字,而是使用了 SIGRTMIN+N 或 SIGRTMAXN 的方式来表示。其中32和33空缺。
不可靠信号表

6
名称 解释 默认动作
1 SIGHUP 挂起
2 SIGINT 中断
3 SIGQUIT 退出
4 SIGILL 非法指令
5 SIGTRAP 断点或陷阱指令
SIGABRT abort发出的信号
7 SIGBUS 非法内存访问
8 SIGFPE 浮点异常
9 SIGKILL kill信号 不能被忽略、处理和阻塞
10 SIGUSR1 用户信号1
11 SIGSEGV 无效内存访问
12 SIGUSR2 用户信号2
13 SIGPIPE 管道破损,没有读端的管道写数据
14 SIGALRM alarm发出的信号
15 SIGTERM 终止信号
16 SIGSTKFLT 栈溢出
17 SIGCHLD 子进程退出 默认忽略
18 SIGCONT 进程继续
19 SIGSTOP 进程停止 不能被忽略、处理和阻塞
20 SIGTSTP 进程停止
21 SIGTTIN 进程停止,后台进程从终端读数据时
22 SIGTTOU 进程停止,后台进程想终端写数据时
23 SIGURG I/O有紧急数据到达当前进程 默认忽略
24 SIGXCPU 进程的CPU时间片到期
25 SIGXFSZ 文件大小的超出上限
26 SIGVTALRM 虚拟时钟超时
27 SIGPROF profile时钟超时
28 SIGWINCH 窗口大小改变 默认忽略
29 SIGIO I/O相关
30 SIGPWR 关机 默认忽略
31 SIGSYS 系统调用异常

五、信号处理

  1. signal()
    \”signal.h\”信号处理库提供了signal函数,用来捕获突发事件。以下是 signal() 函数的语法ads。
typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
  • signum:可使用信号名(宏)或信号的数字编号,建议使用信号名。
  • handler:参数 handler 既可以设置为用户自定义的函数,也可以设置为 SIG_IGN 或 SIG_DFL,SIG_IGN 表示此进程需要忽略该信号,SIG_DFL 则表示设置为系统默认操作。
  1. raise()
    有时进程需要向自身发送信号,raise()函数可用于实现这一要求.
int raise(int sig);
  • sig:需要发送的信号。
  1. sigaction()
    除了signal()之外,sigaction()系统调用是设置信号处理方式的另一选择,虽然 signal()函数简单好用,而 sigaction()更为复杂,但作为回报,sigaction()也更具灵活性以及移植性。
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum:需要设置的信号,除了 SIGKILL 信号和 SIGSTOP 信号之外的任何信号。
  • act:参数 act 不为 NULL,则表示需要为信号设置新的处理方式;如果参数 act 为 NULL,则表示无需改变信号当前的处理方式
  • oldact:参数oldact 不为 NULL,则会将信号之前的处理方式等信息通过参数 oldact 返回出来;如果无意获取此类信息,那么可将该参数设置为 NULL。
  • 返回值:成功返回 0;失败将返回-1,并设置 errno。
    struct sigaction 结构体
struct sigaction {void     (*sa_handler)(int);void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask;int        sa_flags;void     (*sa_restorer)(void);};
  • sa_handler:指定信号处理函数,与 signal()函数的 handler 参数相同。
  • sa_sigaction:也用于指定信号处理函数,这是一个替代的信号处理函数。
  • sa_mask:参数 sa_mask 定义了一组信号。
  • sa_restorer:该成员已过时,不要再使用了。
  • sa_flags:参数 sa_flags 指定了一组标志,这些标志用于控制信号的处理过程。
  1. kill()
    kill()系统调用可将信号发送给指定的进程或进程组中的每一个进程。
int kill(pid_t pid, int sig);
  • pid:参数 pid 为正数的情况下,用于指定接收此信号的进程 pid。
  • sig:参数 sig 指定需要发送的信号,也可设置为 0,如果参数 sig 设置为 0 则表示不发送信号,但任执行错误检查,这通常可用于检查参数 pid 指定的进程是否存在。
  • 返回值:成功返回 0;失败将返回-1,并设置 errno。
  1. alarm()
    使用 alarm()函数可以设置一个定时器(闹钟),当定时器定时时间到时,内核会向进程发送 SIGALRM信号。
  2. unsigned int alarm(unsigned int seconds);
    • seconds:设置定时时间,以秒为单位;如果参数 seconds 等于 0,则表示取消之前设置的 alarm 闹钟。
    • 返回值:如果在调用 alarm()时,之前已经为该进程设置了 alarm 闹钟还没有超时,则该闹钟的剩余值作为本次 alarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回 0。
  3. pause()
    pause()系统调用可以使得进程暂停运行、进入休眠状态,直到进程捕获到一个信号为止,只有执行了信号处理函数并从其返回时,pause()才返回,在这种情况下,pause()返回-1,并且将 errno 设置为EINTR。
int pause(void);
  1. 使用案例
    demo1
#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>void signal_handler(int signum){printf(\"Interrupt signal (%d) received. \\n\", signum);switch(signum){case SIGINT:printf(\"ctrl + c \\n\");exit(signum);break;case SIGQUIT:printf(\"ctrl + \\\\ \\n\");exit(signum);}}int main(int argc, char *argv[]) {int i = 0;//注册信号与信号处理程序signal(SIGINT, signal_handler);signal(SIGQUIT, signal_handler);while(1){printf(\"Going to sleep....\\n\");if (i > 5){raise( SIGINT);}sleep(1);i++;}return 0;}

参考文献

Linux信号(signal)机制:http://gityuan.com/2015/12/20/signal/
linux 信号及处理过程详解:https://blog.csdn.net/u010765526/article/details/80085895
linux kill信号详解:https://www.geek-share.com/detail/2707194922.html
《正点原子应用编程指南》

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » linux 信号与处理