本节实现重新写内存分配管理。主要有两个知识点,一个是内存管理,一个是系统调用,现在先实现内存管理,然后实现系统调用,预计至多4个课时。
预计内存管理就是写比较简单的,系统调用都是这样预见的。
要实现内存管理,就要实现多道程序并行运行,不然内存管理没法测试,因此要先实现Exec。
userprog/syscall.h里面写的系统调用号为:
/* system call codes -- used by the stubs to tell the kernel which system call
* is being asked for
*/
#define SC_Halt 0
#define SC_Exit 1
#define SC_Exec 2
#define SC_Join 3
#define SC_Create 4
#define SC_Open 5
#define SC_Read 6
#define SC_Write 7
#define SC_Close 8
#define SC_Fork 9
#define SC_Yield 10
而且在这个文件里,包括exec,join,fork,read等等耳熟能详的函数,系统调用接口,不过这是头文件,并没有它的详细实现,并且没有对应的源文件,大概率是让我们实现这个源文件。
文档中说,进程开启入口写在userprog/exception中的ExceptionHandler(ExceptionType which)函数里,这个which是一个中断性质的参数,被引入后,在SyscallException中实现进程开启,否则就是其他错误中断。
中断开启后,文档中说,我们需要指定一个文件,来运行文件,那么如何指定呢?在ExceptionHandler上面的注释里,我们知道,它是直接在CPU的寄存器中读取一些数据,详细格式如下:
// system call code -- r2
// arg1 -- r4
// arg2 -- r5
// arg3 -- r6
// arg4 -- r7
当r2值为CS_Exec的话,我们就可以在这个分支里进行进程开启,因此把SyscallException函数设计成一个switch类型的,比较直观:
void
ExceptionHandler(ExceptionType which)
{
int type = machine->ReadRegister(2);
if (which == SyscallException) {
swhich(type){
case SC_Halt:
DEBUG('a', "Shutdown, initiated by user program.\n");
interrupt->Halt();
break;
default: break;
}
} else {
printf("Unexpected user mode exception %d %d\n", which, type);
ASSERT(FALSE);
}
}
它本来就实现了SC_Halt,这为我们提供了一个模板,我们写的函数应该都是在syscall.c里面,并且引用syscall.h头文件,这里有接口。
然后,我们可以看到,exec函数只有一个字符串作为参数,返回的是一个spaceID类型。
即便是字符串能用,该如何调用?它应该是一个二进制文件,二进制文件的话,怎么着也得再调用几个函数才行。
在thread中,包含了thread和它的实现,但是仅仅是一个线程类,线程类的话,它并不是进程 ,而我们要造一个进程,除非有一个进程类。然而线程类包含了我们如何工作的,可以先看一看线程类,理解fork是如何工作的。
fork是相当难于理解的,先执行stackallocate函数,它是分配栈的函数,然后凭空出现一个interrupt,这个 应该是一个全局变量,它是中断,它setlevel函数使用,为了关中断,又一个scheduler变量,大概也是一个全局变量 ,它使用readytorun(this),大概就能跑了。这个scheduler在system.h被extern,其实它有一个自己的类,先看看这个scheduler类,它是程序可以运行的关键。
scheduler里面的ReadyToRun里面仅仅是thread的setStatus(Ready),然后在list里面去append,这个大概就是一个管理表的功能,而它的运行,实际上还是在thread类里面。thread里面的setStatus代表可以运行的函数。在run函数里也是这样,只不过它是把前面的那个线程的上下文存储起来了,然后调用新线程的setStatus,
现在还不知道其原理,但是可以预测的是,它在一开始输出什么参数,里面是文件,然后处理一下,把它放到一个公共table表,然后把相应index压入一个寄存器中,再执行中断,那么在中断里就可以获取这个index,进而获取这个文件名,然后开始执行。关键是里面有一个SWITCH(oldThread, nextThread)函数,它的实现是在汇编代码中,好了,现在看thread里面的东西。
但是这个setstatus设计的相当之短,它只有一个status = st,因此说,线程的切换,全在SWITCH里面,既然不会SWITCH,就丢锅给SWITCH。
其实SWITCH也是在这个文件夹里面的,看了一下,它的头文件里面满满的数的定义。在.s文件里面,还真的是二进制文件。。。这里就有一个问题,nachos是在linux里面模拟了一个系统,但是对于CPU的调用还是同一个CPU吧,毕竟汇编代码用到就是CPU里面的东西,它交换数据,还是什么东西,最后一个j,大概就是跳到对应的地址,开始执行。
执行的话,可能是调用machine->Run(这个二进制文件)来实现的,至于文件里是写的什么,暂不做考虑。