C语言POSIX有名信号量跨进程共享失败问题咨询
你的代码存在3处明显问题,其中命名问题是导致跨进程同步失败的核心原因:
1. 信号量命名不符合POSIX强制规范
POSIX标准明确要求,传给sem_open的有名信号量名称必须以/作为第一个字符,名称其余部分不能再包含/。
你当前用的"sem1"到"sem8"都没有前置斜杠,属于实现自定义的非标准行为:在Linux的glibc实现中,这类名称不会被创建为系统全局可见的有名信号量,不会挂载到/dev/shm全局共享目录下,仅在调用进程内部生效,两个进程自然会各自生成独立的信号量实例,根本访问不到同一个内核对象。
直接把名称数组改成带前置斜杠的格式即可修复,注意数组第二维长度也要同步修改,否则会出现字符串截断溢出:
char names[8][6] = {"/sem1", "/sem2", "/sem3", "/sem4", "/sem5", "/sem6", "/sem7", "/sem8"};
2. 用指针地址判断信号量是否共享是常识错误
就算你修正命名后两个进程成功打开了同一个有名信号量,两个进程里打印出来的sem_t*地址也几乎不可能相同。sem_open返回的是信号量在当前进程独立虚拟地址空间的映射地址,操作系统本身就会给每个进程隔离独立的虚拟地址空间,指针值不同是完全正常的现象,根本不能作为信号量是否共享的判断依据。
正确的验证方式是做功能测试:在第一个进程中对信号量执行sem_wait让其阻塞,第二个进程对同名信号量执行sem_post,如果第一个进程能正常解除阻塞,就说明信号量跨进程工作正常。
3. 失败标记的逻辑完全写反
你代码里failed变量初始值设为了1,后续用逻辑或更新状态:
int failed = 1; failed = failed || (sem[i] == SEM_FAILED);
逻辑或运算只要有一个操作数为真,结果就永远为真。也就是说不管8个sem_open是不是全部调用成功,这个函数永远返回1(代表失败),返回值完全没有参考价值。
修复方式很简单,把failed的初始值改成0即可,只有当某个sem_open返回SEM_FAILED时,标记才会被置为1。
内容的提问来源于stack exchange,提问作者Kolt Penny

