Linux:何为信号?一文介绍信号的一生(产生、安装、等待、处理)

信号是操作系统中用于处理异步事件的机制,常用于进程间通信。默认的信号处理方式包括忽略、终止进程、生成核心转储等。通过signal和sigaction函数可以安装自定义的信号处理函数,实现更精确的控制。信号发送可通过kill、tkill、tgkill和raise函数,等待信号则有pause、sigsuspend、sigwait和sigwaitinfo等函数。信号在系统管理和调试中起到关键作用。
摘要由CSDN通过智能技术生成

何为信号

信号是一种软件中断,用来处理异步事件。内核递送这些异步事件到某个进程,告诉进程某个特殊事件发生了。这些异步事件,可能来自硬件,比如访问了非法的内存地址,或者除以0了;可能来自用户的输入,比如shell终端上用户在键盘上敲击了Ctrl+C;还可能来自另一个进程,甚至有些来自进程自身。

信号的本质是一种进程间的通信,一个进程向另一个进程发送信号,内核至少传递了信号值这个字段。实际上,通信的内容不止是信号值。

信号的本质是一种进程间的通信
进程之间约定好:如果发生了某件事情T(trigger),就向目标进程(destination process)发送某特定信号X,而目标进程看到X,就意识到T事件发生了,目标进程就会执行相应的动作A(action)。

接下来以配置文件改变为例,来描述整个过程。
很多应用都有配置文件,如果配置文件发生改变,需要通知进程重新加载配置。一般而言,程序会默认采用SIGHUP信号来通知目标进程重新加载配置文件。
在这里插入图片描述

信号的默认处理函数

当信号产生,内核将信号递送给进程后,进程会执行什么操作呢?
很多信号尤其是传统的信号,都会有默认的信号处理方式。如果我们不改变信号的处理函数,那么收到信号之后,就会执行默认的操作。
信号的默认操作有以下几种:

  • 显式地忽略信号(Ignore):即内核将会丢弃该信号,信号不会对目标进程产生任何影响。 ·
  • 终止进程(Terminate):很多信号的默认处理是终止进程,即将进程杀死。
  • 生成核心转储文件并终止进程(Core):进程被杀死,并且产生核心转储文件。核心转储文件记录了进程死亡现场的信息。用户可以使用核心转储 文件来调试,分析进程死亡的原因。
  • 停止进程(Stop):停止进程不同于终止进程,终止进程是进程已经死亡,但是停止进程仅仅是使进程暂停,将进程的状态设置成 TASK_STOPPED,一旦收到恢复执行的信号,进程还可以继续执行。
  • 恢复进程的执行(Continue):和停止进程相对应,某些信号可以使进程恢复执行。

信号的这些默认行为是非常有用的。比如停止行为和恢复执行。系统可能有一些备份的工作,这些工作优先级并不高,但是却消耗了大量 的I/O资源,甚至是CPU资源(比如需要先压缩再备份)。这样的工作一般是在夜深人静,业务稀少的时候进行的。在业务比较繁忙的情况下, 如果备份工作还在进行,则可能会影响到业务。这时候停止和恢复就非常有用了。在业务繁忙之前,可以通过SIGSTOP信号将备份进程暂停, 在几乎没有什么业务的时候,通过SIGCONT信号使备份进程恢复执行。

很多信号产生核心转储文件也是非常有意义的。一般而言,程序出错才会导致SIGSEGV、SIGBUS、SIGFPE、SIGILL及SIGABRT等信号的产生。生成的核心转储文件保留了进程死亡的现场,提供了大量的信息供程序员调试、分析错误产生的原因。



信号的安装(注册)

很多情况下,默认的信号处理函数,可能并不能满足实际的需要,这时需要修改信号的信号处理函数。信号发生时,不执行默认的信号处理函数,改而执行用户自定义的信号处理函数。
为信号指定新的信号处理函数的动作,被称为信号的安装
glibc提供了signal函数sigaction函数来完成信号的安装。signal出现得比较早,接口也比较简单,sigaction则提供了精确的控制。

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);

#include <signal.h>
int sigaction( int signo, const struct sigaction *restrict act, struct sigaction *restrict oldact);

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_mask就是信号处理函数执行期间的屏蔽信号集。
bsd_signal函数为SIGINT安装处理函数时,内核会自动将SIGINT添加入屏蔽信号集,在SIGINT信号处理函数执行期间,SIGINT信号不会被递送给进程。但是,也仅仅是SIGINT,如果执行SIGINT信号处理函数期间,需要屏蔽SIGHUP、SIGUSR1等其他信号,bsd_signal函数就无能为力。而sigaction函数可以使用sa_mask来实现。

struct sigaction sa;
sa.sa_mask = SIGHUP|SIGUSR1|SIGINT;

sigaction函数靠sa_flags的参数提供更精确的控制。
(1)SA_NOCLDSTOP
这个标志位只用于SIGCHLD信号。SA_NOCLDSTOP标志位是用来控制子进程暂停和子进程恢复的。即父进程为SIGCHLD设置了SA_NOCLDSTOP标志位,那么子进程停止和子进程恢复这两件事情,就无须向父进程发送SIGCHLD信号了。
(2)SA_NOCLDWAIT
这个标志位只用于SIGCHLD信号。SA_NOCLDWAIT标志位是用来控制子进程终止的。即父进程为SIGCHLD设置了SA_NOCLDWAIT标志位,那么子进程退出时,就不会进入僵尸状态,而是直接自行了断。
(3)SA_ONESHOT和SA_RESETHAND
表示信号处理函数是一次性的,信号递送出去之后,信号处理函数便恢复成默认值SIG_DFL。
(4)SA_NODEFER和SA_NOMASK
在信号处理函数执行期间,不阻塞当前信号。
(5)SA_RESTART
如果系统调用被信号中断,则不返回错误,而是自动重启系统调用。
(6)SA_SIGINFO
这个标志位表示信号发送者会提供关于信号额外的信息。


信号的发送

kill、tkill和tgkill函数
  • kill函数的作用是发送信号。kill函数利用 参数pid 不仅可以向特定进程发送信号,也可以向特定进程组发送信号。
  • 如何向线程发送信号?
  • tkill 和 tgkill 两个系统调用来向某个线程发送信号。与 tkill 相比,tgkill接口更加安全。
raise函数
  • raise函数的作用是向进程自身发送信号的接口。
  • 对于单线程的程序而言,就相当于 kill(getpid(),sig)。
  • 对于多线程的程序而言,就相当于 pthread_kill(pthread_self(),sig)。
  • 值得注意的是,信号处理函数执行完毕之后,raise才能返回。

等待信号

pause函数
sigsuspend函数
sigwait函数和sigwaitinfo函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值