当前位置: 首页 > news >正文

UNIX下C语言编程与实践37-UNIX 信号:概念、产生原因与处理方式

从信号本质到实战处理,掌握 UNIX 进程间异步通信的核心机制

一、核心概念:什么是 UNIX 信号?

在 UNIX 系统中,信号(Signal)是一种轻量级的进程间异步通信机制,其核心定位是“向进程传递事件通知”。它像“中断请求”一样,可在进程执行任意代码时异步触发——无论进程正在执行主逻辑、调用函数还是等待 I/O,一旦收到信号,系统会暂停当前执行流程,转而去处理信号事件,处理完成后再恢复原流程(或根据信号类型终止进程)。

信号的本质是“整数标识的事件”:每个信号对应一个唯一的整数(如 SIGINT 对应 2、SIGKILL 对应 9),系统通过传递这个整数,告知进程“发生了某个特定事件”。例如,用户按下 Ctrl+C 时,系统会向当前终端的前台进程发送 SIGINT 信号,通知进程“用户请求中断”。

信号的“异步性”与“不可预测性”

信号的触发时机是不可预测的——进程无法提前知道信号会在哪个指令周期到来,可能在执行循环、调用函数甚至修改共享数据时被中断。这种异步特性决定了信号处理函数需遵循严格的编程规范(如避免重入、使用可重入函数),否则易导致数据错乱或程序崩溃。

二、信号的产生原因:三类触发场景

UNIX 信号的产生源于系统或用户的事件通知需求,可归纳为三类核心场景:程序错误、外部事件和显式请求。每种场景对应不同的信号类型,且有固定的触发逻辑。

1. 程序错误:进程自身异常触发

当进程执行非法操作时,内核会检测到错误并向进程发送信号,强制进程处理错误(默认通常为终止进程并生成核心转储文件)。这类信号是系统对进程异常行为的“纠错通知”。

典型场景与对应信号

  • SIGFPE(信号 8):算术错误,如除数为 0、浮点运算溢出;
  • SIGSEGV(信号 11):段错误,如访问无效内存地址(空指针、数组越界);
  • SIGBUS(信号 7):总线错误,如访问未对齐的内存地址(如在 32 位系统中访问奇数地址的 4 字节数据);
  • SIGILL(信号 4):非法指令,如执行无效的机器指令(程序代码损坏、指令集不兼容)。

特点:这类信号由内核主动触发,进程无法避免,只能通过信号处理函数捕获并尝试恢复(如日志记录后优雅退出),但多数错误(如段错误)无法恢复,最终仍需终止进程。

2. 外部事件:系统或硬件触发

系统时钟、硬件状态变化或终端操作等外部事件发生时,内核会向相关进程发送信号,告知进程“外部环境发生变化”。这类信号是进程与系统交互的“事件通知”。

典型场景与对应信号

  • SIGINT(信号 2):终端中断,用户按下 Ctrl+C,请求终止前台进程;
  • SIGTSTP(信号 20):终端暂停,用户按下 Ctrl+Z,请求暂停前台进程;
  • SIGALRM(信号 14):定时器到期,进程通过 alarm 函数设置的定时器超时;
  • SIGHUP(信号 1):终端挂起,如终端窗口关闭,通知关联进程“终端已断开”;
  • SIGPIPE(信号 13):管道断裂,向已关闭读端的管道写入数据时触发(如客户端断开连接后,服务器仍向套接字写入数据)。

特点:这类信号由外部环境触发,进程可通过信号处理函数灵活响应(如 SIGINT 捕获后清理资源再退出,SIGALRM 捕获后执行定时任务)。

3. 显式请求:用户或进程主动发送

用户通过命令(如 kill)或进程通过系统调用(如 killpthread_kill)主动向目标进程发送信号,实现进程间的“主动通知”。这类信号是进程间异步通信的核心方式。

典型场景与对应信号

  • SIGTERM(信号 15):终止请求,用户通过 kill 进程PID 发送,请求进程优雅退出(默认信号);
  • SIGKILL(信号 9):强制终止,用户通过 kill -9 进程PID 发送,强制进程立即终止(不可捕获、不可忽略);
  • SIGUSR1(信号 10)/ SIGUSR2(信号 12):用户自定义信号,进程可根据业务需求定义其含义(如 SIGUSR1 触发日志轮转,SIGUSR2 触发配置重载);
  • SIGCHLD(信号 17):子进程状态变化,子进程终止或暂停时,内核向父进程发送,通知父进程回收子进程资源。

