引言

本章导读

本章不同于前面几章对OS框架的大量修改,主要着力于增加OS对于进程管理的支持。因此内容和难度相比前面都会轻松很多~~.

支持了页表之后,我们的操作系统在硬件上的支持就告一段落了。但是目前我们应用测例的执行方式还是十分机械化的,并且无法和用户交互。目前为止,所有的应用都是在内核初始化阶段被一并加载到内存中的,之后也无法对应用进行动态增删,从用户的角度来看这和第二章的批处理系统似乎并没有什么不同。

于是,本章我们会开发一个用户 终端 (Terminal) 或称 命令行 应用(Command Line Application, 俗称 Shell ) ,形成用户与操作系统进行交互的命令行界面(Command Line Interface),它就和我们今天常用的 OS 中的命令行应用(如 Linux中的bash,Windows中的CMD等)没有什么不同:只需在其中输入命令即可启动或杀死应用,或者监控系统的运行状况。这自然是现代 OS 中不可缺少的一部分,并大大增加了系统的 可交互性 ,使得用户可以更加灵活地控制系统。

我们想一下shell执行命令的过程。首先,shell必须支持读入用户的输入,并且如果我们在shell之中运行一个测例程序,它需要创建一个新的进程来执行这个命令对应的执行流。这里shell本身对于OS来说,也是一个进程。这就意味着我们需要支持进程创建进程的系统调用。实际上,在第四章添加了页表支持之后,现在我们可以开始实现几个进程非常关键的系统调用了,它们都是大家在课堂上已经耳熟能详的函数:

sys_read(int fd, char* buf, int size): 从标准输入读取若干个字节。
sys_fork(): 创建一个与当前进程几乎完全一致的进程。
sys_exec(char* filename): 修改当前进程,使其从头开始执行指定程序。
sys_wait(int pid, int* exit_code): 等待某一个或者任意一个子进程结束,获取其 exit_code。

实践体验

获取本章代码:

$ git checkout ch5

在 qemu 模拟器上运行本章代码:

$ make test BASE=1

# ....
app list:
ch2b_exit
ch2b_hello_world
ch2b_power
ch2b_write1
ch3b_sleep
ch3b_sleep1
ch3b_yield0
ch3b_yield1
ch3b_yield2
ch5b_exec_simple
ch5b_exit
ch5b_forktest0
ch5b_forktest1
ch5b_forktest2
ch5b_getpid
ch5b_usertest
usershell
C user shell
>>

不出意外,你将最终运行进入 C suer shell,这里,你可以输入 app list 中的一个应用,敲击回车之后就可以运行。其中 ch5b_usertest 打包了很多应用,只要执行它就能够自动执行所有基础测试:

>> ch2b_exit
Shell: Process 2 exited with code 1234
>> ch2b_hello_world
Hello world from user mode program!
Test hello_world OK!
Shell: Process 3 exited with code 0

当应用执行完毕后,将继续回到shell程序的命令输入模式。另外,这个命令行支持退格键。

本章代码导读

本章对于框架没有大量修改的代码。由于添加的系统调用是针对进程方面的,除了在syscall.c之中添加了相关接口的定义之外,主要函数的实现都在proc.c之中完成。此外,为了方便大家理解本章的进程调度部分内容,加入了queue.c文件,定义了一个就绪进程的一个队列。

我们已经完成了对上述几系统调用的支持,在开始本章的练习之前,大家需要仔细研究它们的实现细节,可以复习课堂上的知识,并且大大降低练习的难度。