《Linux内核分析》期末总结
20135313吴子怡.北京电子科技学院
Chapter1 往期博客传送门
(1)计算机是如何工作的:Linux内核分析——第一周学习笔记
(2)操作系统是如何工作的:Linux内核分析——第二周学习笔记
(3)Linux系统启动过程:Linux内核分析——第三周学习笔记
(4)系统调用的方法:
Linux内核分析——第四周学习笔记
Linux内核实验作业四
(5)分析system_call中断处理过程:
Linux内核分析——第五周学习笔记
实验作业:使gdb跟踪分析一个系统调用内核函数
(6)分析Linux内核创建新进程的过程:
Linux内核实验作业六
Linux内核分析——第六周学习笔记
(7)Linux如何装载和启动一个可执行程序:
Linux内核实验作业六
(8)理解进程调度时机跟踪分析进程调度和进程切换的过程:
Linux内核分析——第八周学习笔记
chapter2 知识点梳理
1. 计算机是如何工作的?
存储程序计算机工作模型:冯诺依曼体系结构 X86汇编基础:CPU的寄存器(通用寄存器、段寄存器、标志寄存器)、常见汇编指令、堆栈 汇编一个简单的C程序分析其汇编指令执行过程
2. 操作系统是如何工作的?
函数调用堆栈 借助Linux内核部分源代码模拟存储程序计算机工作模型及时钟中断 在mykernel基础上构造一个简单的操作系统内核 三个法宝:
- 存储程序计算机:所有计算机基础性的逻辑框架
- 堆栈:高级语言的起点,函数调用需要堆栈机制
- 中断机制:多道系统的基础,是计算机效率提升的关键
3. 构造一个简单的Linux系统MenuOS
- Linux内核源代码简介
•arch:支持不同的CPU的源代码,其中的关键目录包括:Documentation、drivers、firewall、fs、include等 •documentation:文档目录 •fs:文件系统 •init:内核启动相关的代码main.c、Makefile等基本都在该目录中。(main.c中的start_ kernel函数是Linux内核启动的起点,即初始化内核的起点) •kernel:Linux内核核心代码在kernel目录中。 •lib:公用的库文件 •mm:内存管理的代码 •scripts:与脚本相关的代码 •security:与安全相关的代码 •sound目录:与声音相关的代码 •tools目录:与工具相关的代码 •net:与网络相关的代码 •readme:介绍了什么是Linux,Linux能够在哪些硬件上运行,如何安装内核源代码等
- 构造一个简单的Linux系统
cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
qemu命令是模拟内核启动虚拟机,启动Linux内核需要三个参数(kernel、initrd、root所在的分区和目录),执行的第一个文件是init。 -kernel指明内核文件名 -initrd指明根文件系统,启动其中的init文件。(menuOS源代码编译->init->rootfs.img)其中rootfs.img 为根文件系统,目前只支持help、version、quit功能。 启动过程为:启动内核->启动init->启动进程
- 跟踪调试Linux内核的启动过程
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
4. 扒开系统调用的三层皮(上)
(一)用户态、内核态和中断处理过程
内核态:一般现代CPU有几种指令执行级别。在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别对应着内核态 用户态:在相应的低级别执行状态下,代码的掌控范围有限,只能在对应级别允许的范围内活动 中断处理是从用户态进入内核态的主要方式,中断/int指令会在堆栈上保存一些寄存器的值:如用户态栈顶地址、当前的状态字、当时cs:eip的值(当前中断程序的入口)
(二)系统调用概述
系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口 系统调用概述和系统调用的三层皮:xyz(API)、system_ call(中断向量)、sys_xyz(中断向量对应的中断服务程序)
(三)使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
使用库函数API获取系统当前时间 C代码中嵌入汇编代码的方法 使用C代码中嵌入汇编代码触发系统调用获取系统当前时间
5. 扒开应用系统的三层皮(下)
rm menu -rf //强制删除当前menu git clone http://github.com/mengning/menu.git //重新克隆新版本的menu cd menu ls make rootfs //rootfs是事先写好的一个脚本,自动编译自动生成根文件系统,同时自动启动MenuOS vi test.c //进入test.c文件 MenuConfig("getpid","Show Pid",Getpid); MenuConfig("getpid_asm","Show Pid(asm)",GetpidAsm); //在main函数中增加MenuConfig() int Getpid(int argc,char *argv[]); int GetpidAsm(int argc,char *argv[]); //增加对应的Getpid和GetpidAsm两个函数 make rootfs //编译
chapter 3 重点问题
传送门:http://www.cnblogs.com/paperfish/p/5293913.html
chapter 4 总结
在跟随网课学习过程中,我每周都过得很充实。虽然每周的课程时间都不长,但每分每秒都是精华。第一周课程基础知识的学习中,我掌握了计算机的结构--冯诺依曼体系结构以及x86汇编指令基础。尤其是x86汇编基础,可以说是贯穿了整个学习过程。除了知识的教学,老师还会布置与知识相关的实验,让我们通过自己动手实践,更快的掌握课堂所学并对课堂知识有着更加深刻的理解和掌握。?
第一周:对“计算机是如何工作的”理解
1、计算机是依据冯诺依曼体存储结构,依据其核心思想——存储程序计算机工作模型,按程序编排的顺序,一步一步地取出指令,自动地完成指令规定的操作。
2、计算机的工作过程其实集中在了CPU和为其传输数据的地址总线上。计算机执行的最底层的操作其实就是N种指令而已。
3、CPU可以抽象成一个for循环,总是从内存中执行下一条指令。内存负责保存指令数据,CPU负责解释执行指令、数据。内存与CPU通过总线连接。
4、CPU中一个关键寄存器IP总是指向内存中某块区域。计算机在工作时会先从内存中取出一条IP指向的指令给CPU执行,按指令的要求从存储器中取出数据进行指定的运算和逻辑操作,然后再把结果送到内存中去。
第二周:阐明对“操作系统是如何工作的”的理解
操作系统工作的基础:存储程序计算机、堆栈机制、中断机制。操作系统是管理电脑硬件与软件资源的程序,同时也是计算机系统的内核与基石。操作系统是管理计算机系统的全部硬件资源包括软件资源及数据资源;控制程序运行;改善人机界面;为其它应用软件提供支持等,使计算机系统所有资源最大限度地发挥作用,为用户提供方便的、有效的、友善的服务界面。操作系统是一个庞大的管理控制程序,大致包括5个方面的管理功能:进程与处理机管理、作业管理、存储管理、设备管理、文件管理。Linux系统有一个进程控制表(process table),一个进程就是其中的一项。进程控制表中的每一项都是一个task_struct结构,在task_struct结构中存储各种低级和高级的信息,包括从一些硬件设备的寄存器拷贝到进程的工作目录的链接点。进程控制表既是一个数组,又是一个双向链表,同时又是一个树,其物理实现是一个包括多个指针的静态数组。系统启动后,内核通常作为某一个进程的代表。一个指向task_struct的全局指针变量current用来记录正在运行的进程。
第三周:阐明对“Linux系统启动过程”的理解
sched_init()初始化函数内对0号进程,即idle进程进行初始化。然后rest_init()其他初始化函数,函数内将创建1号进程,即init进程。随后rest_init实际上就是start_kernel内核一启动的时候会一直存在,这个就叫0号进程;0号进程创建了1号进程kernel_init和其他服务线程。这就是内核的启动过程。
第四周:系统调用的工作机制
系统调用的工作机制就是系统调用经过封装包装成为一个封装例程,然后交由Libc库,被Libc库引用保留,然后Libc库再将其提供给API去用。用户通过API间接地使用系统调用各种功能。即是说,系统调用是通过库函数封装的内核态的功能操作。
它的三层皮是:API、中断向量、中断服务程序。
第五周:“系统调用处理过程及到中断处理过程的推广”
具体的系统调用与系统调用号绑定,然后都记载在一个系统调用表内,每次使用系统调用时都是通过这样的绑定关系,由系统调用号去找系统调用表然后查找到所对应的系统调用的位置。
同理,中断处理过程也是一样的,它也是经由中断向量号作为索引去查表,然后执行相应的具体的中断处理程序去处理中断。
简而言之就是“两个号&两张表”。
第六周:“Linux系统创建一个新进程”的理解
Linux通过复制父进程来创建一个新进程,通过调用do_fork来实现并为每个新创建的进程动态地分配一个task_struct
结构。为了把内核中的所有进程组织起来,Linux提供了几种组织方式,其中哈希表和双向循环链表方式是针对系统中的所有进程(包括内核线程),而运行队列和等待队列是把处于同一状态的进程组织起来。
fork()函数被调用一次,但返回两次。
以下是进程创建流程图:
可以通过fork,复制一个已有的进程,进而产生一个子进程,新进程几乎但不完全与父进程相同。子进程得到和父进程用户级虚拟地址空间相同的一份拷贝,包括代码段,数据段和bss段,堆以及用户栈。子进程还获得和父进程任何打开文件描述符相同的拷贝,最大的区别就是在于他们拥有不同的PID.
第七周:“Linux内核装载和启动一个可执行程序”
linux通过sys_execve()系统调用从文件系统中读取、识别并加载elf。
调用sys_execve后,执行过程:
1 |
|
根据elf的库类型,elf_entry是不一样的。load_elf_binary通过解析器将不同的入口地址写入。
<<<流程图:(execve–> do——execve –> search_binary_handle –> load_binary)
<<<堆栈变化图(参数块和环境块如何被传到新进程):
第八周:对“Linux系统一般执行过程”的理解
在调度时机方面,内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度。schedule()函数实现进程调度,context_ switch完成进程上下文切换,switch_ to完成寄存器的切换。用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
我对Linux内核分析有这样一些简要的总结理解(其他详细内容都可以再每周博客中看到我详细的理解)
Linux系统分为用户态与内核态,像一个球有内外之分,而在其中运行的叫做进程。沟通内核态与用户态的叫做系统调用,而系统调用是通过中断处理程序,API(应用程序编程接口)和不同的服务程序实现的。进程的创建是由do_fork()函数实现的。Linux系统的一般执行过程可以抽象为正在运行的用户态进程X切换到运行用户态进程Y的过程
1. 正在运行的用户态进程X 2. 发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack). 3. SAVE_ALL //保存现场 4. 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换 5. 标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行) 6. restore_all //恢复现场 7. iret - pop cs:eip/ss:esp/eflags from kernel stack 8. 继续运行用户态进程Y
学习以来,我觉得我最重要的收获就是自学能力的提高,作为大学生,自学能力尤为重要,而MOOC恰恰培养我们的自学能力,这点收获可以说受益终生。
说起遗憾:我觉得我在网课中的学习,每一周都是很有规律而且很充实的。但是最后的结尾有一些匆忙仓促,让我在期待新一周的知识的时候突然就结束了。很是猝不及防。我觉得Linux的学习就像我们平时生活中技能的学习。老师领进门。还需要学生自己的体会和深入。我还会继续看书学习的。
chapter 5 附录
作者:吴子怡
学号:20135313
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000