特点:这类信号由用户或进程主动控制,是进程间协作的关键(如父进程发送 SIGUSR1 通知子进程更新配置),灵活性最高。

三、信号的处理方式:三种核心响应策略

当进程收到信号时,内核会根据进程对该信号的“处理动作”进行响应。UNIX 系统定义了三种标准处理方式,进程可通过系统调用修改信号的处理动作(除 SIGKILL 和 SIGSTOP,这两种信号不可捕获、不可忽略)。

1. 系统默认处理(Default Action)

定义:若进程未显式设置信号的处理动作,内核会执行该信号的默认处理逻辑。不同信号的默认处理动作由 UNIX 标准规定,主要分为五类:

  • 终止(Terminate):终止进程,如 SIGINTSIGTERMSIGFPE
  • 终止并生成核心转储(Core Dump):终止进程并生成 core 文件(包含进程崩溃时的内存快照),用于调试,如 SIGSEGVSIGBUS
  • 暂停(Stop):暂停进程的执行,可通过 SIGCONT 恢复,如 SIGTSTPSIGSTOP
  • 恢复(Continue):恢复被暂停的进程,如 SIGCONT
  • 忽略(Ignore):对信号不做任何处理,如 SIGCHLD(部分系统默认忽略)、SIGURG(紧急数据到达信号)。

示例:当进程未处理 SIGINT 时,用户按下 Ctrl+C,进程会被默认终止;若未处理 SIGSEGV,进程会终止并生成 core 文件。

2. 忽略信号(Ignore)

定义:进程显式设置信号的处理动作为“忽略”,内核收到该信号后会直接丢弃,不中断进程的执行流程。

实现方式:通过 signal 或 sigaction 函数,将信号的处理函数设置为 SIG_IGN(忽略标识符)。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main() {// 设置忽略 SIGINT 信号(Ctrl+C 无效)if (signal(SIGINT, SIG_IGN) == SIG_ERR) {perror("signal 设置忽略 SIGINT 失败");return 1;}printf("已忽略 SIGINT 信号,按下 Ctrl+C 无效(10 秒后退出)\n");sleep(10);printf("程序正常退出\n");return 0;
}

已忽略 SIGINT 信号,按下 Ctrl+C 无效(10 秒后退出) ^C^C^C // 按下 Ctrl+C 无反应 程序正常退出

注意事项

  • SIGKILL(9)和 SIGSTOP(19)不可忽略,即使设置为 SIG_IGN,内核仍会执行默认处理(强制终止/暂停);
  • 忽略 SIGCHLD 信号可让内核自动回收子进程(部分系统支持),避免僵死进程,但无法获取子进程的退出状态。

3. 捕获信号(Catch)

定义:进程显式注册“信号处理函数”,当收到信号时,内核会暂停进程当前执行流程,转而去执行该处理函数,处理完成后再恢复原流程(或根据处理函数逻辑退出)。这是最灵活的信号处理方式,可实现自定义业务逻辑。

实现方式:通过 signal 或 sigaction 函数,将信号的处理函数设置为自定义函数(需符合 void (*handler)(int) 类型,参数为信号编号)。

以下是规范格式后的代码,保持原内容不变,仅做缩进调整:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>// 自定义 SIGINT 信号处理函数
void sigint_handler(int sig) {printf("\n捕获到 SIGINT 信号(编号:%d),正在清理资源...\n", sig);// 模拟资源清理(如关闭文件、释放内存)sleep(2);printf("资源清理完成,程序优雅退出\n");exit(EXIT_SUCCESS);
}int main() {// 注册 SIGINT 信号处理函数if (signal(SIGINT, sigint_handler) == SIG_ERR) {perror("signal 注册 SIGINT 处理函数失败");return 1;}printf("已注册 SIGINT 处理函数,按下 Ctrl+C 触发优雅退出(或等待 10 秒)\n");sleep(10);printf("程序正常退出\n");return 0;
}

