C语言用SIGUSR1/SIGUSR2实现的服务端客户端程序通信异常求助
minitalk信号传输异常问题排查与修复
核心故障原因
- 信号竞态丢失:客户端调用
kill()发送信号后,服务端的响应信号可能在客户端执行pause()之前就已经送达,此时信号处理函数执行完成后才会走到pause(),客户端会永远阻塞,消息越长触发该问题的概率越高,完全符合你描述的故障现象。 - 客户端sigaction未初始化完整:客户端定义的
c_signal结构体只赋值了sa_handler,其余字段(sa_mask、sa_flags)为栈上随机值,会导致信号处理逻辑不稳定。 - (可选优化)服务端在信号处理函数中调用
usleep(300)完全多余,反而会增加传输耗时和异常概率。
修复方案
客户端修改
- 初始化sigaction结构体时清空所有字段,设置合理的信号掩码:
// main函数中sigaction初始化部分修改为: g_info = (s_info *)malloc(sizeof(s_info)); memset(&c_signal, 0, sizeof(c_signal)); // 清空结构体,避免随机值 c_signal.sa_handler = sig_handler; sigemptyset(&c_signal.sa_mask); sigaddset(&c_signal.sa_mask, SIGUSR1); sigaddset(&c_signal.sa_mask, SIGUSR2); // 处理信号时阻塞另一路信号,避免嵌套干扰 sigaction(SIGUSR1, &c_signal, NULL); sigaction(SIGUSR2, &c_signal, NULL);
- 解决信号竞态问题:添加全局标志位标记是否收到响应,发信号前先重置标志位,循环检查标志位结合pause避免丢信号:
首先在头文件s_info结构体中新增字段:
typedef struct t_info { // 原有字段保留,新增如下字段 int ack_received; } s_info;
修改信号处理函数:
void sig_handler(int signum) { if (signum == g_info->cur_sig) g_info->ack_received = 1; else { write (1, "signal interrupt\n", 17); exit(0); } }
修改send_int和send_string中发送信号的逻辑,每次发信号前重置标志位,把原有的单条pause()替换为循环检查:
// 每次kill发送信号前新增一行 g_info->ack_received = 0; // 把原来的pause()替换为 while(!g_info->ack_received) pause();
服务端修改
- 移除信号处理函数中的
usleep(300),无任何作用反而拖慢速度。 - (可选)不需要在sigaction的
sa_mask中阻塞SIGUSR1和SIGUSR2,信号处理函数执行时默认会阻塞当前触发的信号,足够使用。
验证
修改完成后重新编译,长文本传输成功率会达到100%,不会再出现服务端无输出的问题。
内容的提问来源于stack exchange,提问作者acho
相关产品推荐
相关产品推荐

