这一部分要实现抢占式调度和进程间通信
前面的调度是进程资源放弃CPU,但是实际中没有进程会这样做的,而为了不让某一进程耗尽CPU资源,需要抢占式调度,也就需要硬件定时
但是外部硬件定时在Bootloader的时候就关闭了,至今都没有开启
而JOS采取的策略是,在内核中的时候,外部中断是始终关闭的,而在用户态的时候,需要开启中断
所以首先需要求改IDT,但是我在之前把256个都弄好了
所以现在只需要在进入用户态的时候,保证外部中断是使能的就可以了,为了做到这一点,可以在每个进程初始化的时候就将外部中断使能位置位
在kern/env.c的env_alloc中
现在虽然中断使能已经打开,在用户态进程运行的时候,外部中断会产生并进入内核,但是现在还没有能处理这类中断
所以需要修改trap_dispatch,在发生外部定时中断的时候,调用调度器,调度另外一个可运行的进程
在跑make run-spin的时候,总是遇到bug,发现父进程调用sys_env_destroy没能把子进程杀死
找了半天,发现传入sys_env_destroy的参数是子进程的进程id,而在内核的系统调用sys_env_destroy中,却莫名其妙的变成了父进程的进程id,最后发现是syscall函数写错了
这一句的参数应该是a1,我却写成了curenv->env_id
这个问题显然是自lab3就有的,但是到现在才发现是错的,而且之前一直没有出错。。。
实现IPC
IPC是计算机系统中非常重要的一部分,而JOS选择了一种看起来挺别扭的方法来实现
两个进程需要通信的话,一方要发起recv,然后阻塞,直到有一个进程调用send向正在接受的进程发送了信息,阻塞的进程才会被唤醒
而JOS中,可以允许传递两种信息,一是一个32位整数,另外一个就是传递页的映射,在这个过程中,接收方和发送方将同时映射到一个相同的物理页,此时也就实现了内存共享
然后将这两个功能实现在了同一个系统调用中
首先看内核中的sys_ipc_recv函数。当一个进程试图去接收信息的时候,应该将自己标记为正在接收信息,而且为了不浪费CPU资源,应该同时标记自己为ENV_NOT_RUNNABLE,只有当有进程向自己发了信息之后,才会重新恢复可运行
将自己标记为不可运行之后,调用调度器运行其他进程
而内核中的sys_ipc_try_send的实现则相对来说麻烦很多,因为有很多的检测项,包括权限是否符合要求,要传送的页有没有,能不能将这一页映射到对方页表中去等等
如果srcva是在UTOP之下,那么说明是要共享内存,那就首先要在发送方的页表中找到srcva对应的页表项,然后在接收方给定的虚地址处插入这个页表项
接收完成之后,重新将当前进程设置为可运行,同时把env_ipc_recving设置为0,以防止其他的进程再发送,覆盖掉当前的内容
以上的两个函数都是系统调用,而在用户态下的函数调用需要另外实现
至此,整个lab4就实现完成了,好累。。。