为何Semaphore无法解决共享内存程序的竞态条件与进程阻塞问题?
共享内存+信号量进程同步阻塞问题
我编写了一个基于共享内存传输字节的小型程序,目前遇到的问题是子进程出现阻塞——打印“Process 2 print: 5000”后无后续输出。我原本期望父子进程能交替执行,尝试过调整锁的位置、增减锁/解锁操作、添加sleep(1)等方法,但问题仍未解决。相关代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> int main() { int sem_id; int shm_id; key_t key = ftok("shared_memory", 1); // Create key shared memory key_t keytwo = ftok("semaphore", 1); // Create key semaphore shm_id = shmget(key, sizeof(int), IPC_CREAT | 0666); // Create shared memory sem_id = semget(keytwo, 1, IPC_CREAT | 0666); // Create Semaphore if((shm_id = shmget(key, sizeof(int), IPC_CREAT | 0666)) == -1){ // -1 indicates error perror("semctl"); exit(1); } else { printf("Shared memory created with id: %d \n", shm_id); } if((sem_id = semget(keytwo, 1, IPC_CREAT | 0666)) == -1){ // -1 indicates error perror("semctl"); exit(1); } else { printf("Sephamore created with id: %d \n", shm_id); } struct sembuf sem_lock = {0, -1, 0}; // Struct for semop to lock struct sembuf sem_unlock = {0, 1, 0}; // Struct for semop to unlock char *data = (char *)shmat(shm_id, NULL, 0); // Connect Shared-Memorys to Process pid_t pid; semctl(sem_id, 0, SETVAL, 1); //initialize the semaphore with value 1 if((semctl(sem_id, 0, SETVAL, 1)) == -1){ // -1 indicates error perror("semctl"); exit(1); } else { printf("Sephamore initialized \n"); } pid = fork(); if(pid < 0){ printf("Error"); return 1; } int p = 1; int c = 1; //16777216 = 1024 * 16384 char *message = (char *) malloc(16777216); char *read = (char *) malloc(16777216); for (int KiB = 1; KiB <= 16384; KiB *= 2) { int bytes = KiB * 1024; for(int i = 0; i < 1000; i++){ if (pid == 0) { // Process 2: semop(sem_id, &sem_lock, 1); memcpy(read, data, bytes); //"read" data //printf("%s \n", read) //print to check whether read worked semop(sem_id, &sem_unlock, 1); //print to check race condition printf("Process 2 print: %d \n", p); p++; } else { // Process 1: for (int i = 0; i < bytes; ++i) { message[i] = 'a'; } semop(sem_id, &sem_lock, 1); printf("%d", sem_id); memcpy(data, message, bytes); semop(sem_id, &sem_unlock, 1); //print to check race condition printf("Process 1 print: %d \n", c); c++; wait(NULL); } } } shmdt(data); // Remove shared-memory from process shmctl(shm_id, IPC_RMID, NULL); // Delete shared memory semctl(sem_id, 0, IPC_RMID); // Delete semaphore return 0; }
问题根源及修复方案
共享内存大小严重不足
- 原代码创建共享内存时仅分配
sizeof(int)(4字节),但实际需要传输最大16MiB的数据,直接导致内存越界,破坏进程状态。 - 修复:创建共享内存时指定最大需要的16MiB空间,替换
sizeof(int)。
- 原代码创建共享内存时仅分配
父进程
wait(NULL)调用位置错误- 父进程每次循环都调用
wait(NULL),会立即等待子进程终止,但子进程仍在循环执行,父进程会一直阻塞,导致后续循环无法推进,子进程也因父进程停止写入而无输出。 - 修复:将
wait(NULL)移到所有循环结束后,仅在父进程清理资源前调用一次,等待子进程完成所有任务。
- 父进程每次循环都调用
冗余的信号量初始化
- 原代码连续两次调用
semctl(sem_id, 0, SETVAL, 1),保留一次即可。
- 原代码连续两次调用
细节优化
- 用
memset替代循环赋值填充字符,提升效率; - 修正“Sephamore”的拼写错误为“Semaphore”;
- 增加
shmat、malloc的错误检查,提升程序健壮性。
- 用
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #define MAX_SHM_SIZE 16777216 // 16MiB int main() { int sem_id; int shm_id; key_t key = ftok("shared_memory", 1); key_t keytwo = ftok("semaphore", 1); // 创建指定大小的共享内存 if((shm_id = shmget(key, MAX_SHM_SIZE, IPC_CREAT | 0666)) == -1){ perror("shmget"); exit(1); } else { printf("Shared memory created with id: %d \n", shm_id); } if((sem_id = semget(keytwo, 1, IPC_CREAT | 0666)) == -1){ perror("semget"); exit(1); } else { printf("Semaphore created with id: %d \n", sem_id); } struct sembuf sem_lock = {0, -1, 0}; struct sembuf sem_unlock = {0, 1, 0}; char *data = (char *)shmat(shm_id, NULL, 0); if (data == (char*)-1) { perror("shmat"); exit(1); } pid_t pid; // 仅初始化一次信号量 if(semctl(sem_id, 0, SETVAL, 1) == -1){ perror("semctl"); exit(1); } else { printf("Semaphore initialized \n"); } pid = fork(); if(pid < 0){ perror("fork"); return 1; } int p = 1; int c = 1; char *message = (char *) malloc(MAX_SHM_SIZE); char *read_buf = (char *) malloc(MAX_SHM_SIZE); if (!message || !read_buf) { perror("malloc"); exit(1); } for (int KiB = 1; KiB <= 16384; KiB *= 2) { int bytes = KiB * 1024; for(int i = 0; i < 1000; i++){ if (pid == 0) { // 子进程:读取共享内存 semop(sem_id, &sem_lock, 1); memcpy(read_buf, data, bytes); semop(sem_id, &sem_unlock, 1); printf("Process 2 print: %d \n", p); p++; } else { // 父进程:写入共享内存 memset(message, 'a', bytes); semop(sem_id, &sem_lock, 1); memcpy(data, message, bytes); semop(sem_id, &sem_unlock, 1); printf("Process 1 print: %d \n", c); c++; } } } shmdt(data); if (pid != 0) { wait(NULL); // 父进程等待子进程完成后再清理资源 shmctl(shm_id, IPC_RMID, NULL); semctl(sem_id, 0, IPC_RMID); } free(message); free(read_buf); return 0; }
内容的提问来源于stack exchange,提问作者Riuma
相关产品推荐
相关产品推荐