已注册 SIGINT 处理函数,按下 Ctrl+C 触发优雅退出(或等待 10 秒) ^C 捕获到 SIGINT 信号(编号:2),正在清理资源... 资源清理完成,程序优雅退出

关键注意

  • 信号处理函数需简洁、高效,避免执行耗时操作(如 sleepprintf 虽常用,但需注意重入问题);
  • 信号处理函数中应使用“可重入函数”(如 writeclose),避免使用“不可重入函数”(如 mallocfreeprintf),防止数据竞争;
  • 处理函数执行期间,内核会自动阻塞当前信号(避免同一信号嵌套触发),但其他信号仍可中断处理函数。

四、实战:信号的查看、发送与处理

通过系统命令和 C 语言程序,可直观地查看系统信号列表、发送信号给进程,以及实现自定义信号处理逻辑。以下是典型实战场景:

1. 查看系统中的所有信号:kill -l 命令

kill -ll 为 list 缩写)命令可列出 UNIX 系统支持的所有信号,包括信号编号和名称,是了解系统信号的基础工具:

查看所有信号(编号 1~64,不同系统数量可能不同)

kill -l

输出示例(关键信号标注)

1) SIGHUP  
2) SIGINT  
3) SIGQUIT  
4) SIGILL  
5) SIGTRAP  
6) SIGABRT  
7) SIGBUS  
8) SIGFPE  
9) SIGKILL  
10) SIGUSR1  
11) SIGSEGV  
12) SIGUSR2  
13) SIGPIPE  
14) SIGALRM  
15) SIGTERM  
16) SIGSTKFLT  
17) SIGCHLD  
18) SIGCONT  
19) SIGSTOP  
20) SIGTSTP  
21) SIGTTIN  
22) SIGTTOU  
23) SIGURG  
24) SIGXCPU  
25) SIGXFSZ  
26) SIGVTALRM  
27) SIGPROF  
28) SIGWINCH  
29) SIGIO  
30) SIGPWR  
31) SIGSYS  
34) SIGRTMIN  
35) SIGRTMIN+1  
36) SIGRTMIN+2  
37) SIGRTMIN+3  
...  
63) SIGRTMAX-1  
64) SIGRTMAX

查看指定信号的详细说明(如 SIGINT)

man 7 signal | grep -A 10 "SIGINT"

关键信号说明

  • SIGKILL(9):唯一不可捕获、不可忽略的终止信号,用于强制杀死无响应进程;
  • SIGSTOP(19):唯一不可捕获、不可忽略的暂停信号,用于强制暂停进程;
  • SIGUSR1(10)/ SIGUSR2(12):用户自定义信号,无默认业务含义,需进程自行定义;
  • SIGRTMIN(34)~ SIGRTMAX(64):实时信号(可靠信号),用于需要可靠传递的场景(如实时数据传输)。

2. 发送信号给进程:kill 与 killall 命令

用户可通过 kill 命令向指定 PID 的进程发送信号,或通过 killall 命令向指定名称的所有进程发送信号,实现对进程的控制(终止、暂停、自定义通知)。

发送默认信号(SIGTERM,15)终止进程(PID=1234)

kill 1234

发送 SIGKILL(9)强制终止进程(PID=1234)

kill -9 1234
# 等价于
kill -SIGKILL 1234

发送 SIGINT(2)给进程(模拟 Ctrl+C)

kill -2 1234
# 等价于
kill -SIGINT 1234

发送 SIGUSR1(10)给进程(触发自定义逻辑,如日志轮转)

kill -10 1234
# 等价于
kill -SIGUSR1 1234

发送信号给指定名称的所有进程(如终止所有 nginx 进程)

killall -9 nginx

查看进程的信号处理状态(如 PID=1234)

ps -o pid,sig,cmd 1234
# sig 列显示进程已阻塞的信号(无阻塞则为空)

示例:发送 SIGUSR1 给自定义进程(PID=1234),触发日志轮转

kill -10 1234
# 查看进程日志(验证信号处理)
cat /var/log/my_process.log
[2024-10-01 16:00:00] 捕获到 SIGUSR1 信号,开始日志轮转...
[2024-10-01 16:00:01] 日志轮转完成,新日志文件:/var/log/my_process_20241001.log

