基于SystemV信号量与共享内存的Server-Clients同步问题排查
问题分析与修复方案
1. 多服务器进程引发冲突
你的主循环里,每fork一个客户端,父进程就进入服务器处理逻辑。这会生成N个服务器进程,它们同时争抢共享内存和信号量,直接导致临界区冲突。服务器应该是单个进程,等所有客户端fork完成后,统一处理所有请求。
2. 共享内存空间不足
创建共享内存时用了sizeof(int),但实际要存的是struct SharedData结构体,空间不够会导致数据越界覆盖,引发混乱:
// 错误代码 shm_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0660); // 修复后 shm_id = shmget(IPC_PRIVATE, sizeof(struct SharedData), IPC_CREAT | 0660);
3. 信号量同步逻辑完全颠倒
服务器端的信号量操作顺序搞反了,正确的同步流程应该是:
- 客户端:填好请求数据 →
sem_V(sem_id, 1)(通知服务器有请求)→sem_P(sem_id, 0)(等服务器处理完) - 服务器:
sem_P(sem_id, 1)(等客户端发请求)→ 处理请求 →sem_V(sem_id, 0)(通知客户端处理完成)
你的服务器先执行sem_V(sem_id, 0)再sem_P(sem_id, 1),客户端还没发请求就被唤醒,完全失去同步作用。
4. 随机数未初始化
用rand()前没调用srand(),所有进程生成的随机数序列完全一样,不符合测试预期。可以在客户端进程开头加:
srand(getpid()); // 用进程PID当随机种子
5. 信号量资源释放错误
free_resources里逐个删除信号量是错的,直接删除整个信号量集就行:
void free_resources(int shm_id, int sem_id) { shmctl(shm_id, IPC_RMID, NULL); semctl(sem_id, IPC_RMID, 0); // 删除整个信号量集 }
修复后的核心代码片段
主函数(单个服务器进程)
// 先fork所有客户端 for (int i = 0; i < N; i++) { sleeptime = random() % 10; if ((pid = fork()) == -1) { perror("fork"); free_resources(shm_id, sem_id); exit(EXIT_FAILURE); } if (pid == 0) { srand(getpid()); printf("# I am the child process with process id: %d\n", getpid()); int j = 0; while (j < data->max_requests) { data->file_index = rand() % MAX_FILES + 1; data->start_line = rand() % MAX_LINES + 1; data->end_line = rand() % (MAX_LINES - data->start_line) + data->start_line; printf("(%d): Client %d request %d\n", getpid(), i, j); data->client_request = j+1; sem_V(sem_id, 1); // 通知服务器有请求 sem_P(sem_id, 0); // 等服务器处理完 printf("(%d): Server processed request %d\n", getpid(), j); j++; printf("j is %d \n",j); } exit(EXIT_SUCCESS); } } // 单个服务器处理所有请求 int total_requests = N * L; int processed = 0; while (processed < total_requests) { sem_P(sem_id, 1); // 等客户端请求 printf("# I am the server process with process id: %d\n", getpid()); printf("(%d): processing client request %d\n", getpid(), processed+1); strcpy(data->shared_memory, "Response from server"); sem_V(sem_id, 0); // 通知客户端处理完成 processed++; } // 等所有客户端退出 for (int i = 0; i < N; i++) { wait(NULL); }
关键说明
- 必须保证只有一个服务器进程,避免多进程争抢资源。
- 严格遵守信号量同步顺序,别搞反等待和通知的时机。
- 共享内存大小要足够存整个结构体,防止数据越界。
内容的提问来源于stack exchange,提问作者Yourias
相关产品推荐
相关产品推荐

