Linux信号是一种进程间通信机制,用于通知进程发生了某种事件。信号可以被内核、其他进程或进程自身发送。当进程接收到信号时,它可以采取以下三种处理方式之一:
Linux系统提供了多种标准信号,其中与进程终止相关的常见信号包括:
在Linux系统中,当一个进程终止时,它不会立即从系统中完全消失。进程会进入"僵尸"(Zombie)状态,直到其父进程通过wait()或waitpid()系统调用读取其退出状态。这种机制允许父进程了解子进程的终止状态。
如果父进程没有正确回收子进程,会导致:
最常用的进程终止方式是发送SIGTERM信号:
|
1 |
kill -15 PID |
或者更简洁地:
|
1 |
kill PID |
这种方式允许进程执行清理操作后再退出。
当进程不响应SIGTERM时,可以使用SIGKILL强制终止:
|
1 |
kill -9 PID |
注意:这不会给进程执行清理的机会,可能导致资源泄漏。
通过pkill或killall可以按名称终止进程:
|
1 2 |
pkill process_name killall process_name |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h>
void cleanup(int signum) { printf("Received signal %d, performing cleanup...\n", signum); // 执行资源释放等清理操作 exit(0); }
int main() { // 注册信号处理函数 signal(SIGTERM, cleanup); signal(SIGINT, cleanup);
while(1) { printf("Running...\n"); sleep(1); }
return 0; } |
sigaction提供了更强大和可靠的信号处理接口:
|
1 2 3 4 5 6 7 8 9 |
struct sigaction sa; sa.sa_handler = cleanup; sigemptyset(&sa.sa_mask); sa.sa_flags = 0;
if (sigaction(SIGTERM, &sa, NULL) == -1) { perror("sigaction"); exit(1); } |
在多线程环境中,可以使用sigprocmask或pthread_sigmask来控制信号的接收:
|
1 2 3 4 |
sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGTERM); pthread_sigmask(SIG_BLOCK, &mask, NULL); |
父进程可以通过信号来监控子进程状态:
|
1 2 3 4 5 6 7 8 9 |
pid_t pid = fork(); if (pid == 0) { // 子进程代码 } else { // 父进程等待子进程结束 int status; waitpid(pid, &status, 0); printf("Child exited with status %d\n", WEXITSTATUS(status)); } |
守护进程通常需要处理以下信号:
在init脚本中合理使用信号:
|
1 2 3 4 5 6 7 8 9 10 11 |
case "$1" in start) start_service ;; stop) kill -TERM `cat /var/run/service.pid` ;; restart) kill -HUP `cat /var/run/service.pid` ;; esac |
在Docker等容器环境中,信号传递尤为重要:
|
1 2 |
STOPSIGNAL SIGTERM CMD ["/usr/bin/your_app"] |
解决方案:
排查步骤:
避免在信号处理函数中执行可能阻塞的操作,如:
Linux信号机制为进程管理提供了强大而灵活的工具。通过合理使用信号,我们可以实现进程的优雅终止和资源回收,构建更健壮的系统和服务。理解信号的特性、掌握正确的使用方法,是每个Linux系统管理员和开发者必备的技能。
在实际工作中,应当根据具体场景选择合适的信号和回收策略,在确保系统稳定性的同时,提供良好的用户体验和服务质量。