3. C 程序实战:信号的捕获与自定义处理

需求

编写 C 程序,实现以下功能:
1. 捕获 SIGINT(Ctrl+C)和 SIGTERM(kill 命令默认信号),执行“清理资源→日志记录→优雅退出”;
2. 捕获 SIGUSR1,触发日志轮转;
3. 忽略 SIGTSTP(Ctrl+Z),避免进程被暂停。

以下是规范格式后的代码,保持原内容不变并做缩进:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>#define LOG_FILE "/var/log/signal_demo.log"// 日志记录函数
void log_message(const char *msg) {FILE *fp = fopen(LOG_FILE, "a");if (fp == NULL) {perror("打开日志文件失败");return;}time_t now = time(NULL);struct tm *tm = localtime(&now);char time_str[64];strftime(time_str, sizeof(time_str), "[%Y-%m-%d %H:%M:%S]", tm);fprintf(fp, "%s %s\n", time_str, msg);fclose(fp);
}// SIGINT 和 SIGTERM 处理函数:优雅退出
void sig_term_handler(int sig) {char msg[128];snprintf(msg, sizeof(msg), "捕获到信号 %d(%s),开始清理资源...", sig, (sig == SIGINT) ? "SIGINT" : "SIGTERM");log_message(msg);// 模拟资源清理(如关闭文件、释放内存)sleep(2);log_message("资源清理完成,程序优雅退出");exit(EXIT_SUCCESS);
}// SIGUSR1 处理函数:日志轮转
void sig_usr1_handler(int sig) {log_message("捕获到 SIGUSR1 信号,触发日志轮转");// 模拟日志轮转(重命名旧日志,创建新日志)char old_log[128], new_log[128];time_t now = time(NULL);struct tm *tm = localtime(&now);strftime(new_log, sizeof(new_log), "/var/log/signal_demo_%Y%m%d_%H%M%S.log", tm);strcpy(old_log, LOG_FILE);rename(old_log, new_log);// 创建新的日志文件FILE *fp = fopen(LOG_FILE, "w");if (fp != NULL) {fclose(fp);}log_message("日志轮转完成,新日志文件:日志轮转完成,新日志文件:%s", new_log);
}int main() {// 1. 注册 SIGINT 和 SIGTERM 处理函数(共享同一个处理函数)if (signal(SIGINT, sig_term_handler) == SIG_ERR || signal(SIGTERM, sig_term_handler) == SIG_ERR) {perror("注册终止信号处理函数失败");log_message("注册终止信号处理函数失败,程序退出");return 1;}// 2. 注册 SIGUSR1 处理函数if (signal(SIGUSR1, sig_usr1_handler) == SIG_ERR) {perror("注册 SIGUSR1 处理函数失败");log_message("注册 SIGUSR1 处理函数失败,程序退出");return 1;}// 3. 忽略 SIGTSTP 信号(Ctrl+Z 无效)if (signal(SIGTSTP, SIG_IGN) == SIG_ERR) {perror("忽略 SIGTSTP 信号失败");log_message("忽略 SIGTSTP 信号失败");}// 记录程序启动日志char start_msg[128];snprintf(start_msg, sizeof(start_msg), "程序启动,PID = %d", getpid());log_message(start_msg);printf("程序启动,PID = %d\n", getpid());printf("功能说明:\n");printf("1. 按下 Ctrl+C 或执行 'kill %d' 触发优雅退出\n", getpid());printf("2. 执行 'kill -10 %d' 触发日志轮转\n", getpid());printf("3. 按下 Ctrl+Z 无效(已忽略 SIGTSTP)\n");// 模拟程序主逻辑(无限循环)while (1) {sleep(1);}return 0;
}

编译和运行指令

# 1. 编译程序
gcc signal_demo.c -o signal_demo# 2. 启动程序(后台运行)
sudo ./signal_demo &# 3. 发送 SIGUSR1 信号,触发日志轮转
sudo kill -10 $(pgrep signal_demo)# 4. 发送 SIGINT 信号,触发优雅退出
sudo kill -2 $(pgrep signal_demo)# 5. 查看日志,验证信号处理
cat /var/log/signal_demo.log

