lab2: Simple xv6 shell
Simple xv6 shell
写一个xv6的shell,可以处理输入输出、重定向、管道。
shell文件应该位于user/nsh.c,然后更新makefile,自己实现的shell中的prompt应该使用符号@,以便于和原本的shell的$区分。
hints
- 不要使用动态内存分配如malloc,只能使用局部变量以及全局变量。(但是这个感觉有点骗测试的样子orz)
- 可以做出一些强制限制,比如命令的长度、参数的个数、每个参数的长度等(便于静态声明变量)
- nsh的测试文件为testsh,运行方式为testsh nsh
- 不用merge之前的实验,也基本用不到之前的代码。
- 不用实现testsh中没有的测试点,比如对括号以及引用的解析
- 参考K&R的C书, 比如5.12节的gettoken很有用
- xv6的shell复杂的多,可以借鉴。复用代码须注释说明
- 可以使用用户空间的库user/ulib.c
- 及时关闭不使用的文件描述符
- 所有的系统调用需要检查返回值,是否正确
- testsh会重定向shell的标准输出,在shell中把error以及debug信息输出到fd 2。
- 关注testsh中的one函数,可能会有用。
intend: 这个lab主要应该还是让用一用syscall,理解syscall过程。然后多用一用fork、exec、pipe,redirect等等吧。那些东西是父子进程共享的,那些是独有的。然后看代码。
管道:|
重定向:>、<
对于管道和重定向同时存在的情况,管道的优先级要高,那就是先处理管道、再处理重定向。
- 管道把命令分成了两部分,先执行管道左侧的命令,然后创建管道,写入数据;再执行右侧命令,从管道读数据,处理。
- 重定向负责把标准输入、输出、错误重定向到某个文件?
- 对于重定向输出,fork执行>左侧的命令的时候,设置重定向
- 对于重定向输入,fork执行<右侧的命令的时候,设置重定向
示例
echo hello there
echo something > file.txt
ls | grep READ
grep lion < data.txt | wc > count
echo echo hello | nsh
find . b | xargs grep hello
Bugs
- 遇到的第一个问题就是不太会处理字符串,然后也没有用sh.c中的其他函数。
- 第二个问题是不会读数据,最后发现构造的测试样例是每个命令以\n结尾。然后就是在读输入时,遇到\n就break,如果读到文件结束就退出,测试层序会多次调用nsh进程。
- 第三个问题就是我知道posix管道是半双工的,但是在父进程创建管道之后,写管道子进程没有关闭管道的读管道描述符;读管道子进程也没有关闭写管道描述符,然后造成了程序的undefined behavior,太蠢了。最后在ULK的page768找到解决方案。
- 最后一个坑是先写管道,写完之后要及时关闭写管道描述符,我只是在子进程中关闭了写管道描述符,但是在父进程中没有关(管道可以被多个进程读写、需要加锁保证数据可用),导致了后半段命令的子进程一直sleep,不能读到管道EOF,然后退出进程。 这个问题在源代码中反映为user/nsh.c中88-91行,debug之前是先两个wait,然后两个close。太蠢了。
xv6-book片段
- fork和exec分成两步的原因就是在这两个系统调用中间可以插入一个IO重定向的部分。这样就能在不修改代码的情况下实现多个程序的连接
- fork以及dup系统调用之后,父子进程对于同一个打开的文件描述符共享同一个文件偏移,因此下面的代码会输出hello,world到文件。这也是多个shell命令实现的方式。
- 2>&1:这个命令的意思是对于shell命令,把文件描述符2设置为文件描述符1的复制,即 close(2); dup(1); 这样做,可以让shell把标准输出和标准错误合并到同一个输出文件中。
- 管道pipe中,写管道结束之后,应该关闭管道的写一侧,否则管道的读一侧会一直阻塞。