Linux内核分析:Linux内核学习总结

  

          这学期选了孟老师的《Linux内核分析》这门课,在学习之前听说这门课很难,通过也不是很容易,但我还是选了这门课。课程不容易学,含金量才会大,才会有收获。现在,学习这门课已经两个月了,我的收获很大,特别是以前接触《计算机组成原理》,《操作系统》这两门课时遇到的很难理解的地方,通过孟老师的Linux内核讲解,使我对以前的盲点有了豁然开朗的感觉。我感觉孟老师开始的网上课堂很不错,有种另辟蹊径的感觉,课程内容上传到云课堂上,一方面可以节约课堂上讲授时间,将更多的时间放在答疑方面,另一方面通过新型的授课方式,方便学生课后温习、回顾、吸收、理解。

         通过这两个多月的学习,我掌握了Linux内核在计算机底层工作过程中的具体作用,掌握了进程调度,进程切换的原理,特别是进程在切换过程中需要从用户态切换到内核态,通过调用内核函数来完成进程切换的过程。对于多任务的情况,Linux内核会通过do_fork()来完成子进程的创建,如下:


1、分析汇编代码理解计算机是如何工作的

        这部分主要是通过理解反汇编C代码,生成汇编程序,深入代码底层了解程序的在计算机中的运行过程。掌握计算机的基本体系结构。了解计算机的基本原理是存储程序和程序控制。当计算机在运行时,先从内存中取出第一条指令,通过控制器的译码,按指令的要求,从存储器中取出数据进行指定的运算和逻辑操作等加工,然后再按地址把结果送到内存中去。接下来,再取出第二条指令,在控制器的指挥下完成规定操作。依此进行下去。直至遇到停止指令。
        汇编代码的执行,都是每次一条指令一条指令的执行。寄存器EIP中存储下一条将要执行指令的起始地址,EBP指向栈开始地址,ESP执行栈顶地址。累加寄存器用来存储运算过程中产生的临时变量。整个代码执行过程,就是取一条指令,执行完这条指令再取下一条指令。


2、一个简单的时间片轮转多道程序内核代码

        这部分主要是熟练进程调度的原理过程,掌握Linux内核的工作原理。决定进程是否要进行调度是由my_time_handler决定的,这个函数是设定时间片大小的函数,当时间片用完之后,进程便开始调度;而且这个函数还决定着进程上下文切换的的设定:

  • my_need_sched = 1;当 my_need_sched = 1时,my_process()函数才能被允许执行my_schedule()函数调度。
  • my_schedule()函数内容实现了进程调度算法,当需要进程调度时,my_schedule()便选择一个处于就绪状态的进程,用于进程调度。


3、跟踪分析Linux内核的启动过程

         这部分主要是熟练start_kernel的原理过程,掌握Linux内核的工作原理。掌握内核进程的init()过程中是怎么调用的。通过对start_kernel的源代码分析,体会进程在切换过程中操作系统对进程运行环境的管理。事实上有时候我们可以把它叫做运行框架,也可以说是上下文,现场环境等等。

         实际上这些东西指的都是程序运行时候的堆栈,运行内存空间的地址,寄存器值的集合。中断上下文和进程上下文的切换是操作系统的“两把剑”,熟练掌握和理解中断上下文和进程上下文,我们才能充分理解Linux内核源码中进程调度的原理,才能为后续学习Linux操作系统做好基础准备。


4、扒开系统调用三层皮

中断处理过程:
  • 中断指令interrupt(ex:int 0x80)开始进行系统调用;
  • 保存当前CS:EIP,SS:ESP,eflags的值到内核堆栈,同时加载了中断服务程序的地址到CS:EIP以及内核堆栈栈顶指针到SS:ESP中。Int指令完成上述操作过程。
  • 内核代码,完成中断服务:
    • 发生进程调度,则保存调度时的现场,进行调度,完成调度后再恢复现场;
    • 不发生进程调度,则恢复之前的保存现场:iret - pop cs:EIP/SS:ESP/eflags from kernel stack.

5、分析system_call中断处理过程

    这部分内容主要是掌握系统调用具体步骤:
  1. 程序调用库的封装函数;
  2. 调用软中断int $0x80进入内核;
        在内核中首先执行system_call()函数,接着根据系统调用号在系统调用表中查找到对应的系统调用服务例程;执行该服务例程;执行完毕后,转入ret_from_sys_call例程,从系统调用返回。

6、分析Linux内核创建一个新进程的过程

        这部分内容主要是理解fork()函数创建一个子进程的过程:

  1. 首先是fork()函数执行系统调用,调用system_call();
  2. system_call进而根据进程调用号找到sys_fork()函数所在的位置;
  3. 进而执行sys_fork()函数;
  4. 之后,sys_fork()函数开始调用do_fork()函数,do_frok()函数位于fork.c文件中,它完成了子进程创建的大部分工作;
  5. 最后do_fork()函数调用copy_process()函数,copy_process()函数完成了很多工作,比如,为新进程分配内核堆栈,检查进程数目是否超限,区分父、子进程,获取子进程PID等操作。


7、Linux内核装载和启动一个可执行程序的分析

         这部分内容主要是理解Linux内核装载一个可执行程序的过程:

  1. 首先创建新进程;
  2. 该进程通过execve()系统调用执行指定的ELF文件;
  3. 再调用execve()系统调用对应的内核的入口函数sys_execve();
  4. sys_execve()服务例程修改当前进程的执行上下文。


8、理解进程调度时机、进程调度、进程切换

        这部分内容是Linux内内核分析阶段的重中之重,主要内容有进程切换、任务切换、上下文切换;上面的进程调度时机的分析,可见,在进程完成调度过程中,有三个部分是很重要的:schedule(),context_switch(),switch_to()。其中:
  • schedule()是进程调度的开始,在这里会获取进程调度的队列,将需要调度的进程加载到进程调度队列上,初始化为高优先级,优先运行。保存将被置换出去的进程。
  • context_switch()的主要任务是完成进程上下文的切换,包括保存现场,该函数主要是调用switch_to()完成进程上下文的切换。
  • switch_to()的主要任务是具体完成进程上下文的切换,利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程,该函数的主要部分是嵌入式汇编部分,完成内核堆栈的切换。


      学习《Linux内核分析》这门课,使得今后在学习或工作中,可以更好地帮助我们理解编程语言底层的实现细节,特别是对以后想从事C/C++开发工作的我来说,学习深入学习Linux内核就显得尤为重要。

      学习了孟老师的这门课,真心感觉选这门课很值得。加油!


你可能感兴趣的:(Linux内核分析:Linux内核学习总结)