程序启动输出

[1] 1234  
程序启动,PID = 1234  

功能说明

  • 按下 Ctrl+C 或执行 kill 1234 触发优雅退出
  • 执行 kill -10 1234 触发日志轮转
  • 按下 Ctrl+Z 无效(已忽略 SIGTSTP

日志文件内容

[2024-10-01 16:30:00] 程序启动,PID = 1234  
[2024-10-01 16:30:10] 捕获到 SIGUSR1 信号,触发日志轮转  
[2024-10-01 16:30:10] 日志轮转完成,新日志文件:/var/log/signal_demo_20241001_163010.log  
[2024-10-01 16:30:20] 捕获到信号 2(SIGINT),开始清理资源...  
[2024-10-01 16:30:22] 资源清理完成,程序优雅退出  

五、信号的常见问题与解决方法

信号的异步特性和历史设计缺陷,导致在使用过程中易出现信号丢失、处理函数重入等问题。以下是高频问题及对应的解决方法:

常见问题问题现象原因分析解决方法
非可靠信号丢失短时间内多次发送同一非可靠信号(如 SIGINT、SIGUSR1),进程仅捕获到一次,其他信号丢失UNIX 信号分为“非可靠信号”(1~31 号)和“可靠信号”(34~64 号):
- 非可靠信号:内核不记录信号发送次数,若同一信号未处理时再次发送,会被合并为一次,导致丢失;
- 可靠信号:内核会记录信号发送次数,即使信号未处理,多次发送也不会丢失,进程会依次处理
1. 优先使用可靠信号(SIGRTMIN~SIGRTMAX),如用 SIGRTMIN 替代 SIGUSR1,确保信号不丢失;
2. 若必须使用非可靠信号,在信号处理函数中通过“状态标记”而非信号次数判断逻辑(如设置 flag=1,而非计数);
3. 示例:
#define MY_SIGNAL (SIGRTMIN + 1) // 使用可靠信号
信号处理函数重入问题信号处理函数执行期间,被其他信号中断,导致不可重入函数(如 malloc、printf)的数据结构损坏,程序崩溃1. 信号处理函数执行时,内核仅阻塞当前信号,其他信号仍可中断处理函数;
2. 若处理函数中调用不可重入函数(如 malloc 维护全局链表,被中断会导致链表错乱),会引发数据竞争;
3. 不可重入函数通常包含全局/静态变量、动态内存分配或 I/O 操作
1. 信号处理函数中仅使用“可重入函数”,如 write、close、_exit、sigaction 等(参考 man 7 signal 中的可重入函数列表);
2. 若需使用不可重入函数,通过“信号掩码”在处理函数执行期间阻塞其他信号,示例:
sigset_t mask; sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); // 阻塞所有信号
3. 复杂逻辑(如日志记录、内存分配)移到主流程,处理函数仅设置“事件标记”,主流程轮询标记并执行逻辑。
signal 函数的兼容性问题不同 UNIX 系统(如 Linux、BSD)中,signal 函数的行为不一致(如信号处理函数执行后是否恢复默认动作),导致程序跨平台运行异常signal 函数是早期 UNIX 接口,不同系统对其行为的实现存在差异:
- Linux 中,signal 注册的处理函数会持续生效(除非显式修改);
- BSD 中,信号处理函数执行一次后会自动恢复为默认动作;
- 这种差异导致程序在不同系统中行为不一致
1. 放弃使用 signal 函数,改用 POSIX 标准的 sigaction 函数,其行为在所有 UNIX 系统中一致;
2. sigaction 支持更灵活的配置(如设置信号掩码、是否自动恢复默认动作);
3. 示例(用 sigaction 注册信号处理函数):
struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL);
信号与 I/O 操作的冲突进程阻塞在 I/O 操作(如 read、accept)时,收到信号后 I/O 操作返回 -1,错误码为 EINTR,程序未处理导致 I/O 逻辑中断1. 大部分 I/O 操作(如 read、write、accept、select)是“可中断的”,收到信号后会立即返回,错误码设为 EINTR;
2. 若程序未检查 EINTR 错误码,会误判 I/O 操作失败,导致逻辑中断(如服务器 accept 被信号中断后退出循环)
1. I/O 操作后检查错误码,若为 EINTR,重新执行 I/O 操作,示例:
while ((n = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR); // 重试
2. 对不希望被中断的 I/O 操作,通过 sigaction 的 SA_RESTART 标志自动重启被中断的系统调用,示例:
sa.sa_flags |= SA_RESTART; // 自动重启可中断系统调用
3. 注意:并非所有系统调用都支持 SA_RESTART(如 select、poll 不支持),需手动重试。

六、拓展:可靠信号与 sigaction 函数

为解决非可靠信号的缺陷和 signal 函数的兼容性问题,POSIX 标准引入了“可靠信号”和“sigaction 函数”。掌握这两个特性,是编写健壮、跨平台信号处理程序的关键。

1. 可靠信号与非可靠信号的区别

对比维度非可靠信号(1~31 号)可靠信号(34~64 号,SIGRTMIN~SIGRTMAX)核心差异
信号编号1~31(如 SIGINT=2、SIGUSR1=10)34~64(SIGRTMIN 为 34,SIGRTMAX 为 64)编号范围不同,可靠信号编号更高
信号传递不记录发送次数,同一信号未处理时多次发送会合并,导致丢失记录发送次数,多次发送不会合并,进程会依次处理所有信号可靠信号支持信号排队,非可靠信号不支持
信号名称有固定名称(如 SIGINT、SIGTERM)无固定名称,需用户自定义(如 SIGRTMIN+1)可靠信号灵活性更高,适合用户自定义场景
适用场景简单通知(如终止、暂停),无需确保信号不丢失需要可靠传递的场景(如实时数据传输、事件计数)根据信号是否需要“不丢失”选择类型

2. sigaction 函数:替代 signal 的标准接口

sigaction 函数是 POSIX 标准定义的信号处理接口,支持设置信号处理函数、信号掩码、信号标志,行为在所有 UNIX 系统中一致,解决了 signal 函数的兼容性问题。

sigaction 函数原型与参数
#include <signal.h>// 功能:设置或获取信号的处理动作
// 参数:
// sig:要处理的信号(如 SIGINT、SIGRTMIN+1);
// act:非 NULL 时,设置信号的处理动作;
// oldact:非 NULL 时,保存信号的旧处理动作;
// 返回值:成功返回 0,失败返回 -1,错误码存入 errno。
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);// 信号处理动作结构体
struct sigaction {void (*sa_handler)(int);                    // 信号处理函数(SIG_IGN 表示忽略,SIG_DFL 表示默认)sigset_t sa_mask;                           // 信号掩码:处理函数执行期间阻塞的信号int sa_flags;                               // 信号标志:控制处理函数的行为void (*sa_sigaction)(int, siginfo_t *, void *); // 扩展处理函数(带信号信息)
};

