You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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;
}

问题根源及修复方案

  1. 共享内存大小严重不足

    • 原代码创建共享内存时仅分配sizeof(int)(4字节),但实际需要传输最大16MiB的数据,直接导致内存越界,破坏进程状态。
    • 修复:创建共享内存时指定最大需要的16MiB空间,替换sizeof(int)。
  2. 父进程wait(NULL)调用位置错误

    • 父进程每次循环都调用wait(NULL),会立即等待子进程终止,但子进程仍在循环执行,父进程会一直阻塞,导致后续循环无法推进,子进程也因父进程停止写入而无输出。
    • 修复:将wait(NULL)移到所有循环结束后,仅在父进程清理资源前调用一次,等待子进程完成所有任务。
  3. 冗余的信号量初始化

    • 原代码连续两次调用semctl(sem_id, 0, SETVAL, 1),保留一次即可。
  4. 细节优化

    • 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:40:27