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

xv6并发素数筛程序无法自动终止问题求助

MIT 6.1810 xv6并发素数筛程序无法自动终止问题排查

问题背景

我正在完成MIT 6.1810 2023秋季操作系统实验的xv6并发素数筛任务,要求用管道实现Doug McIlroy提出的素数筛设计,代码放在user/primes.c文件中。程序能输出正确的素数,但无法自动终止,需手动输入换行才会回到shell提示符,导致测试不通过。尝试在main函数中添加wait调用后,程序仅输出前两个素数就挂起,需要排查无法终止的原因及解决方法。

现有代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define PROCESS_MAX (int) 35

void extend_pipeline(int* pipe);
int main(int argc, char *argv[]) {
  int pipefd[2];
  pipe(pipefd);

  // this is the generating process
  for (int i = 2; i <= PROCESS_MAX; i++) {
    write(pipefd[1], &i, sizeof(int));
  }
  extend_pipeline(pipefd);
  exit(0);
}

void extend_pipeline(int* pipe_parent) {

  int cpid;
  int pipe_child[2];
  pipe(pipe_child);

  int p = 0;
  int n = 0;
  close(pipe_parent[1]);
  int iteration = 0;

  while (read(pipe_parent[0], &n, sizeof(int)) > 0) {

    iteration++;
    if (iteration == 1) {
      p = n;
      sleep(1);
      printf("prime %d\n", p);
     }

    if (n % p != 0) {
      write(pipe_child[1], &n, sizeof(int));
    }
   }
  if (iteration == 1) {
    exit(0);
  }
  if ((cpid = fork()) == 0) { // child
    extend_pipeline(pipe_child);
    exit(0);
  }
}

实际与期望输出

实际输出

$ ./primes
prime 2
$ prime 3
prime 5
prime 7
prime 11
prime 13
prime 17
prime 19
prime 23
prime 29
prime 31

期望输出

$ ./primes
prime 2
prime 3
prime 5
prime 7
prime 11
prime 13
prime 17
prime 19
prime 23
prime 29
prime 31
$

问题原因分析

  1. 管道写端未完全关闭:主进程写完初始数字后,没有关闭自身持有的管道写端pipefd[1],仅在extend_pipeline中关闭了该写端的副本。由于管道写端仍有一个引用(主进程的pipefd[1]),下游进程的read会一直阻塞等待更多数据,无法正常结束循环。
  2. 子管道写端未及时关闭:每个extend_pipeline进程写完数据到子管道后,没有关闭子管道的写端pipe_child[1],导致子进程的read一直等待,无法退出。
  3. wait调用位置错误:仅在main函数中添加wait会导致主进程提前等待第一个子进程,但此时管道未完全关闭,第一个子进程无法正常退出,进而导致整个程序挂起。同时,未在父进程中等待子进程会产生僵尸进程,影响程序正常终止。

解决方法及修改后的代码

修改要点

  • 主进程写完初始数据后立即关闭管道写端,让下游进程的read在读完所有数据后返回0。
  • 每个进程读完父管道后关闭父管道读端,写完子管道后关闭子管道写端,确保管道资源被正确释放。
  • 父进程fork后关闭子管道的两端,并调用wait等待子进程退出,避免僵尸进程并保证父进程在子进程完成后再退出。
  • 最后一个素数进程(无后续子进程)关闭子管道的读端后再退出,避免资源泄漏。

修改后的代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define PROCESS_MAX (int) 35

void extend_pipeline(int* pipe);
int main(int argc, char *argv[]) {
  int pipefd[2];
  pipe(pipefd);

  // 生成初始数字序列
  for (int i = 2; i <= PROCESS_MAX; i++) {
    write(pipefd[1], &i, sizeof(int));
  }
  // 主进程写完后立即关闭管道写端
  close(pipefd[1]);
  extend_pipeline(pipefd);
  // 主进程等待最后一个子进程完成
  wait(0);
  exit(0);
}

void extend_pipeline(int* pipe_parent) {
  int cpid;
  int pipe_child[2];
  pipe(pipe_child);

  int p = 0;
  int n = 0;
  // 当前进程仅需读取父管道,关闭写端
  close(pipe_parent[1]);
  int iteration = 0;

  while (read(pipe_parent[0], &n, sizeof(int)) > 0) {
    iteration++;
    if (iteration == 1) {
      p = n;
      printf("prime %d\n", p);
    }
    if (n % p != 0) {
      write(pipe_child[1], &n, sizeof(int));
    }
  }
  // 读完父管道数据后,关闭读端
  close(pipe_parent[0]);

  // 写完所有数据到子管道后,关闭写端
  close(pipe_child[1]);

  if (iteration == 1) {
    // 无后续子进程,关闭子管道读端后退出
    close(pipe_child[0]);
    exit(0);
  }

  if ((cpid = fork()) == 0) { // 子进程处理子管道
    extend_pipeline(pipe_child);
    exit(0);
  } else {
    // 父进程无需操作子管道,关闭两端
    close(pipe_child[0]);
    // 等待子进程完成后再退出
    wait(0);
  }
}

验证效果

修改后的程序会正常输出所有素数,并自动终止回到shell提示符,符合期望输出要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:14:53