关键参数说明
  • sa_mask:设置处理函数执行期间需要阻塞的信号。例如,若设置 sa_mask = SIGINT,则处理函数执行时,SIGINT 信号会被阻塞,避免同一信号嵌套触发。可通过 sigemptysetsigaddset 函数初始化掩码。
  • sa_flags:常用标志:
    • SA_RESTART:自动重启被信号中断的可中断系统调用(如 read、accept),避免手动重试;
    • SA_SIGINFO:使用 sa_sigaction 作为处理函数(而非 sa_handler),该函数可获取信号的详细信息(如发送者 PID、信号附加数据);
    • SA_NOCLDSTOP:仅当子进程终止时发送 SIGCHLD,子进程暂停时不发送。
实战:用 sigaction 注册可靠信号处理函数
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>// 可靠信号:SIGRTMIN + 1
#define MY_RELIABLE_SIGNAL (SIGRTMIN + 1)// 扩展信号处理函数(带信号信息)
void sig_handler(int sig, siginfo_t *info, void *context) {printf("捕获到可靠信号 %d\n", sig);printf("信号发送者 PID:%d\n", info->si_pid);      // 获取发送者 PIDprintf("信号附加数据:%d\n", info->si_value.sival_int); // 获取附加数据
}int main() {struct sigaction sa;sigemptyset(&sa.sa_mask);      // 初始化信号掩码(不阻塞其他信号)sa.sa_flags = SA_SIGINFO;      // 使用扩展处理函数sa.sa_sigaction = sig_handler; // 设置扩展处理函数// 注册可靠信号处理函数if (sigaction(MY_RELIABLE_SIGNAL, &sa, NULL) == -1) {perror("sigaction 注册信号失败");return 1;}printf("程序启动,PID = %d\n", getpid());printf("发送可靠信号:kill -%d %d -o 123(123 为附加数据)\n", MY_RELIABLE_SIGNAL, getpid());// 主循环while (1) {sleep(1);}return 0;
}

