两个月Linux内核的学习,让我理解了Linux内核的基本工作原理,包括进程管理、内存管理、设备驱动、文件系统,从分析内核到了解整个系统是如何工作的、如何控制管理资源分配、进程切换并执行、各种策略和结构让系统运行时更有效率等。每周都要看视频、做实验、写博客,在一点一滴的积累中,我逐渐弄清楚Linux内核的脉络,更令我感到欣喜的是本来略感散乱的知识点也在最后一节课的总览里看到了其内在联系。苟有恒,学好Linux不是梦!
一、博客目录
第一周《计算机是如何工作的?》 http://www.cnblogs.com/Nancy5104/p/5215595.html
第二周《操作系统是如何工作的?》http://www.cnblogs.com/Nancy5104/p/5244715.html
第三周《构建一个简单的操作系统MenuOS》http://www.cnblogs.com/Nancy5104/p/5267189.html
第四周 《扒开操作系统的“三层皮”(上)》http://www.cnblogs.com/Nancy5104/p/5286555.html
第五周《扒开操作系统的“三层皮”(下)》http://www.cnblogs.com/Nancy5104/p/5313150.html
第六周《进程的描述与创建》http://www.cnblogs.com/Nancy5104/p/5338062.html
第七周《可执行程序的装载》http://www.cnblogs.com/Nancy5104/p/5338062.html
第八周《进程的切换和系统的一般执行过程》http://www.cnblogs.com/Nancy5104/p/5389990.html
二、知识点梳理
1.计算机是如何工作的?
寻址方式
直接寻址:movl $0x123,%eax
立即数寻址:movl 0x123,%eax
变址寻址:movl 4(%ebx),%edx ebx的值加4之后作为一个地址,将其指向的数据赋给%edx
重要的汇编指令:enter指令相当于在原来的堆栈上再建一个新的空堆栈,leave指令与enter相反,相当于撤销函数调用堆栈
存储程序计算机工作模型:冯诺依曼体系结构
X86汇编基础:CPU的寄存器(通用寄存器、段寄存器、标志寄存器)、常见汇编指令、堆栈
汇编一个简单的C程序分析其汇编指令执行过程
2.操作系统是如何工作的?
三个法宝:
存储程序计算机:所有计算机基础性的逻辑框架
堆栈:高级语言的起点,函数调用需要堆栈机制
中断机制:多道系统的基础,是计算机效率提升的关键
3.构建一个简单的操作系统MenuOS
构造一个简单的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(中断向量对应的中断服务程序)。
系统调用的三层皮
5.扒开操作系统的“三层皮”(下)
给MenuOS增加time和time-asm命令
rm menu -rf //首先删除当前的menu git clone http://github.com/mengning/menu.git //克隆新版本的menu,需联网下载 cd menu ls make rootfs 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
使用gdb跟踪系统调用内核函数sys_time
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S gdb (gdb)file linux-3.18.6/vmlinux (gdb)target remote:1234 //首先应连接到需要调试的MenuOS (gdb)b start_kernel //设置断点 (gdb)c list //可查看start_kernel的代码 (gdb)b sys_time //13号系统调用对应的内核处理函数时sys_time (gdb)c
系统调用在内核代码中的工作机制和初始化
6.进程的描述与创建
内核三大功能:进程管理、文件管理、内核系统
进程创建:
0号进程(手写)->1号进程(复制0号进程的PCB,根据其修改pid加载init可执行程序)。
在fork中,两个进程,fork系统调用在父子进程个返回一次,子 pid=0,父 pid=子进程的id。
新进程是从ret_ from_ fork开始执行
7.可执行程序的装载
c代码—>编译器预处理.cpp—>汇编代码.s—>目标代码.o—链接成为可执行文件a.out—>memory
三种主要文件:可重定位文件、可执行文件(保存一个用来执行的程序,指出exec(BA_OS)如何创建程序映像、共享Object文件:保存着两个连接器用来链接的数据
两个链接器:链接编辑器和动态链接器
庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现自己是蝴蝶(被execve加载的可执行程序)
修改int 0x80压入内核堆栈的EIP
8.进程的切换和系统的一般执行过程
进程进度与进程调度的时机:中断处理过程
Linux系统的一般执行过程:正在运行的用户态进程X切换到运行用户态进程Y的过程。正在运行的用户态进程X发生中断,SAVE_ALL(保存现场),中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换,标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行),restore_all(恢复现场),iret,继续运行用户态进程Y。
三、测验回顾
第一周
32位x86计算机中,cs:eip不总是指向地址连续的下一条指令。
第二周
32位x86的Linux系统中,函数调用约定使用__stdcall方式, 调用f(x,y,z)时,需要把参数压栈,首先压入的参数是x,y,z中的哪一个? z
mykernel实验中,时钟中断处理函数是void my_timer_handler(void) 。第四周
针对API xyz, Linux中系统调用的三层皮指的包括:
API xyz
中断向量system_call
中断服务程序sys_xyz
第五周
Linux内核中,系统调用处理过程中保护现场使用的宏是SAVE_ALL。
第六周
操作系统的三大管理功能包括:进程管理、内存管理、文件系统。
Linux内核通过唯一的进程标识PID来区别每个进程。
第七周
动态连接有两种形式:可执行程序装载时动态连接和运行时动态链接。
execve执行静态链接程序时,通过修改内核堆栈中EIP的值作为新进程的起点。
第八周
Linux中,内核线程是只有内核态没有用户态的特殊进程。
四、总结
都说一个成熟的编程人员,必须熟悉Linux。在这两个月的时间里,我有幸跟随孟老师一步步分析着Linux内核的原理。老师的学员特别多,对于我这样的普通大三学生,批改作业的时候真心感觉到自己与别人的差距,在掌握知识的同时也鞭策自己不断向优秀的同学看齐。
我最大的收获是能够从操作系统内核的设计层面去分析Linux,比原先只会使用Linux命令更进一步,做到了知其然而知其所以然。虽然对于我来说自己编写一个小型的操作系统仍是望尘莫及,但是我学会了分析内核代码的方法,如何使用gdb调试内核代码,以及在调试的过程中体会理解内核代码的具体实现,如进程创建、切换、操作系统启动都有了更为深入的认识和理解。
至于遗憾,我觉得在于自己的操作系统基础比较薄弱,对操作系统的有些知识之前没有涉猎,所以在学习视频期间还学习了另一本书——《Linux内核设计的艺术》,这本书的特点是图比较多,不是完全靠文字和代码讲解,适合入门。如果能在寒假上课前就学完这本书听课效果应该会更佳。
大道无形,现在的很多编程课都注重项目实战,殊不知若无对内核的深入理解,那么编程则是无本之木。唯有深入分析过内核,才能“万行代码过,bug不沾身”。
这就是我在这门课期间的总结,感谢网易云课堂和孟宁老师提供了这个学习的机会,祝孟老师万事如意,桃李满天下。
犯其至难,图其至远。
刘帅
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000