消费者进程可获取POSIX信号量值但调用sem_trywait触发段错误
问题描述
在多进程项目中,sender.c(生产者进程)可通过sem_wait和sem_post修改共享内存中的无名POSIX信号量值,但receiver.c(消费者进程)仅能获取该信号量数值,调用sem_trywait时会触发段错误。
怀疑问题源于参数传递(传引用vs传值)或信号量释放/销毁方式不当,sender和receiver均通过静态链接获取buffer_node结构体。已在x64 Ubuntu和ARM树莓派Raspbian测试,替换sem_trywait为sem_wait问题仍存在,希望尽量避免改用命名信号量。
相关代码
头文件(cbuffapi.h)
#define _CBUFFAPI_H_ #include <stdlib.h> #include <signal.h> #include "stdio.h" #include <semaphore.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/mman.h> #include <assert.h> #include <sys/stat.h> #include <fcntl.h> #define BUFFER_SIZE 5 #define SAMPLES_AMOUNT 240 struct buffer_node { unsigned long int buffered_samples[SAMPLES_AMOUNT]; sem_t semaphore; int sem_val; }; struct cyclic_buffer { struct buffer_node buffer_elements[BUFFER_SIZE]; unsigned int head; unsigned int tail; unsigned int full; unsigned int max; }; /*cyclic_buffer* init_buffer(); //void buffer_reset(cyclic_buffer* cb); //void buffer_free(cyclic_buffer* cb); //void buffer_put(cyclic_buffer* cb, unsigned int index); //unsigned int is_buffer_full(cyclic_buffer* cb);*/ int get_sem_val(sem_t sem); #endif
Sender进程(sender.c)
#include <stdlib.h> #include <signal.h> #include "stdio.h" #include <semaphore.h> #include "cbuffapi.h" #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/mman.h> #include <unistd.h> #include <sys/types.h> #include <assert.h> #include <sys/stat.h> #include <fcntl.h> #define NAME "/testmem" struct buffer_node * shm_buffer; void Handler(int signo) { //System Exit printf("\r\n END \r\n"); sem_close(&(shm_buffer->semaphore)); munmap(0, sizeof(struct buffer_node)); shm_unlink(NAME); exit(0); } int main() { signal(SIGINT, Handler); int shm_cyclic_buffer_fd = shm_open(NAME, O_CREAT|O_RDWR|O_EXCL, 0666); if(shm_cyclic_buffer_fd < 0) { perror("shm_open()"); return 1; } ftruncate(shm_cyclic_buffer_fd, sizeof(struct buffer_node)); shm_buffer = (struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_WRITE|PROT_READ, MAP_SHARED, shm_cyclic_buffer_fd, 0); shm_buffer->sem_val = 67; sem_init(&(shm_buffer->semaphore), 1, 4); int test_sem_val = 15; while(1) { if(sem_trywait(&(shm_buffer->semaphore))==0) { sem_getvalue(&(shm_buffer->semaphore), &test_sem_val); printf("%d\n", test_sem_val); shm_buffer->sem_val = shm_buffer->sem_val + 1; printf("%d\n", shm_buffer->sem_val); sleep(5); sem_post(&(shm_buffer->semaphore)); sem_getvalue(&(shm_buffer->semaphore), &test_sem_val); printf("%d\n", test_sem_val); } else if(sem_trywait(&(shm_buffer->semaphore))== -1) { printf("variable reading - overwriting impossible\n"); } sleep(0.5); } return 0; }
Receiver进程(receiver.c)
#include <stdlib.h> #include <signal.h> #include "stdio.h" #include <semaphore.h> #include "cbuffapi.h" #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/mman.h> #include <unistd.h> #include <sys/types.h> #include <assert.h> #include <sys/stat.h> #include <fcntl.h> #define NAME "/testmem" int shared_memory_fd; void Handler(int signo) { //System Exit printf("\r\n END \r\n"); munmap(0, sizeof(struct buffer_node)); close(shared_memory_fd); shm_unlink(NAME); exit(0); } int main() { signal(SIGINT, Handler); struct buffer_node* shm_buffer; int shared_memory_fd = shm_open(NAME, O_RDONLY, 0666); if(shared_memory_fd < 0) { perror("Impossible to get shared memory file descriptor"); return 1; } printf("Shared memory ID ok\n"); //memory mapping shm_buffer=(struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_READ, MAP_SHARED, shared_memory_fd, 0); if(shm_buffer == (void *) -1) { perror("Cannot attach to shared memory"); return 2; } printf("map ok"); printf("shared memory ID ok\n"); int test_sem_val = 1; while(1) { sem_getvalue(&(shm_buffer->semaphore), &test_sem_val); printf("%d\n", test_sem_val); printf("%d\n", shm_buffer->sem_val); sleep(5); if(sem_trywait(&(shm_buffer->semaphore)) == 0) { printf("%d\n", shm_buffer->sem_val); sleep(1); sem_post(&(shm_buffer->semaphore)); } else { printf("variable modified\n"); sleep(1); } } return 0; }
问题分析与解决
核心问题1:共享内存映射权限不足
Receiver进程仅用PROT_READ权限映射共享内存,但sem_trywait和sem_post会修改信号量内部状态,需要写入共享内存,权限不足直接触发段错误。修复方式是给Receiver的映射加上PROT_WRITE权限:shm_buffer=(struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_READ|PROT_WRITE, MAP_SHARED, shared_memory_fd, 0);核心问题2:信号量操作重复调用
Sender进程中连续两次调用sem_trywait,第一次失败后再次尝试会浪费资源,应改为调用一次并保存结果:int ret = sem_trywait(&(shm_buffer->semaphore)); if(ret == 0) { // 成功处理逻辑 } else if(ret == -1) { // 失败处理逻辑 }信号量与共享内存销毁错误
- Sender进程误用
sem_close,无名信号量应使用sem_destroy,且需确保所有进程不再使用时再调用,否则会引发未定义行为。 - Receiver进程不应调用
shm_unlink,共享内存的unlink操作应由创建它的Sender进程负责,Receiver只需关闭文件描述符并解除映射。 - 信号处理函数中
munmap的第一个参数应为映射起始地址shm_buffer,而非0,否则会导致未定义行为。
- Sender进程误用
其他规范优化
- 检查
mmap返回值时,建议使用标准宏MAP_FAILED替代(void*)-1,兼容性更好。 - 确保
sem_init的第二个参数设为1(标识为跨进程共享的信号量),当前代码已正确设置。
- 检查
内容的提问来源于stack exchange,提问作者V8Interceptor
相关产品推荐
相关产品推荐

