前言
通过学习《Linux操作系统分析》课程,自底向上,深入浅出,并通过结合具体例子进行原理分析,系统的了解了Linux操作系统的基本原理,包括Linux的初始化过程、零号进程的创建、系统调用的原理、进程的创建与切换、中断的原理、设备驱动等等。几乎涵盖了Linux操作系统最主要的功能,但各个部分并非完全独立工作而是互有交织,通过整理归纳所学知识构造概念模型,可以更加系统的认识Linux系统。
一、Linux系统概念模型
1. 初始化
- X86复位后工作在实模式下,该模式下CPU的寻址空间为1M。
- CS:IP的复位值时FFFF:0000,物理为FFF0,所 以主板设计者必须保证把这个物理地址映射到 BIOS芯片上,而丌是RAM上。
- 当运行BIOS上面的程序后,将在物理地址前 1KB内存中建立实模式的中断向量表,随后一部分被用来保存BIOS在启动阶段检测到的硬件信息。
- BIOS代码运行结束后,BIOS把MBR中的LILO第一部分代码和分区表加载到内存地址0X00007c00,最后跳转到LILO第一部分代码;
- LILO第一部分代码把自己加载到内存地址0X00096a00,并在内存地址0X00098000处设置实模式堆栈,然后LILO把自己的第二部分代码加载到内存地址0X00096c00,最后跳转到LILO第二部分代码;
- LILO第二部分代码把header.S代码,一共两个512字节(boot sector和setup)分别加载到内存地址0X00090000和0X0009200,同时把Linux小内核映像加载到内存地址0X00010000或者Linux大内核映像加载到内存地址0X00100000,最后跳转到header.S代码的setup代码(240行)。
- 最终调用start_kernel函数,执行最后的初始化,包括初始化内核时钟、初始化硬件中断、初始化任务调度、0号进程的创建、系统调用初始化等等,至此初始化完毕。
2. 初始化后
初始化完成后,用户可以开始执行自己的程序,但自己的程序可能采用多线程技术,可能需要与硬件打交道,这时就会涉及到系统调用、中断、进程创建与切换、设备驱动等功能。现在假设有一个用户程序,父进程创建了一个子进程,父进程负责从usb读取数据存入缓冲区,子进程负责将缓冲区中的数据写入txt文本,通过信号量进行通讯。
- 首先父进程通过调用fork函数创建了子进程,该fork函数为libc库中封装好的API,该库函数实际上会最终调用__do_fork函数,这个函数主要通过调用copy_process()来复制父进程的进程描述符、进程状态设置为TASK_RUNNING、采⽤写时复制技术逐⼀复制所有其他进程资源、调⽤copy_thread_tls初始化⼦进程内核栈、设置⼦进程pid等,然后调用wake_up_new_task将子进程加入就绪队列等待调度,最后系统调用返回。
- 父进程和子进程通过信号量进行通讯,子进程获取信号量发现缓冲区为空时,主动进入阻塞状态,此时发生进程切换。即内核会挂起正在CPU上运⾏的子进程,并恢复执行以前挂起的某个进程,切换过程可以分为两步,一是切换⻚全局⽬录以安装⼀个新的地址空间;二是切换内核态堆栈和进程的CPU上下文。
- 父进程会执行类似 fd = open("/dev/ttyS3", O_RDWR) 的语句打开串口设备,当串口没有数据过来时会阻塞进程,当有数据进来时会发生中断,此时先由硬件将cs:eip压入内核栈中,然后根据中断向量号计算找到中断描述符表中的相应中断处理函数入口,并将该入口地址加载到cs:eip中。接着执行中断处理函数,首先得保存现场,再进行数据传输处理,最后中断返回前调用一次schedule() 进行进程切换调度。此时父进程可能会得到运行,read成功读取数据,然后写入缓冲区中并更改信号量。
- 子进程一开始open创建一个txt文本,当被唤醒后从缓冲区中读取数据,然后调用write函数写入txt文本中,由于txt文本需要保存于硬盘中,因此必须有驱动程序来包装硬盘的所有底层操作,向上提供标准posix接口,所以我们只需要调用write函数即可将文本写入硬盘,由内核转化为相应的设备驱动程序对应的函数并执行。Linux的驱动层次结构及经典字符设备框架如下图:
二、心得体会
通过学习《Linux操作系统分析》,了解了Linux操作系统的基本原理,不仅可以让自己能够更好的使用Linux这个开发环境,更加强了对操作系统原理的认识,做一定的知识储备,壮大国内自主操作系统的后备力量,相信国产操作系统会越来越强。我觉得操作系统的流畅性非常关键,希望这门课程今后对这个方面多做一些研究和入门介绍。