C语言信号处理器修改全局变量失效,lock字段未更新求助
首先,咱们来拆解下你遇到的问题:明明子进程能收到信号,lockSig也被正确设置了,但lock.locked就是没变化,核心原因有两个——全局变量的内存可见性问题,以及信号触发后的处理时机问题。
1. 内存可见性:lockSig需要volatile修饰
你定义的int lockSig = -1是普通全局变量,编译器会对它做优化:可能把它的值缓存到CPU寄存器里,而不是每次都从内存读取。这就导致信号处理器里修改了内存中的lockSig,但主循环里读取的还是寄存器里的旧值,自然不会进入对应的分支更新lock.locked。
修复方法:给lockSig加上volatile关键字,告诉编译器这个变量可能被异步修改(比如信号处理器),必须每次从内存读取:
volatile int lockSig = -1;
2. 信号处理时机:别只在处理特定命令时检查lockSig
你的代码里,检查lockSig的逻辑是和strcmp(buffer, "LOCK")/"UNLOCK"绑定在一起的。如果信号是在两次处理管道输入的间隙收到的,lockSig被设置成1或0,但主循环没走到那两个if分支,就不会处理这个信号触发的状态变更。
修复方法:把检查lockSig的逻辑独立出来,放到主循环的每次迭代中,确保信号触发后能被及时处理:
// 假设这是你的主循环(处理管道输入的无限循环) while (1) { // 先读取管道输入到buffer(你的原有逻辑) // ... // 优先处理信号触发的锁状态变更 if (lockSig == 1) { lock.locked = 1; lockSig = -1; // 重置标记,等待下一次信号 fscanf(kid, "%d", &temp); // 等待父进程输入 } else if (lockSig == 0) { lock.locked = 0; lockSig = -1; fscanf(kid, "%d", &temp); } // 再处理管道传来的LOCK/UNLOCK命令(你的原有逻辑) if (strcmp(buffer, "LOCK") == 0) { lock.locked = 1; fscanf(kid, "%d", &temp); } else if (strcmp(buffer, "UNLOCK") == 0) { lock.locked = 0; fscanf(kid, "%d", &temp); } // 其他业务逻辑... }
3. 可选优化:用sigaction替代signal
signal函数在不同平台上的行为可能不一致,属于较老旧的接口。推荐用更可靠的sigaction来注册信号处理器,避免潜在的兼容性问题:
void lockHandler(int sig){ lockSig=1; } void unlockHandler(int sig){ lockSig=0; } // 注册SIGUSR1信号处理 struct sigaction sa_lock; sa_lock.sa_handler = lockHandler; sigemptyset(&sa_lock.sa_mask); // 处理信号时不阻塞其他信号 sa_lock.sa_flags = 0; sigaction(SIGUSR1, &sa_lock, NULL); // 注册SIGUSR2信号处理 struct sigaction sa_unlock; sa_unlock.sa_handler = unlockHandler; sigemptyset(&sa_unlock.sa_mask); sa_unlock.sa_flags = 0; sigaction(SIGUSR2, &sa_unlock, NULL);
额外注意点
如果你的fscanf(kid, "%d", &temp)是阻塞式等待父进程输入,那信号触发后,子进程会卡在fscanf直到父进程发送数据,才会处理lockSig的变更。如果需要更及时的响应,可以考虑用非阻塞IO(比如fcntl设置管道为非阻塞),或者调整父进程的逻辑,在发送信号后立即发送对应的数据。
内容的提问来源于stack exchange,提问作者1024 Arthas

