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 $
问题原因分析
- 管道写端未完全关闭:主进程写完初始数字后,没有关闭自身持有的管道写端
pipefd[1],仅在extend_pipeline中关闭了该写端的副本。由于管道写端仍有一个引用(主进程的pipefd[1]),下游进程的read会一直阻塞等待更多数据,无法正常结束循环。 - 子管道写端未及时关闭:每个
extend_pipeline进程写完数据到子管道后,没有关闭子管道的写端pipe_child[1],导致子进程的read一直等待,无法退出。 - 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
相关产品推荐
相关产品推荐

