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

基于C语言的共享内存生产者-消费者同步故障排查求助

解决你的生产者-消费者程序无响应问题

我仔细看了你的代码,发现几个核心逻辑错误导致程序卡住,下面逐个拆解并给出修复方案:

1. Consumer进程根本没被创建

你的main.c里只执行了一次fork(),然后错误地用同一个pid变量尝试启动Consumer。第一个fork()后,父进程中的pid是Producer子进程的ID(非0),所以第二个switch(pid)的case 0永远不会触发——也就是说,Consumer进程从来没被启动过!

修复方式:在父进程里再调用一次fork()来创建Consumer,调整后的main.c核心代码如下:

// 创建Producer子进程
pid_t pid_prod = fork();
switch(pid_prod) {
    case -1:
        perror("fork() error in producer section");
        exit(2);
    case 0:
        execl("./producer", "./producer", NULL);
        perror("execl() error in producer section");
        exit(3);
    default:
        // 父进程继续创建Consumer子进程
        pid_t pid_cons = fork();
        switch(pid_cons) {
            case -1:
                perror("fork() error in consumer section");
                exit(4);
            case 0:
                execl("./consumer", "./consumer", NULL);
                perror("execl() error in consumer section");
                exit(5);
            default:
                break;
        }
        break;
}

2. 信号量操作完全搞反了

生产者-消费者模型的信号量逻辑是:

  • 空槽信号量(sem0,初始值SIZE):生产者必须先"获取"(P操作)它,确保有空闲槽位才能写数据;写完后"释放"(V操作)满槽信号量(sem1,初始值0),告诉消费者有数据可读。
  • 满槽信号量(sem1):消费者必须先"获取"(P操作)它,确保有数据才能读;读完后"释放"(V操作)空槽信号量(sem0),把槽位还给生产者。

但你的代码完全颠倒了这个逻辑:

  • Producer里先semRelease(semid, 0)(给空槽信号量加1),然后写数据,接着semAcquire(semid, 1)(等待满槽信号量>0,而初始值是0,这里直接卡住!)。
  • Consumer里先semRelease(semid, 1)(给满槽信号量加1),然后读数据,接着semAcquire(semid, 0)(等待空槽信号量>0),逻辑完全错误。

修复方式:

  • Producer循环内的信号量操作改为:
semAcquire(semid, 0); // 等待空槽可用
usleep(rand()%5555);
buf->bufor[i%SIZE] = c;
buf->size++;
semRelease(semid, 1); // 通知消费者有新数据
printf(" %c", c);
fflush(stdout); // 强制刷新输出,避免缓冲看不到实时内容
  • Consumer循环内的信号量操作改为:
semAcquire(semid, 1); // 等待有数据可读
usleep(rand()%5555);
c = buf->bufor[i%SIZE];
semRelease(semid, 0); // 释放空槽给生产者
fputc(c, fp);
printf(" %c", c);
fflush(stdout);
i++;
c_cnt++;

3. Producer/Consumer重复创建信号量

Main进程已经创建了信号量集合,Producer和Consumer应该获取已有的信号量,而不是再次调用semCreate。如果你的semCreate用了IPC_CREAT | IPC_EXCL标志,重复调用会失败返回-1,后续信号量操作出错导致卡住。

修复方式:
把Producer和Consumer里的semid = semCreate(key, 2);改为semid = semGet(key);(如果你的工具函数里没有semGet,可以直接用semget(key, 2, 0)来获取已有信号量)。

4. Producer代码的笔误

你Producer里有一行y memoryID = memoryAccess('A');,多了个y,应该改成int memoryID = memoryAccess('A');——这个笔误会导致编译错误,Producer根本无法运行。

5. Consumer循环终止条件不可靠

原来的!((semctl(semid, 0, GETVAL) == SIZE) && (c_cnt == buf->size))容易出现竞态条件,比如生产者刚写完数据,信号量值还没更新,消费者就提前退出了。

修复方式:给共享结构体加一个结束标志,让生产者写完后通知消费者停止:

// 修改共享结构体
typedef struct cyclicalBuf {
    int size;
    int is_done; // 生产者完成后设为1
    char bufor[SIZE];
} cyclicalBuf;

然后Producer在循环结束后设置buf->is_done = 1;,并额外释放一次满槽信号量唤醒消费者:

// Producer循环结束后
buf->is_done = 1;
semRelease(semid, 1); // 唤醒可能等待的消费者

Consumer的循环条件改为:

while(!(buf->is_done && (c_cnt == buf->size))) {
    semAcquire(semid, 1);
    // 额外判断,避免生产者结束后还继续等待
    if(buf->is_done && c_cnt >= buf->size) {
        semRelease(semid, 1);
        break;
    }
    // ... 原有操作
}

额外小贴士

  • 所有printf后加fflush(stdout):终端输出默认是行缓冲,不刷新的话可能看不到实时的处理过程。
  • 确保input.txt存在:否则Producer会直接报错退出。
  • 检查你的信号量封装函数:比如semAcquire要正确调用semop执行减1操作,semRelease执行加1操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:06:56