对linux内核学习的一点感受

徐晨 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

跟着这门课不知不觉已经学了8个星期了,对Linux内核有了一个基本的认识,可以说是入门了。既然是课程总结,我大概描述一下这个课程,供对Linux内核感兴趣的同学参考。

这门课没讲什么

  1. 在学习操作系统的时候,我们知道了操作系统将CPU抽象为进程,将内存抽象为虚拟内存,学习了进程的调度算法,内存页面的置换算法,这门课并没有关注这些算法;
  2. 操作系统的主要功能就是为用户屏蔽硬件的操作细节,帮助用户管理计算机系统的各种资源,同步机制是我们处理并发任务和进行资源管理的重要手段,关于原子操作信号量自旋锁等内容该课程中没有讲解;
  3. 在操作系统原理课程中没有着重讲解的各种设备驱动程序实际上占据了Linux内核代码的大部分比例,这门课并没有这部分内容;
  4. 没有讲解文件系统的结构与实现,VFS等

这门课讲了什么

  1. 对于要研究Linux内核的人来说,X86汇编语言是你必须要面对的第一关,因为操作系统需要大量对寄存器的操作,这是体系结构相关的操作,所以必须用汇编语言来解决,这门课在一开始就讲解了X86汇编,并在后面的课程中不断巩固,这点对于阅读内核源码非常有用。
  2. 该课程用一个简单的演示内核myKernel来说明Linux是如何启动的,包括一个进程是怎样描述的(PCB信息),0号进程(idle)的创建与演化,1号进程init的创建与加载,2号进程kthreadd的创建等等。这可以使我们从高处对Linux内核有个大概的认识,并且课中手把手的源码阅读可以让人对减少对结构复杂的内核代码的恐惧。
  3. 我们日常使用内核,其实大部分功能都是使用它的系统调用,比如从创建一个新的进程fork,装载程序execve,到输入输出,时间查询等等。因此我们研究内核,很大一部分都是在研究如何实现这些系统调用。这门课在8周中花了两周时间来讲解系统调用在内核中是如何进行的。如果把进程创建可执行程序的装载也算作系统调用的讲解的话,那实际上占了课程的一半。所以课程的设置正体现了这些系统调用在内核构成中的重要性。课中提供了一个试验环境MenuOS,该系统实现了一个命令行菜单系统,我们只需要添加我们希望执行的功能函数到菜单就OK了,同时利用Qemu和gdb,我们跟踪了各种系统调用的执行过程。
  4. 虽然这门课没有将具体的调度算法,比如Linux内核中著名的完全公平队列CFS,但对于进程调度来说,除了调度算法,还有两个重要问题,那就是进程的调度时机与切换过程,该课程花了一节课来讲解schedule()函数的实现。
  5. 我们不仅需要学习Linux内核的相关知识,更需要学习正确的人生观和世界观,这门课的精髓在于,不仅教会你如何分析Linux内核,更教你做事的方法论:“天下难事必做于易,天下大事必做于细”,对于代码量庞大无从下手的内核,我们从小处入手,步步为营,最终掌握全局。

其实上面的两部分内容已经包含了我对内核的理解,我认为内核中的重要部分基本上包含在了上面两部分中,这也是下一步我继续研究内核的努力方向。

我对Linux的认识的一点补充

前面已经讲了很多,我对内核的认识和了解也基本上包含在了上面两个部分,最后我还是谈一下系统调用
操作系统的主要功能就是为用户屏蔽硬件的操作细节,帮助用户管理计算机系统的各种资源,这样一来操作系统本身就需要拥有一定的特权来区别于用户应用程序,这就是内核态用户态的由来。当然这个机制需要CPU的支持,X86系列处理器定义了R0 - R4四个特权级别,Linux中使用了其中的R0作为内核态,R4作为用户态。
内核态与用户态使用了不同的地址空间,他们通过系统调用的方式进行通讯。在32位下进程空间是2^32=4GByte,其中0-3G是用户空间,3G-4G是内核空间。首先,这个4G空间对应的是虚拟内存空间的概念,也就是说这个4G并不在物理内存中,而是通过页面置换不断部分进入或移出物理内存(缺页异常机制),而这个4G中,内核空间的1G空间是所有的进程所共享的,而其余3G是互不可见的。这样的地址设置加上中断机制,对于一个进程来说,就相当于独占了这个计算机。
RobertLove的书里说,CPU在任一时刻都处于下面三种状态中的一种:
1、内核态,运行于进程上下文,内核代表进程运行于内核空间;
2、内核态,运行于中断上下文,内核代表硬件运行于内核空间;
3、用户态,运行于用户空间。
孟老师在课上将中断上下文的切换和进程上下文的切换称为操作系统的“两把宝剑”,内核态正是运行于这两种环境中执行对中断、异常和系统调用的处理。我之前已经提到,系统调用是这门课的重中之重,从一开始,老师就提到“计算机工作的三个法宝”:存储程序计算机、函数调用堆栈和中断机制,系统调用也可以算是一种特殊的中断机制。我们日常使用系统调用都是通过调用封装好的C标准库函数来间接调用系统调用服务例程。这正是老师总结的“系统调用的三层皮”:API xyz –> 中断向量system_call –> 中断服务程序sys_xyz。这样就进入内核态,运行于进程上下文。

广告

我认为这门课程值得一听,有如下三个原因:
首先这门课的实操性质很强,上完一遍你一定会减少对庞大内核代码的恐惧感;
其次孟老师的经验丰富,讲解非常生动有趣,我已经决定继续跟孟老师的另外一门课了;
其次这门课听起来很方便,下载在手机上,很适合在碎片时间进行学习。

作业列表

这门课程的作业是通过撰写博客来记录自己对课程的理解和实验报告,下面就是我这8周的作业:

通过汇编一个简单的C程序,分析汇编代码理解计算机工作原理
将一个非常简单的小程序进行汇编,分析运行状态,从而理解函数调用堆栈的变化。

从mykernel来分析linux系统的启动过程
教你自己写简化的PCB结构,创建0号进程my_process,并利用其进行最简单的调度,进行进程的切换。

利用gdb分析从start_kernel到init启动的过程
利用MenuOS来跟踪并分析Linux Kernel启动时的最后一步,即从init/start_kernel()开始分析第一个用户态进程init是如何启动的这里给出一张调用关系图,细节可以看博文。
对linux内核学习的一点感受_第1张图片

使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
我在这节用了4个系统调用“打开文件open(系统调用号5),关闭文件close(系统调用号6),读文件read(系统调用号3),以及写文件write(系统调用号4)”,分别通过C库和嵌入式汇编直接调用int 0x80中断来实现了一个文件拷贝的操作。

分析system_call中断处理过程
我们深入系统调用的关键代码片段system_call,来理解一个系统调用内部是怎么完成的,总结的流程图如下,细节参见博文。
对linux内核学习的一点感受_第2张图片

分析Linux内核创建一个新进程的过程
这一节,我们深入fork的系统调用服务例程sys_clone,来观察它的实现细节。一个fork的执行过程如下所示:
libc fork() -> system_call -> sys_clone() -> do_fork() -> copy_process() {dup_task_struct; copy_thread } -> wake_up_new_task() -> ret_from_fork

Linux内核如何装载和启动一个可执行程序
这节我们分析了目标文件的格式,动态链接可执行程序的编译链接方法,如何装载和启动一个可执行程序即sys_execve的实现细节。

理解进程调度时机跟踪分析进程调度与进程切换的过程
这一节我们分析了进程的调度时机和切换过程,主要分析了schedule()函数的实现细节。

你可能感兴趣的:(对linux内核学习的一点感受)