5228 原创做品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000html
《Linux内核分析》第一周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5215176.htmllinux
《Linux内核分析》第二周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5243214.htmlgit
《Linux内核分析》第三周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5270286.htmlgithub
《Linux内核设计与实现》第1、二章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5274011.html算法
《Linux内核分析》第四周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5284262.html安全
《Linux内核设计与实现》第五章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5297421.html网络
《Linux内核分析》第五周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5312936.html数据结构
《Linux内核设计与实现》第十八章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5330236.html架构
《Linux内核分析》第六周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5334985.html框架
《Linux内核设计与实现》第三章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5340778.html
《深刻理解计算机系统》第七章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5343928.html
《Linux内核分析》第七周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5360264.html
《Linux内核分析》第八周学习笔记:http://www.cnblogs.com/20135228guoyao/p/5383322.html
《Linux内核设计与实现》第四章学习笔记:http://www.cnblogs.com/20135228guoyao/p/5384449.html
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
跟踪调试Linux内核的启动过程
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
(一)用户态、内核态和中断处理过程
(二)系统调用概述
(三)使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
(一)给MenuOS增长time和time-asm命令
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 //编译
(二)使用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 //执行,可见程序在start_kernel处停下 list //可查看start_kernel的代码 (gdb)b sys_time //sys_time是13号系统调用对应的内核处理函数,在该函数处设置断点 (gdb)c //若是这里一直按n单步执行,会进入schedule函数。sys_time返回后进入汇编代码处理,gdb没法继续进行追踪 执行int 0x80后执行system call对应的代码(system call不是函数,是一段特殊的汇编代码,gdb还不能进行跟踪)。
(三)系统调用在内核代码中的工做机制和初始化
(一)进程的描述
进程描述符task_struct数据结构
struct task_struct{ volatile long state; //进程状态,-1表示不可执行,0表示可执行,大于1表示中止 void *stack; //内核堆栈 atomic_t usage; unsigned int flags; //进程标识符 unsigned int ptrace; …… }
(二)进程的建立
(一)预处理、编译、连接和目标文件的格式
可执行程序是怎么得来的
1. C源代码(.c)通过编译器预处理被编译成汇编代码(.asm) 2. 汇编代码由汇编器被编译成目标代码(.o) 3. 将目标代码连接成可执行文件(a.out) 4. 可执行文件由操做系统加载到内存中执行
目标文件的格式ELF
目标文件的三种形式: 1. 可重定位文件.o,用来和其余object文件一块儿建立可执行文件和共享文件 2. 可执行文件,指出应该从哪里开始执行 3. 共享文件,主要是.so文件,用来被连接编辑器和动态连接器连接
静态连接的ELF可执行文件和进程的地址空间
可执行文件加载到内存时: 1. 加载效果:将代码段数据加载到内存中,再把数据加载到内存,默认从0x8048000地址开始加载 2. 启动一个刚加载过可执行文件的进程时,可执行文件加载到内存以后执行的第一条代码地址 3. 通常静态连接会将全部代码放在一个代码段,而动态连接的进程会有多个代码段
(二)可执行程序、共享库和动态加载
装载可执行程序以前的工做
1. 通常执行一个程序的Shell环境,实验中直接使用execve系统调用 2. Shell自己不限制命令行参数的个数,命令行参数的个数受限于命令自身 3. Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
装载时动态连接和运行时动态连接应用举例
1. 准备.so文件(在Linux下动态连接文件格式,在Windows中是.dll) 2. 编译成.so文件
(三)可执行程序的装载
可执行程序的装载相关关键问题分析
1. execve与fork是比较特殊的系统调用: • execve用它加载的可执行文件把当前的进程覆盖掉,返回以后就不是原来的程序而是新的可执行程序起点; • fork函数的返回点ret_ from_fork是用户态起点 2. sys_ execve内核处理过程: • do_ execve -> do_ execve_ common -> exec_ binprm -> search_ binary_handler,最后根据文件头部信息寻找对应的文件格式处理模块
sys_execve的内部处理过程
exec通常和fork调用,常规用法是fork出一个子进程,而后在子进程中执行exec,替换为新的代码。
使用gdb跟踪sys_execve内核函数的处理过程
1. 开始先更新内核,再用test_exec.c将test.c覆盖掉 2. test.c文件中增长了exec系统调用,Makefile文件中增长了gcc -o hello hello.c -m32 -static 3. 启动内核并验证execv函数 4. 启动gdb调试 5. 先停在sys_execve处,再设置其它断点 6. 进入函数单步执行
可执行程序的装载与庄生梦蝶的故事
庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现本身是蝴蝶(被execve加载的可执行程序)
浅析动态连接的可执行程序的装载
(一)进程切换的关键代码switch_to分析
1. 进程进度与进程调度的时机分析
schedule()函数实现调度:
2. 进程上下文切换相关代码分析
(二)Linux系统的通常执行过程
1. 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
2. Linux系统执行过程当中的几个特殊状况
1. 经过中断处理过程当中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最通常的状况很是相似,只是内核线程运行过程当中发生中断没有进程用户态和内核态的转换 2. 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最通常的状况略简略 3. 建立子进程的系统调用在子进程中的执行起点及返回用户态,如fork 4. 加载一个新的可执行程序后返回到用户态的状况,如execve
3. 内核与舞女
1. 进程的地址空间一共有4G,其中0——3G是用户态能够访问,3G以上只有内核态能够访问 2. 内核至关于出租车,能够为每个“招手”的进程提供内核态到用户态的转换。 3. 没有进程须要“承载”的时候,内核进入idle0号进程进行“空转”。当用户进程有需求时,内核发生中断,帮助用户进程完成请求,而后再返回到用户进程。就好像Taxi将用户载了一圈以后又把用户放下来。 4. 3G以上的部分就是这样的“出租车”,是全部进程共享的,在内核态部分切换的时候就比较容易 5. 内核是各类中断处理程序和内核线程的集合
(三)Linux系统架构和执行过程概览
1. Linux操做系统架构概览
2. 最简单也是最复杂的操做——执行ls操做
3. 从CPU和内存的角度看Linux系统的执行
在这几周的实验中我遇到的问题一共有三个:一是clone新版本的menu时显示链接超时,解决方法为在实验楼环境中点击“中止实验”而后再从新进入输入命令;二是编译命令make rootfs结果显示失败,缘由是该路径下原有的rootfs文件还存在,解决方法为删除原rootfs文件再执行编译指令;三是偶尔target remote:1234这个命令没法执行,显示链接超时,解决方法为从新执行qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 指令。
经过八周的视频学习和教材学习,我对于Linux内核有了初步的理解。在视频中老师为咱们讲解了部分核心源码,在课后做业里也有尝试着本身分析进程建立过程相关的关键代码、可执行程序的装载、进程上下文切换相关代码、Linux系统架构和执行过程等,虽举步维艰,但也收获颇丰。Linux内核主要包括进程管理、内存管理、设备驱动、文件系统,从分析内核到了解整个系统是如何工做的、如何控制管理资源分配、进程切换并执行、各类策略和结构让系统运行时更有效率等,在日渐深刻的学习中我愈发认识到内核源码数据结构和算法的精妙之处。不过我也认识到自身的许多不足,在平时的学习中有一些内容仅仅是浅尝辄止而没有来得及细嚼慢咽,掌握的不够全面。并且在本身虚拟机环境中动手实践的效果不理想。在往后的学习中我会定时查漏补缺,争取有朝一日可以彻底领会内核源码的精妙之处并能吸取借鉴用于其它方面。