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

共享内存练习问题:消费者进程无法接收并打印数据

问题分析与修复方案

你的代码存在几个核心问题,导致消费者无法打印内容,逐一拆解:

1. 进程创建逻辑错误

两次fork()会生成4个进程,而非你需要的1个生产者+1个消费者。你对进程身份的判断只能匹配其中两个进程,剩下的进程会空转,同时这种方式也会导致共享内存的创建混乱。

2. 共享内存创建时机错误

每个进程都调用了shmget(IPC_PRIVATE, ...),IPC_PRIVATE会为每个调用进程创建独立的私有共享内存段,这意味着生产者和消费者根本不在同一块内存上操作,自然读不到数据。必须在fork之前创建共享内存,子进程才会继承同一个shmid,访问同一块内存。

3. 无进程同步机制

生产者和消费者执行完全异步,消费者可能在生产者还没写入数据时就开始读取,或者读取的位置从未被写入(共享内存初始值随机),导致没有有效输出。

4. 其他细节问题

  • 未初始化随机数生成器srand(time(NULL)),rand()会生成固定序列;
  • 消费者一直读取buffer[0],但生产者循环写入i%32的位置,大概率buffer[0]不会被持续写入;
  • 未清理共享内存,会造成系统资源泄漏;
  • 未等待子进程结束,父进程提前退出可能导致输出异常。

修复后的代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <semaphore.h>
#include <sys/mman.h>

// 定义共享内存结构体,包含缓冲区和同步信号量
typedef struct {
    char buffer[32];
    sem_t empty;  // 标记缓冲区空位
    sem_t full;   // 标记缓冲区已填充位
    int write_idx; // 生产者写入位置
    int read_idx;  // 消费者读取位置
} SharedData;

int main()
{
    int shmid;
    SharedData *shared;
    pid_t producer_pid, consumer_pid;

    // 1. 先创建共享内存,大小为结构体大小
    shmid = shmget(IPC_PRIVATE, sizeof(SharedData), IPC_CREAT | S_IRUSR | S_IWUSR);
    if (shmid == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }

    // 映射共享内存到当前进程地址空间
    shared = (SharedData *)shmat(shmid, NULL, 0);
    if (shared == (void *)-1) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }

    // 2. 初始化信号量(必须在fork前完成,确保子进程继承初始化后的信号量)
    sem_init(&shared->empty, 1, 32); // 初始有32个空位,pshared=1表示进程间共享
    sem_init(&shared->full, 1, 0);   // 初始没有已填充位
    shared->write_idx = 0;
    shared->read_idx = 0;

    // 3. 创建生产者进程
    producer_pid = fork();
    if (producer_pid == -1) {
        perror("fork producer failed");
        exit(EXIT_FAILURE);
    }

    if (producer_pid == 0) {
        // 生产者进程
        srand(time(NULL) + getpid()); // 用进程ID区分随机序列
        char alphabet[26];
        for (int k = 0; k < 26; k++) {
            alphabet[k] = 'A' + k;
        }

        for (int i = 0; i < 128; i++) {
            sem_wait(&shared->empty); // 等待空位

            // 生成随机字母写入缓冲区
            char c = alphabet[rand() % 26];
            shared->buffer[shared->write_idx] = c;
            shared->write_idx = (shared->write_idx + 1) % 32;

            sem_post(&shared->full); // 标记有新数据
            usleep(10000); // 模拟生产耗时,可选
        }

        // 生产者结束,分离共享内存
        shmdt(shared);
        exit(EXIT_SUCCESS);
    }

    // 4. 创建消费者进程
    consumer_pid = fork();
    if (consumer_pid == -1) {
        perror("fork consumer failed");
        exit(EXIT_FAILURE);
    }

    if (consumer_pid == 0) {
        // 消费者进程
        for (int i = 0; i < 128; i++) {
            sem_wait(&shared->full); // 等待有数据

            // 读取缓冲区数据并打印
            char c = shared->buffer[shared->read_idx];
            printf("%c ", c);
            fflush(stdout); // 强制刷新输出缓冲区,避免卡顿
            shared->read_idx = (shared->read_idx + 1) % 32;

            sem_post(&shared->empty); // 标记空位可用
            usleep(15000); // 模拟消费耗时,可选
        }
        printf("\n");

        // 消费者结束,分离共享内存
        shmdt(shared);
        exit(EXIT_SUCCESS);
    }

    // 5. 父进程等待子进程全部结束
    waitpid(producer_pid, NULL, 0);
    waitpid(consumer_pid, NULL, 0);

    // 6. 销毁信号量,删除共享内存
    sem_destroy(&shared->empty);
    sem_destroy(&shared->full);
    shmdt(shared);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

编译与运行

编译时需要链接实时库:

gcc -o shm_prod_cons shm_prod_cons.c -lrt

运行后就能看到消费者打印出生产者生成的随机字母。

内容的提问来源于stack exchange,提问作者Gustavo Soares

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 13:40:59