Hw1 配套阅读:理解 NexOS 的 Shell 工作原理¶
阅读目标¶
- 作为
Hw1与实验一的配套阅读材料,帮助你理解 NexOS 用户态Shell的核心机制; - 理解 NexOS 用户态
Shell的基本原理与整体执行流程; - 掌握 Linux/UNIX 常见系统调用(如
fork、exec、wait、pipe、dup)在 Shell 实现中的具体用法; - 能够独立阅读并分析
user/sh.c源代码,实现包含内建命令、外部命令、重定向及管道功能的简易 Shell; - 学习编写 Makefile,并结合自动化测试方法对 Shell 功能进行验证与测试;
- 为完成
Hw1中与 Shell 相关的作业题,以及实验一中的后续实践任务打下基础。
章节一 NexOS Shell 源码阅读与原理¶
1.0 若干名词解释¶
1.0.1 Shell¶
Shell 是用户态的命令解释程序。它的作用不是“亲自完成所有工作”,而是:
- 读取用户输入的一行命令;
- 分析这行命令中包含的程序名、参数以及特殊符号;
- 通过系统调用创建子进程,并在子进程中执行目标程序;
- 在需要时负责搭建管道、设置输入输出重定向、等待子进程结束。
在 NexOS 中,Shell 本身也是一个普通的用户态程序,源文件位于 user/sh.c。
1.0.2 内建命令与外部命令¶
- 外部命令:以普通可执行文件的形式存在,例如
echo、cat、wc、grep、ls等。Shell 通过fork + exec启动它们,由子进程执行完成。 - 内建命令:直接由 Shell 自己处理,而不是再去
exec一个新程序。典型例子是cd和exit。
为什么 cd 必须是内建命令?因为 cd 要修改的是 Shell 进程自身 的当前工作目录。若 Shell 只是 fork 出一个子进程,再让子进程去执行“切换目录”,那么当子进程退出后,父进程 Shell 的目录并不会改变。
1.0.3 文件描述符、标准输入输出与管道¶
在 NexOS 中,普通文件、控制台、管道都通过文件描述符(fd)访问。在 Hw1 与实验一相关内容中,最重要的三个约定是:
0:标准输入(stdin)1:标准输出(stdout)2:标准错误(stderr)
大多数命令默认都从 fd=0 读取输入,向 fd=1 输出结果。因此:
- 如果把文件接到
fd=0上,就实现了输入重定向; - 如果把文件接到
fd=1上,就实现了输出重定向; - 如果把一个进程的
fd=1接到管道写端,再把另一个进程的fd=0接到管道读端,就实现了|管道。
1.1 NexOS 中 Shell 的启动过程¶
NexOS 启动完成后,内核会先进入第一个用户进程 init,其代码位于 user/init.c。该程序的核心逻辑非常简单:fork 一个子进程,并在子进程中执行 /sh。
因此,NexOS 中 Shell 的启动过程可以概括为:
- 内核启动后,进入用户态
init; init通过fork()创建子进程;- 子进程调用
exec("/sh", argv),把自己替换成 Shell 程序; - 父进程
init调用wait()等待这个 Shell 结束; - 若 Shell 退出,
init会再次fork + exec启动一个新的 Shell。
这意味着:在 NexOS 中输入
exit后,当前sh会结束,但随后init会重新拉起一个新的sh。因此你看到的现象通常不是“系统退出”,而是很快再次回到 Shell 提示符。
1.2 当前 user/sh.c 已实现的功能¶
课程给出的 user/sh.c 已经是一个能工作的最小 Shell。阅读该文件后,你可以发现它已经完成了如下工作:
- 使用
readline()从标准输入读取一整行命令; - 使用
parseargs()按空格和制表符切分参数; - 支持内建命令:
helpcd [dir]exit- 使用
makepath()处理命令路径: - 若命令本身以
/开头,则直接执行该绝对路径; - 否则自动在前面补一个
/,例如把echo变成/echo; - 对于普通外部命令,执行:
fork()创建子进程;- 子进程中调用
exec(path, argv); - 父进程中调用
wait(&status)等待子进程结束。
也就是说,当前的 sh 已经支持如下最基本的交互:
但它还没有实现以下功能:
- Shell 提示符中还不会显示当前目录;
- 不支持管道
|; - 不支持命令分隔符
;; - 不支持输入重定向
<; - 不支持输出重定向
>和>>; - 不支持引号、转义、通配符等更复杂的 Shell 语法;
- 不会像 Linux Shell 那样使用
PATH环境变量搜索命令。
注意:本仓库已经提供了
user/kill.c,因此kill 123可以作为普通外部命令运行;它不像cd那样必须写成内建命令。