Linux下C语言读写者共享内存异常求助:段错误+读取无输出
Linux C读写者共享内存程序错误排查与修复
问题描述
Linux环境下用C语言实现读写者模式共享内存程序,预期终端1(writer)输入内容后,终端2(reader)读取共享内存内容并打印。实际运行时writer触发段错误(segmentation fault),reader无法打印输入内容。
原Writer代码
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #define TRUE 1 #define FALSE 0 struct SHM_SIZE { char str[512]; int flag, readcount; }; sem_t *resourcemutex; int shmid, EXIT; void sigint_handler(int sig); struct SHM_SIZE *ptr; int main() { char str[512], ss[512]; key_t key; key = 1053; EXIT=FALSE; if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("SIGQUIT"); exit(1); } resourcemutex = sem_open("PRAK5-3RESC", O_CREAT, 0644, 1); if (resourcemutex == SEM_FAILED) { perror("unable to execute semaphore"); sem_unlink("PRAK5-3RESC"); exit(-1); } shmid = shmget(key, sizeof(struct SHM_SIZE), IPC_CREAT|0644); if (shmid < 0) { perror("shmget error..."); exit(-1); } ptr = shmat(shmid, NULL, 0); int i = 0; while (EXIT == FALSE) { printf("Masukkan Kata/kalimat : "); scanf("%[^\n]", ss); getchar(); //The input to send to resourcemutex fflush(stdin); sem_wait(resourcemutex); strcpy(ptr->str, ss); ptr->flag = 1; sem_post(resourcemutex); i++; } sem_close(resourcemutex); return 0; } void sigint_handler(int sig) { EXIT = TRUE; //to close resourcemutex sem_close(resourcemutex); sem_unlink("PRAK5-3RESC"); }
原Reader代码
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #define TRUE 1 #define FALSE 0 struct SHM_SIZE { char str[512]; int flag, readcount; }; int EXIT; int shmid; sem_t *readermutex, *resourcemutex; struct SHM_SIZE *ptr; void sigint_handler(int sig); int main() { int i, j; char ss[512], str[512]; key_t key; EXIT=FALSE; if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("SIGQUIT"); exit(1); } readermutex = sem_open("PRAK5-3READ", O_CREAT, 0644, 1); if (readermutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink("PRAK5-3READ"); exit(-1); } resourcemutex = sem_open("PRAK5-3RESC", O_CREAT, 0644, 1); if (resourcemutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink("PRAK5-3READ"); exit(-1); } shmid = shmget(key, sizeof(struct SHM_SIZE), IPC_CREAT | 0644); if (shmid < 0) { perror("failure in shmget"); exit(-1); } ptr = shmat(shmid, NULL, 0); while (EXIT == FALSE) { sem_wait(resourcemutex); if (ptr->flag == 0) { usleep(1000); continue; } else { printf("Reading %s\n", ptr->str); ptr->flag = 0; } sem_post(resourcemutex); } return 0; } void sigint_handler(int sig) { //close the mutex EXIT = TRUE; sem_close(readermutex); sem_close(resourcemutex); sem_unlink("PRAK5-3READ");//close the mutex sem_unlink("PRAK5-3RESC");//close the mutex shmdt(ptr); shmctl(shmid, IPC_RMID, NULL); exit(0); }
错误原因分析
- 共享内存未关联:Reader中
key未初始化,导致与Writer使用不同的共享内存实例,数据无法互通。 - 未检查共享内存挂载结果:
shmat失败时返回(void *)-1,直接访问ptr成员会触发段错误。 - 资源锁未正确释放:Reader中当
ptr->flag == 0时直接跳过,未调用sem_post释放锁,导致Writer永久无法获取锁,程序逻辑阻塞。 - 输入处理不安全:
scanf("%[^\n]", ss)无法处理空输入,且残留换行符会干扰后续输入,容易引发未定义行为。 - 全局变量未加volatile:信号处理函数修改
EXIT后,主循环可能因编译器优化无法感知到值变化,导致无法正常退出。
修复后代码
修复后的Writer代码
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #define TRUE 1 #define FALSE 0 struct SHM_SIZE { char str[512]; int flag, readcount; }; sem_t *resourcemutex; int shmid; volatile int EXIT; void sigint_handler(int sig); struct SHM_SIZE *ptr; int main() { char ss[512]; key_t key = 1053; EXIT = FALSE; if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("SIGQUIT"); exit(1); } resourcemutex = sem_open("PRAK5-3RESC", O_CREAT, 0644, 1); if (resourcemutex == SEM_FAILED) { perror("unable to execute semaphore"); sem_unlink("PRAK5-3RESC"); exit(-1); } shmid = shmget(key, sizeof(struct SHM_SIZE), IPC_CREAT|0644); if (shmid < 0) { perror("shmget error..."); exit(-1); } ptr = shmat(shmid, NULL, 0); if (ptr == (void *)-1) { perror("shmat failed"); exit(-1); } ptr->flag = 0; while (EXIT == FALSE) { printf("Masukkan Kata/kalimat : "); if (fgets(ss, sizeof(ss), stdin) == NULL) { break; } ss[strcspn(ss, "\n")] = '\0'; sem_wait(resourcemutex); strncpy(ptr->str, ss, sizeof(ptr->str)-1); ptr->str[sizeof(ptr->str)-1] = '\0'; ptr->flag = 1; sem_post(resourcemutex); } sem_close(resourcemutex); shmdt(ptr); return 0; } void sigint_handler(int sig) { EXIT = TRUE; sem_close(resourcemutex); shmdt(ptr); sem_unlink("PRAK5-3RESC"); }
修复后的Reader代码
#include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #define TRUE 1 #define FALSE 0 struct SHM_SIZE { char str[512]; int flag, readcount; }; volatile int EXIT; int shmid; sem_t *readermutex, *resourcemutex; struct SHM_SIZE *ptr; void sigint_handler(int sig); int main() { key_t key = 1053; EXIT = FALSE; if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("SIGQUIT"); exit(1); } readermutex = sem_open("PRAK5-3READ", O_CREAT, 0644, 1); if (readermutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink("PRAK5-3READ"); exit(-1); } resourcemutex = sem_open("PRAK5-3RESC", O_CREAT, 0644, 1); if (resourcemutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink("PRAK5-3RESC"); exit(-1); } shmid = shmget(key, sizeof(struct SHM_SIZE), IPC_CREAT | 0644); if (shmid < 0) { perror("failure in shmget"); exit(-1); } ptr = shmat(shmid, NULL, 0); if (ptr == (void *)-1) { perror("shmat failed"); exit(-1); } while (EXIT == FALSE) { sem_wait(resourcemutex); if (ptr->flag == 1) { printf("Reading %s\n", ptr->str); ptr->flag = 0; } sem_post(resourcemutex); usleep(100000); } sem_close(readermutex); sem_close(resourcemutex); shmdt(ptr); return 0; } void sigint_handler(int sig) { EXIT = TRUE; sem_close(readermutex); sem_close(resourcemutex); sem_unlink("PRAK5-3READ"); sem_unlink("PRAK5-3RESC"); shmdt(ptr); shmctl(shmid, IPC_RMID, NULL); exit(0); }
修复说明
- 统一共享内存key:Reader中初始化
key = 1053,与Writer保持一致,确保使用同一块共享内存。 - 添加shmat返回值检查:避免访问无效内存地址引发段错误。
- 修复资源锁释放逻辑:Reader中无论是否读取到内容,都调用
sem_post释放锁,防止死锁。 - 替换输入函数:用
fgets替代scanf,更安全地处理输入,同时去掉读取到的换行符。 - 全局变量加volatile修饰:确保信号处理函数修改
EXIT后,主循环能立即感知到变化。 - 初始化共享内存flag:Writer中初始化
ptr->flag = 0,避免初始值不确定导致逻辑异常。
内容的提问来源于stack exchange,提问作者XI MIPA 1 MICHAEL ELBERT JUSTI
相关产品推荐
相关产品推荐

