前言:项目代码在slkfoiw/BUAA-OS-lab (github.com)的master分支,有些地方设计得可能不是很完美,也可能存在错误,欢迎批评指正
实现不带.b指令
在spawn函数里,当尝试打开文件(文件名存储在prog中)失败时,在prog字符串后面添加“.b”后再尝试打开一次
实现指令条件执行
step1:在gettoken里添加检测条件运算符”&&“,”||“的部分
step2:在parsecmd中处理指令:跟管道类似,需要调用fork分别处理运算符两边的两条指令,不同的是”&&“,”||“两边的指令不一定都要执行,而且右边的指令需要等左边的指令处理完,得到返回值才能确定是否处理,所以我在parsecmd函数添加了两个参数:
- is_executable:用来记录当前处理的指令能否执行
- return_value:用来记录处理到目前这条指令时,整个式子的返回值,有三种可能,我用宏定义如下:
//前面的指令还无法确定返回值,如cmd1 && cmd2 || cmd3 的cmd1执行成功,当前在处理cmd2 |
当is_executable==1时,return_value一定是UNCERTAIN;当is_executable==0时,return_value一定是CERTAIN0或CERTAIN1
fork出的child处理左边的指令
- 若能执行(is_executable==1),执行完后用ipc发送返回值给parent进程
- 若不能执行(is_executable==0),说明返回值已确定,当前指令不需要执行,直接通过ipc发送return_value给parent进程
parent进程处理右边的指令
- 接收child发来的返回值,并等待child进程结束
- 根据&&、||的不同设置 is_executable, return_value的值,然后继续调用parsecmd解析指令
实现更多指令
首先建三个文件touch.c、mkdir.c、rm.c,然后修改include.mk里,使它们能编译成可执行文件
touch:创建很简单,open一下,权限设置为O_CREAT就行。主要复杂在错误处理。判断文件所在目录是否存在,解析文件路径,找到最后一个”/“,”/“之前的部分就是目录,用open判断目录是否存在
mkdir:
修改serve_open函数,加入对O_MKDIR权限的处理
修改file_create函数,添加一个type参数,用来给文件的f_type字段赋值
rm:核心就是调用remove。不过需要判断是否为目录,这里要用到stat函数
实现反引号
gettoken里添加检测”`”的部分
parsecmd里添加处理“`”的部分,需要设置一个全局标记flag来判断是左反引号还是右反引号
实现注释功能
gettoken里添加检测”#”的部分
parsecmd里添加处理“#”的部分,后面的不要,直接开始处理指令就行了
实现历史指令
最耗时的指令之一,首先需要注意history是内置指令,也就是说不要新建一个history.c文件,然后调用spawn去fork出一个新进程处理。实现过程中需要注意:避免不同进程修改 跟history相关的数据 导致数据不一致。
跟history指令有关的函数如下(都在sh.c中):
void history_init();//在shell进程开始时初始化跟history相关的数据,如打开.mosh_history文件 |
注意凡是要修改数据的函数都需要在main所在进程里调用
实现一行多指令
跟管道指令类似,fork一下就行啦,不用创建管道了。
实现追加重定向
parsecmd里在读到一个“>”后加一个判断,后面一个token是否也为”>”
修改open函数实现追加功能,主要是要修改offset。
实现引号支持
修改gettoken函数,读到左边引号时一直读到下去,直到读到右边引号才能结束。我还加了一些异常处理,防止输入错误
实现前后台任务管理
另一个实现起来超级耗时的指令。同样,因为都是内置指令,不需要新建文件、新建进程。但是我还是在runcmd进程里处理的(因为需要解析指令参数),然后通过exit传回值,在main进程里修改与jobs相关的数据。
主要函数如下(都在sh.c中):
void add_one_job(int env_id, char *cmd);//添加一条后台指令 |