20135201李辰希 《Linux内核分析》期中总结
李辰希 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一.学习心得与总结
心得:通过八周的视频学习和教材学习,我对于Linux内核有了初步的理解和掌握。在视频中老师为我们讲解了进程创建过程相关的关键代码、可执行程序的装载、进程上下文切换相关代码、Linux系统架构和执行过程等。Linux内核主要包括进程管理、内存管理、设备驱动、文件系统,从分析内核到了解整个系统是如何工作的、如何控制管理资源分配、进程切换并执行、各种策略和结构让系统运行时更有效率等,在日渐深入的学习中我愈发认识到内核源码数据结构和算法的精妙之处。在这两个多月的学习中,我发现我不仅收获了知识,并且我的自主学习能力也提高了不少。
总结:
一、计算机是如何工作的
存储程序计算机工作模型
- 冯诺依曼体系结构: 1.从硬件角度来看:CPU和内存,由总线连接,CPU中有一个名为IP的寄存器,总是指向内存的某一块:CS,代码段,执行命令时就取IP指向的一条指令,然后IP自加1,就指向下一条指令。
- 2.从程序员角度来看:即存储程序计算机,内存存储数据和指令,CPU就是一个for循环,总是在执行下一条指令,CPU负责解释和执行这些指令。
X86汇编基础
- CPU寄存器:通用寄存器+段寄存器+标志寄存器
- X86汇编指令 1.mov指令:b,w,l,q分别代表8位,16位,32位,64位
- 2.寻址方式:寄存器寻址,立即数寻址,直接寻址,间接寻址,变址寻址
- 3.汇编指令:push,pop,call,ret
- 4.leave
- 分析汇编指令片段
汇编一个简单的C程序分析其汇编指令执行过程
- 汇编一个简单的C程序
- C程序和对应的汇编指令
- 完整汇编程序执行过程分析
二、操作系统是如何工作的
函数调用堆栈
- 计算机三大法宝: 存储程序计算机工作模型、函数调用堆栈、中断
- 深入理解函数调用堆栈 函数调用框架
- 传递参数
- 保存返回地址
提供局部变量空间
- 堆栈相关寄存器:esp堆栈指针ebp基址指针,记录当前函数调用基址
- 堆栈操作:push,pop
- cs:eip:总是指向下一条的指令地址,执行call的时候,保存下一条指令地址到栈顶,然后cs:eip指向调用函数入口地址。
- 参数传递与局部变量
借助Linux内核部分源代码模拟存储程序计算机工作模型及时钟中断
- mykernel实验背后涉及的思想
- 利用mykernel实验模拟计算机硬件平台
- 由CPU和内核代码共同实现了保存现场和恢复现场
在mykernel基础上构造一个简单的操作系统内核
- C代码中嵌入汇编代码的写法
- 一个简单的操作系统内核源代码 1.mypcb.h:头文件
- 2.mymain.c: 内核初始化和进程的启动
- 3.myinterrupt.c:进程的切换
- 运行这个精简的操作系统内核
三、构造一个简单的Linux系统MenuOS
Linux内核源代码简介
- 操作系统两把宝剑: 中断上下文的切换、进程上下文的切换
- Linux内核源代码
构造一个简单的Linux系统
- 构造一个简单的Linux系统MenuOS
- qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
- qemu:启动一个虚拟机平台
- -kernel:给一个内核,操作系统
- -initrd:驱动所需的硬盘
- rootfs.img:放一个可执行文件由menuOS源代码编译成的init
- qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
跟踪调试Linux内核的启动过程
- 简单分析一下start_kernel
- 全局变量init_task,即手工创建的PCB,0号进程即最终的idle进程
- trap_init涉及到中断,初始化一些中断向量
- mm_init:内存管理模块初始化
- sched_init:调用进程调度初始化
- rest_init中:
- kernel_init:创建1号进程
- 第一个用户态进程:init_process,1号进程,找默认路径下的程序作为1号进程
- kthreadd:内核线程,用来管理系统资源
- 当系统没有进程需要执行时就调度idle进程,即0号进程,一直存在。
四、系统调用
用户态、内核态和中断
- 用户态&内核态(CPU执行级别) 内核态:高级别执行,可以使用特权指令,访问任意的物理地址。对应x86 0级
- 用户态:低级别执行,代码范围受到限制。对应x86 3级(x86CPU有0-3四个级别)
- 中断处理是从用户态进入内核态主要的方式
- 从用户态进入内核态:必须保存用户态的寄存器上下文
- 中断/int指令在堆栈上保存寄存器的值:用户态/内核态栈顶地址(ss:esp)、状态字(eflags)、cs:eip值(内核态时指向中断服务程序入口)
系统调用概述
- 系统调用的意义: 用户不管硬件编程、提高系统安全性、用户程序可移植
- API和系统调用(不是每个API都对应一个特定的系统调用): 1.API:应用编程接口,是一个函数定义
- 2.系统调用:通过软中断向内核发出明确请求
- 系统调用三层皮: API(xyz)、中断向量(system_call)、中断服务程序(sys_xyz)
使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
- 使用库函数API获取系统当前时间
- 使用C代码中嵌入汇编代码触发系统调用获取系统当前时间
- 传递了一个系统调用号 - eax
- 传递了参数 - ebx
给MenuOS增加time和time-asm命令
- 更新menu代码到最新版
- 在main函数中增加MenuConfig(一个命令一行,与上面的格式一样)
- 增加对应的time和time-asm函数(就是上周写的两个函数)
- make rootfs (rootfs是一个脚本,可以自动生成,编译)
使用gdb跟踪系统调用内核函数sys_time
- c:继续执行,停在断点处
- n/s:单步运行,s进入函数,n不进入
五、进程的描述和进程的创建
进程的描述
- 操作系统的三大管理功能: 进程管理(最重要的)、内存管理、文件系统
- 进程控制块PCB task_struct: 进程状态、进程打开的文件、进程优先级信息
- 进程描述符task_struct数据结构
进程的创建
- 进程的创建概览
- 道生一(start_ kernel....cpu_idle)
- 一生二(kernel_init和kthreadd)
- 二生三(即前面0、1和2三个进程)
- 三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)
- fork创建一个子进程
- fork在父进程和子进程各返回一次
- 创建一个新进程在内核中的执行过程
- 新进程从哪里开始执行?
- fork出来的子进程是从
ret_from_fork开始执行的,然后跳转到syscall_exit,从系统调用中返回。
- fork出来的子进程是从
六、可执行程序的装载
预处理、编译、链接和目标文件的格式
- 目标文件的格式ELF
- 静态链接的ELF可执行文件和进程的地址空间
- 默认加载起始地址是0x8048000
- ELF头部大小不同,程序入口点也将不同,程序入口在头部有定义:
Entry point address,即是可执行文件加载到内存中开始执行的第一行代码
七、进程的切换和系统的一般执行过程
进程切换的关键代码switch_to分析
- 进程调度与进程调度的时机分析
- 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
- 内核线程(只有内核态没有用户态的特殊进程)可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
- 用户态进程无法实现主动调度,只能被动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
- 进程上下文切换相关代码分析
- 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
- 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
- 进程上下文包含了进程执行需要的所有信息
- 用户地址空间:包括程序代码,数据,用户堆栈等
- 控制信息:进程描述符,内核堆栈等
- 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
- schedule()函数选择一个新的进程来运行,并调用
context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换- next = pick_ next_task(rq, prev);//进程调度算法都封装这个函数内部
- context_switch(rq, prev, next);//进程上下文切换
- switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程
Linux系统的一般执行过程
- Linux系统的一般执行过程分析
- 中断上下文的切换(中断和中断返回时CPU进行上下文切换)
- 进程上下文的切换(进程调度过程中,从一个进程的内核堆栈切换到另一个进程的内核堆栈)
- Linux系统执行过程中的几个特殊情况
- 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
- 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;//用户态进程不能主动调用
- fork:创建子进程的系统调用在子进程中的执行起点(next_ ip = ret_ from_ fork)返回用户态,进程返回不是从标号1开始执行,直接跳转到ret_ from_fork执行然后返回到用户态;
- 加载一个新的可执行程序后返回到用户态的情况,如execve,只是中断上下文在execve系统调用内部被修改了。
二.博客总结
1.20135201李辰希《Linux内核分析》第一周 计算机是如何工作的?
2.20135201李辰希《Linux内核》第二周 操作系统是如何工作的
3.20135201李辰希《Linux内核分析》第三次 构造一个简单的Linux系统OS
4.20135201李辰希 《Linux内核分析》第四周 扒开系统调用的“三层皮”
5.20135201李辰希 《Linux内核分析》第五周 扒开系统调用的“三层皮”(下)
6.20135201李辰希《Linux内核分析》第六周 进程的描述与创建
7.20135201李辰希 《Linux内核分析》第七周 可执行程序的装载
8.20135201李辰希 《Linux内核分析》第八周 进程的切换和系统的一般执行过程
7.20135201李辰希 《Linux内核分析》第七周 可执行程序的装载
8.20135201李辰希 《Linux内核分析》第八周 进程的切换和系统的一般执行过程
三.收获与遗憾
收获:学习了Linux相关知识,增强了自主学习能力。
遗憾:没有很多动手实践的机会,老师讲的有点快,跟不上节奏。

浙公网安备 33010602011771号