Linux系统模型
Linux系统模型如下图所示
应用程序通过函数库提供的API,或者shell脚本,或者直接系统调用来与内核交互,但本质上都是使用系统调用来使用内核提供的服务。
内核主要提供了以下几种服务:
- 中断。通过中断,内核可以将原来的控制流转为中断处理程序的控制流.系统调用属于中断,用户程序可以使用系统调用来进行一些操作,如文件读写;
- 进程管理。内核使用PCB来描述和管理一个进程.其中包括进程的创建,调度,销毁等;
- 内存管理。内核将内存分为了内核态和用户态;
- 文件管理。内核使用了VFS来管理文件.VFS对应用程序,提供了统一的接口.对各个不同的文件系统提供了统一的接口;
- 设备管理。内核使用了LKM技术,使得设备的驱动程序可以动态地加入内核.在不重编译内核和重启系统的条件下对Linux系统内核进行修改和扩展。
具体案例
下面文件读取过程为例,介绍内核的运行过程:
要读取一个文件,首先需要使用open()系统调用打开该文件
在32位系统中,open()系统调用执行过程如下:
-
程序调用C库函数中的open()
-
使用int 0x80 指令,触发系统中断
-
内核先从idtr寄存器中找到idt,然后,根据传入的中断号0x80,找到中断描述符
-
从gdtr寄存器中找到gdt,然后根据中断描述符中的段选择符,找到段描述符
-
由于进程在用户态进行了系统调用,所以进程需要从用户态转为内核态。CPU从tr寄存器中获取tss段,找到内核态堆栈的指针和ss段,装载到eip和ss中
-
转到内核态堆栈后,将原来的eflags,eip,cs保存在内核态的堆栈中
-
根据中断描述符中的段偏移和段选择符,装载到eip和cs寄存器中,这样进程就找到了系统调用表所在的位置
-
然后根据系统调用号,找到sys_open()对应的系统调用,开始执行
-
sys_open()的具体工作
-
根据传入的参数,文件名,进行命名查找
-
如果找了该文件,得到文件控制块,然后根据文件的类型,调用对应的文件打开函数
-
文件需要将文件控制块复制到系统文件打开表中,如文件操作,读写指针的位置
-
在进程文件打开表中,有一个fd数组,fd数组中的项指向了系统打开文件表中对应的项。sys_open会找到一个空闲的fd,指向系统打开文件表。然后将fd返回
-
-
sys_open()的返回值会保存在eax中,然后使用iret指令恢复现场
当文件打开后,可以使用read()系统调用打开文件,过程如下:
- 和上文类似,read()实际上是调用了sys_read(),该函数需要三个参数:一个文件描述符fd;一个包含要传送数据的内存缓冲区地址buf;一个指定应该传送多少字节数的count。然后查找到相对应系统调用的位置
- 根据传入的文件描述符fd,找到系统文件打开表中的对应项,然后执行对应的read函数,最后将文件中的内容读取到内存缓冲区
- 最后进行系统调用返回
课程总结
孟老师的课,先通过传授一些汇编知识,帮助我扫清了阅读源码的障碍.然后通过巧妙的实验,让我对Linux的进程切换,系统调用过程有了深入的理解.李老师的课,通过阅读Linux内核的部分源码,我对操作系统的理解不再浮于概念,而是对中断过程,进程管理,设备管理,文件管理有了具体而形象的理解.但是可能是课时原因,对内存管理提及不多,希望李老师今后的授课中,可以加入内存管理的部分.