System V信号量操作异常:进程无法执行后续代码求助
问题分析与解决
以下是出现问题的代码:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/sem.h> #include <signal.h> #include <sys/shm.h> #include <errno.h> #include <time.h> typedef union semun { int val; struct semid_ds *buf; ushort *array; }semun; int main(int argc, char *argv[]) { int key = ftok("test.c", 1); semun t; int sem_id = semget(key, 1 , 0666 | IPC_CREAT | IPC_EXCL); printf("%d", sem_id); if(sem_id != -1){ printf("Creating a new semaphore...\n"); t.val = 1; semctl(sem_id, 0, SETVAL, &t); } else if (errno == EEXIST) { printf("Connecting..."); sem_id = semget(key, 1, 0); } else { printf("LINUX Error"); exit(0); } struct sembuf P; P.sem_num = 0; P.sem_op = -1; P.sem_flg = 0; semop(sem_id, &P, 1); printf("Hello\n"); semctl(sem_id, 0, IPC_RMID); return 0; }
执行semop后,后续的printf("Hello\n")无法输出,每次运行前已清空信号量数组,求问题原因。
核心问题:信号量连接分支未处理初始值,且缺失错误检查
阻塞根源
如果程序之前异常退出(比如第一次运行被强制终止,semctl(sem_id, 0, IPC_RMID)没执行),残留的信号量值会是0(因为之前执行过semop(-1)把初始的1减到0)。下次运行进入EEXIST分支时,直接获取已存在的信号量,接着执行semop(-1)——此时信号量值为0,且sem_flg设为0(默认阻塞模式),程序会卡在semop调用处,自然不会执行后面的printf。隐藏风险
EEXIST分支里的semget(key, 1, 0)没有做返回值检查,如果这个调用失败(比如权限问题),sem_id会是-1,后续semop会直接崩溃,只是你没触发这个情况而已。
修复方案
- 在
EEXIST分支获取信号量后,要么重置信号量的值(单进程测试场景推荐),要么添加IPC_NOWAIT标志避免阻塞(根据业务需求选择),同时必须检查semget的返回值:
修改后的EEXIST分支示例:
else if (errno == EEXIST) { printf("Connecting...\n"); sem_id = semget(key, 1, 0); // 检查semget连接是否成功 if (sem_id == -1) { perror("semget connect failed"); exit(1); } // 重置信号量值为1(单进程测试场景适用) t.val = 1; if (semctl(sem_id, 0, SETVAL, &t) == -1) { perror("semctl setval failed"); exit(1); } }
另外,把开头的printf("%d", sem_id);改成printf("sem_id: %d\n", sem_id);,避免缓冲导致输出不及时干扰排查。
内容的提问来源于stack exchange,提问作者Vojin
相关产品推荐
相关产品推荐

