一文吃透Linux信号:产生、保存、捕捉、可重入、volatile与SIGCHLD
信号是Linux/Unix系统中用于进程间通信或操作的一种异步通知机制。它相当于软件层次上的“中断”,告知进程某个事件发生了。例如,你在终端按下Ctrl+C会向前台进程发送SIGINT信号,默认终止进程。信号贯穿整个Linux编程,理解它是成为高手的必经之路。
信号可以由多种方式产生:系统闹钟:调用alarm()函数设定定时器,到期后内核向进程发送SIGALRM信号。键盘事件:如Ctrl+C产生SIGINT,Ctrl+\产生SIGQUIT。硬件异常:除零、段错误等,硬件检测到后由内核发送对应信号。软件条件:如管道读端关闭时写管道产生SIGPIPE。kill函数:进程调用kill(pid, sig)向其他进程发送信号。
每个进程都有两个信号集:未决信号集和阻塞信号集,它们用sigset_t位图表示。未决集记录已经产生但尚未被处理(即还在等待)的信号;阻塞集则指定哪些信号被屏蔽(即使产生也不会递达)。我们可以通过sigprocmask()等函数操作阻塞集,从而实现信号的精准控制。
信号捕捉就是让进程按自定义的方式处理信号。使用signal()或sigaction()注册处理函数。当信号递达时,内核会调用该函数,执行完后可能返回主程序。注意,SIGKILL和SIGSTOP不能被捕捉或忽略。示例如下:
void handler(int sig) { write(1, "get signal", 12); }signal(SIGINT, handler); 当信号产生后,内核会在进程从内核态返回用户态时检查未决信号集,若存在未阻塞信号,则执行相应的处理函数。处理函数在用户态运行,但返回时会通过特殊的sigreturn再次进入内核以恢复上下文。这个流程涉及多次用户态与内核态的切换,理解它有助于优化高性能服务器。
可重入函数是指可以被多个执行流(如信号处理函数和主程序)同时调用而不会破坏数据的函数。标准库中很多函数是不可重入的,因为它们使用了静态数据或全局变量。在信号处理函数中,必须调用可重入函数(如write),避免使用printf等。否则可能导致程序崩溃或数据错乱。
在多线程或信号处理场景中,如果全局变量被多个执行流修改,必须用volatile声明,防止编译器将其优化到寄存器中,导致其他执行流看不到变化。例如:volatile sig_atomic_t flag = 0;,这样信号处理函数修改flag后,主循环能立即感知。
当子进程终止、暂停或恢复时,内核会向父进程发送SIGCHLD信号。默认处理是忽略,但父进程可以捕捉该信号,在信号处理函数中调用waitpid()回收子进程资源,避免僵尸进程。这是高并发网络编程中常用的技巧。示例:
void chld_handler(int sig) { while(waitpid(-1, NULL, WNOHANG) > 0); }signal(SIGCHLD, chld_handler); 本文从信号的产生、保存、捕捉,到操作系统底层运行、可重入函数、volatile,最后以SIGCHLD收尾,覆盖了Linux进程信号的方方面面。理解这些概念,你就能写出更健壮、高效的Linux程序。希望这篇教程对你有帮助!
—— 原创教程,欢迎分享 ——
本文由主机测评网于2026-02-16发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/20260225373.html