实现user_shell
1.bug修复
在实现shell
应用程序时,我发现了许多bug
,在完成shell
这个应用程序之前先把之前代码的bug
修复了
1.1 sys_fork 错误修复
在之前的实现中,
trap
页中内核栈忘记覆盖了,因为sys_fork
会去空闲任务数组中拿到一个,此时内核栈和父进程不一样,因此trap
页中内核栈的地址需要重新赋值:cx_ptr->kernel_sp = np->kstack;
初始化任务上下文,这里我只是用
tcx_init
这个函数来初始化了,之前是直接赋值,这里优化了一下
1.2 sys_exec 错误修复
int exec(const char* name) |
- 在
exec
实现时,我们是没有重新分配trap
页,我们只是根据传进来的elf
文件对程序进行覆盖替换掉原来的,因此只是重新分配了根页表,然后对地址空间重新进行了映射。本质上exec
前后都还是同一个进程,只是地址空间不同了,trap
页中的某些数据不同了:1. 程序入口地址e_entry
2. 用户栈地址u_stack
。因此trap
页中需要替换的也就是这两个部分,其他是不需要修改的,比如内核栈、trap_handler
地址什么的都是没变的,不需要覆盖。同时由于trap
页没有被重新分配物理页,只是改变了映射的地址空间,因此此页不能被释放掉
- 在
proc_freepagetable
中,trap
页物理内存是否释放的标志位修改为0,不能被释放掉
1.3 sys_read 问题修复
- 之前的实现中,没有去调用
schedule
函数,这样造成的后果就是程序会阻塞在sys_read
中出不去,加上调度后的逻辑就是sys_read
在读取串口的字符,如果此时没读到就调度执行其他进程,重新调度回sys_read
时再判断有没有读到字符,这样就不会阻塞在这里了。
2. user_shell 实现
内核在执行user_shell
这个应用程序时,应该会有一个initproc
的初始化进程,user_shell
是由这个初始化进程fork
来的,初始化进程负责拉起user_shell
和回收结束执行的子进程的资源。初始化进程是内核默认加载执行的第一个应用程序。在这里我们直接sys_exec
来执行user_shell
,因为还没实现子进程的资源回收机制,因此initproc
的程序如下:
|
在内核中手动拉起此初始化进程,:
//加载进程 |
接下来实现user_shell
:
|
user_shell
的逻辑就是从键盘获取输入,将读到的字符放进line
这个字符数组中保存,如果这个字符是键盘上的enter
键,则fork
一个子进程,让子进程通过sys_exec
来执行用户通过键盘输出的可执行程序;如果是键盘上的backspace
键,意味着删除一个字符,在c语言中\b
是退格字符,当它被打印时,它会导致光标向后移动一格,覆盖先前打印的字符。具体而言,printf("\b \b");
会打印一个退格字符,然后打印一个空格,最后再打印一个退格字符。这会导致光标向后移动一格,然后再向前移动一格,从而导致看起来好像什么都没有打印一样。- 这里用到了一个函数
strncat
,这个函数的作用就是用于向一个字符串的末尾拼接字符,具体实现如下:
void strncat(char *dest, const char *src, int n) { |
3.测试
在上面的测试中,可以看见user_shell
由initproc
拉起,然后我测试了一下字符输入与删除,接着输出xec
然后按enter
键,此时会去执行xec
这个子进程,此进程会循环打印exec!
,测试成功!
4. 参考链接
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 TimerのBlog!