编译与运行方法

# 1. 编译程序
gcc sigaction_demo.c -o sigaction_demo# 2. 启动程序
./sigaction_demo# 3. 新终端发送可靠信号(带附加数据 123)
kill -$(($(kill -l SIGRTMIN) + 1)) $(pgrep sigaction_demo) -o 123

预期输出示例

程序启动,PID = 1234
发送可靠信号:kill -35 1234 -o 123(123 为附加数据)捕获到可靠信号 35
信号发送者 PID:5678      // 发送者终端的 PID
信号附加数据:123         // 自定义附加数据

优势:sigaction 不仅解决了 signal 的兼容性问题,还支持获取信号的详细信息(如发送者 PID、附加数据),适合需要进程间传递额外信息的场景(如分布式系统中的任务通知)。

UNIX 信号的概念、产生原因、处理方式,结合实战案例演示了信号的查看、发送与自定义处理,同时分析了常见问题与解决方法。信号作为 UNIX 系统中轻量级的异步通信机制,是进程间交互和系统事件通知的核心工具。

在实际开发中,需注意:

  • 区分可靠信号与非可靠信号,避免信号丢失;
  • 优先使用 sigaction 函数替代 signal,确保跨平台兼容性;
  • 信号处理函数需简洁、使用可重入函数,避免重入问题;
  • I/O 操作需处理 EINTR 错误码,避免逻辑中断。

掌握信号的核心机制,可帮助开发者编写更健壮、灵活的 UNIX 程序,尤其是在服务端开发、系统工具开发等领域,信号处理是不可或缺的技能。

http://www.dtcms.com/a/450473.html

相关文章:

  • 全面检测Linux系统健康情况
  • 成都中小企业网站建设北京朝阳区租房价格
  • 装饰公司营销网站模板北京海淀建设中路哪打疫苗
  • 《网络爬虫技术规范与应用指南系列》(xc—5)完
  • seo网站外链专发制作网络网站
  • 河南住房和城乡建设厅网站首页海飞丝网站建设中面临的技术问题_并提出可行的技术解决方案
  • Product Hunt 每日热榜 | 2025-10-06
  • 在电脑新建网站站点wordpress菜单调用
  • 广州个人网站制作公司男生用的浏览器
  • 网站二级页面设计要求怎么做网站代销
  • 关于举办第十九届iCAN大学生创新创业大赛创业赛道复赛的通知
  • 什么是网站关键词创意餐厅网站建设文案书
  • 增量同步 + 双库写入 + 时间游标更新
  • python爬虫爬小说来做网站wordpress分类设置主题
  • 太原网站定制python django做的网站
  • 普法网站建设方案网站开发谢辞
  • wordpress上传doc文件大小昆明二级站seo整站优化排名
  • 力扣136.只出现一次的数字
  • 网站的分页效果怎么做网站备案有什么作用
  • 怎么做自己的网站徐州建设局官网
  • 记事本代码做网站科学小制作
  • 丹东网站网站建设怎么做相册的网站
  • 湖州做网站公司有那几家广州网站制作系统
  • html判断域名 然后再跳转到网站推广策略英文
  • 4.12 环境光照
  • mcp sse 直接调用mcp方法
  • 11、Linux 密码管理
  • dedecms网站地图路径修改生成后 网站地图前台路径不变爬虫怎么看网站开发者模式
  • 思科交换机VLAN超简单配置(草稿)
  • 上海兼职网站制作seo网站优化外包