Debug记录
1.1 问题描述:在Lab1中的prime实验中,采用单管道进行数据传输,父进程成功写入值,但是子进程无论父进程在管道写入任何值都读只能读到0
第一份代码如下:
#include "kernel/types.h"
#include "user/user.h"
int main()
{
int pid, fd[2], status;
pid = fork();
pipe(fd);
if(pid == 0) // 子进程
{
int value;
close(fd[1]);
read(fd[0], &value, sizeof(int));
printf("value: %d\n", value);
exit(0);
}
else if(pid > 0)
{
int i = 1;
write(fd[1], &i, sizeof(int));
wait(&status);
exit(0);
}
else
{
printf("fork error!!!\n");
exit(1);
}
}
输出结果:
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ prime
value: 0
这个问题卡了我整整4天,我将代码规模缩小到只传入一个int值然后读取,考虑了任何阻塞的可能,但是还是读不到传入的值,正在我心如死灰的时候,我重新写了一遍代码,神奇的是,运行成功了!
第二份代码如下:
#include "kernel/types.h"
#include "user/user.h"
int main()
{
int pid, fd[2], status;
pipe(fd);
pid = fork();
if(pid == 0) // 子进程
{
int value;
close(fd[1]);
read(fd[0], &value, sizeof(int));
printf("value: %d\n", value);
exit(0);
}
else if(pid > 0) //父进程
{
int i = 1;
write(fd[1], &i, sizeof(int));
wait(&status);
exit(0);
}
else
{
printf("fork error!!!\n");
exit(1);
}
}
输出:
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ prime
prime: 1
$
不知道你发现了问题所在了吗~
1.2 是的,问题就出在pipe() 和 fork() 的顺序!这个错误对我来说非常非常隐蔽!
第一种情况:pid = fork()在前,pipe(fd)在后。
此时子进程和父进程都会创建各自的管道,且由于子进程会继承进程的文件描述符,因此子进程和父进程拥有相同的文件描述,分别都指向向读端和写端,但是它们所指的管道不是同一个管道,所以子进程中读到的是自己管道的读端。因为子进程中写入端已经关闭,写入为空。同时无论父进程中写入什么,子进程读到的始终会是0。(PS:如果注释掉子进程中的close(fd[1]),read()会阻塞,因为写入端还没有写入数据同时写入端没有关闭。)
我们在父子进程中输出文件描述符验证一下
#include "kernel/types.h"
#include "user/user.h"
int main()
{
int pid, fd[2], status;
pid = fork();
pipe(fd);
if(pid == 0) // 子进程
{
int value;
close(fd[1]);
read(fd[0], &value, sizeof(int));
printf("value: %d\n", value);
printf("child fd[0]: %d, fd[1]: %d\n", fd[0], fd[1]);
exit(0);
}
else if(pid > 0) //父进程
{
int i = 1;
write(fd[1], &i, sizeof(int));
wait(&status);
printf("parent fd[0]: %d, fd[1]: %d\n", fd[0], fd[1]);
exit(0);
}
else
{
printf("fork error!!!\n");
exit(1);
}
}
输出:
hart 2 starting
hart 1 starting
init: starting sh
$ prime
prime: 0
child fd[0]: 3, fd[1]: 4
parent fd[0]: 3, fd[1]: 4
$
PS:由于水平有限,无法给出这一现象的根本原因,如果有大佬知道的话,帮忙补充,后续如果我补充了这一块的知识,会补上说明。
第二种情况:pipe(fd)在前,pid = fork()在后。
此时父进程创建管道,父子进程拥有相同的文件描述符,并且指向同一个管道,子进程读取到父进程输入的数据。