JOS
这一部分做三件事:
- 建立进程概念
- 异常处理,陷入内核
- 系统调用
进程和用户环境
JOS里进程是一个env结构,里面保存了简单的信息:页目录、寄存器缓存项(用于切换),连接指针(用于调度),状态等
首先提供基础设施:内核中保存进程的链表,这个数组需要初始化在物理内存上
显然创建一个进程,需要创建其对应的页目录,那就要在物理内存上分配对应的页目录和页表结构然后把他映射到虚拟内存里的页目录上。
要在进程上运行程序,就需要加载和解析ELF文件,同样分配物理页,载入elf文件各个segment,建立虚拟地址到物理地址的映射关系,然后修改寄存器的值为elf的entry。就可以运行起来elf上的程序了。
运行一个进程就只需要将进程结构内保存的寄存器值弹出到寄存器里。
trap与异常处理
一般来讲当异常发生,cpu会触发电信号,触发硬中断然后由中断控制器找到中断处理函数,但也有软中断的时候,通过指令提供的中断号结合IDT来查找到对应的中断处理函数。
中断发生,陷入内核,处理器根据TSS寄存器找到TSS结构,将栈寄存器SS和ESP分别设置为其中的SS0和ESP0两个字段的值,这样就切换到内核栈了
缺页异常与系统调用
缺页异常是一个中断,中断号是14,将这个号压入内核栈,然后call trap函数,在trap里dispatch到对应的处理函数就行。缺页的话就要分配物理页然后加载磁盘数据再建立映射关系,这一套操作目前由内核完成。
系统调用是一个中断,我们设置中断处理函数sys_call,并在trap内部根据传入的系统调用号做对应的dispatch到对应的系统调用处理。上面内核栈已经切换成功,其实cpu就在内核态了,接下来只需要压入触发系统调用对应的int指令需要的中断号和其它参数就可以call trap,然后dispatch到sys_call并传递参数(参数保存在一个trapframe结构中)。
Linux Kernel
进程
Linux内核把进程称为任务(task),进程的虚拟地址空间分为用户虚拟地址空间和内核虚拟地址空间,所有进程共享内核虚拟地址空间,每个进程有独立的用户空间虚拟地址空间。
进程有两种特殊形式:没有用户虚拟地址空间的进程称为内核线程,共享用户虚拟地址空间的进程称为用户线程。通用在不会引起混淆的情况下把用户线程简称为线程。共享同一个用户虚拟地址空间的所有用户线程组成一个线程组。
四要素:
a.有一段程序供其执行。
b.有进程专用的系统堆栈空间。
c.在内核有task_struct数据结构。
d.有独立的存储空间,拥有专有的用户空间。
如果只具备前三条而缺少第四条,则称为“线程”。如果完全没有用户空间,就称为“内核线程”。而如果共享用户空间映射就称为“用户线程”。内核为每个进程分配一个task_struct结构时。实际分配两个连续物理页面(8192字节),数据结构task_struct的大小约占1kb字节左右,进程的系统空间堆栈的大小约为7kb字节(不能扩展,是静态确定的)。