编写一个程序,使用UNIX系统调用,通过一对管道在两个进程之间“乒乓”一个字节,每个方向一个字节。父级通过向parent_fd[1]写入字节来发送,而子级通过从parent_fd[0]读取字节来接收。从父级接收到一个字节后,子级通过写入child_fd[1]以自己的字节进行响应,然后由父级读取。您的答案应该在user/pingpong.c文件中。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
int child_fd[2], parent_fd[2];
char buf[1];
pipe(child_fd);
pipe(parent_fd);
int pid = fork();
if (pid == 0) {
close(child_fd[0]);
close(parent_fd[1]);
read(parent_fd[0], buf, sizeof(buf));
close(parent_fd[0]);
fprintf(2,"%d: received ping\n", getpid());
write(child_fd[1], "x", sizeof("x"));
close(child_fd[1]);
} else {
close(parent_fd[0]);
close(child_fd[1]);
write(parent_fd[1], "x", sizeof("x"));
close(parent_fd[1]);
read(child_fd[0], buf, sizeof(buf));
close(child_fd[0]);
fprintf(2,"%d: received pong\n", getpid());
}
exit(0);
}
结果
$ make qemu
riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o user/pingpong.o user/pingpong.c
riscv64-unknown-elf-ld -z max-page-size=4096 -T user/user.ld -o user/_pingpong user/pingpong.o user/ulib.o user/usys.o user/printf.o user/umalloc.o
riscv64-unknown-elf-objdump -S user/_pingpong > user/pingpong.asm
riscv64-unknown-elf-objdump -t user/_pingpong | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > user/pingpong.sym
mkfs/mkfs fs.img README user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_grind user/_wc user/_zombie user/_sleep user/_pingpong
nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 1954 total 2000
balloc: first 808 blocks have been allocated
balloc: write bitmap block at sector 45
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ pingpong
4: received ping
3: received pong
$
通过这个实验加深了对pipe,fork,read,write的理解。
fork非常神奇,从代码当前行程序进入两个线程同时执行,在父进程内pid为子进程pid,子进程内为0,通过这一特征我们的程序判断当前程序执行在哪个进程中。
pipe会打开一个通道,使得不同进程间可以通信,其会生成一对文件描述符,一段读一段写。
在第一次实现的时候理解错了题目的意思,在父进程和子进程中利用两个pipe同时收发,得到了如下结果。
$ make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ pingpong
34:: rerceeivecd epiinvge
d pong
$
因为笔者参考了网上其他博客的代码,又对比了lab里给的结果,思考为什么参考的代码中没有使用wait来避免并行,又想到lab的提示里并没有提示使用wait,发现理解错了题目意思。
同时可以得出结论,使用write,read在不同进程中操作pipe出的同一对文件描述符时是存在一定时序的。而操作不同的文件描述符对是并行的。
同时学到了fd在不用的时候尽量关闭,且使用完后尽快关闭。