Linux内核分析(心得篇)

来源:互联网 发布:java中调用webservice 编辑:程序博客网 时间:2024/06/16 17:57

Linux 内核分析——学习心得

经过这段时间的学习,我对linux的内核有了大致的了解。linux内核是个庞大的操作系统代码,想要真正的弄清楚透彻决非一日之功,还必须循序渐进、持之以恒,可谓是任重而道远。但是俗话说的好——“师傅领进门,修行看个人”,我们已经不再是一无所知的小白,我们见识到了一个操作系统内核的复杂,也对其中部分关键代码进行理解、分析,虽然这部分相对整个内核来说不过九牛一毛,但这也教会了我们以后如何学习内核的方法。

当我们首次接触计算机,发现它可以听歌、看视频、聊天、玩游戏等等,一定会为它的强大,甚至是无所不能所折服。我们肯定会想:为什么这么神奇,怎么办到的?于是,有了第一个问题:

一 计算机是如何工作的?
其实很简单,大家都知道‘死循环’这个概念吧,实际上计算机cpu开机启动以后都会处于这样一个状态——等待用户任务。当用户有任务时(中断过程),就会提交给cpu,cpu暂停当前的状态,然后去执行这个任务,执行后又返回这个等待状态,当有多个任务时,就每个任务执行一小段时间,因为时间间隔太短,我们察觉不出来,所以给我们的感觉就是一直在运行。上面只是个简单的描述,忽略了许多的细节,但总的来说是这样一个过程。
计算机工作的三大法宝:
(1)存储程序计算机工作模型,计算机系统最最基础性的逻辑结构,用于保存cpu执行的指令;
(2)函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那么重要,但有了高级语言及函数,堆栈成为了计算机的基础功能;
(3)中断,多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程序。
下面我们说说,计算机开机启动的过程。
当我们按下电源键的那一刻起,cpu首先会执行固件fireware(COMS/BIOS)里面的内容,进行加电自检POST,主要检查硬件的好坏,对CPU、主板、内存、软硬盘子系统、显示子系统(包括显示缓存)、串并行接口、键盘、CD-ROM光驱等检测。如果都没问题,cpu就会读取硬盘第0号柱面第0号磁道的第一个扇区MBR(main boot record),把它加载到内存中,然后执行这段代码,也叫引导程序(BootLoad),它会引导操作系统内核,实现内核的初始化。初始化包括加载驱动程序,驱动硬件;然后挂载文件系统;最后启动init进程,开启相关的服务。

二 操作系统是如何工作的?
当开机以后,在登录界面输入用户名密码登录,就进入了操作系统的界面。实际上操作系统也是一个程序,只不过它的功能非常齐全,实现的任务比较多,因此代码十分复杂庞大。但既然它也是一个程序,而且linux系统的源代码都是由C和汇编组成的,那么它也少不了编译、链接、运行的过程。因此对于操作系统我们可以从一般到特殊,先了解普通程序是如何执行的。
1

源代码首先经过汇编,然后编译,再链接,形成可执行文件,最后加载到内存交给cpu执行。我们可以想象操作系统也是这样一个流程,只不过它的文件更多,编译链接时间要更长,总之,操作系统也是一个程序,它之所以神秘,是因为我们还没有对它真正的知根知底。

三 初探Linux内核源代码
了解内核源码的目录结构,大致了解每个目录的作用,以及初始化阶段做了那些事情,执行的流程等等。

四 中断和系统调用
中断(广义)是指改变处理器执行指令的顺序,通常与cpu芯片内部或外部硬件电路产生的电信号相对应。Intel x86系列微机共支持256种向量中断,为使处理器较容易地识别每种中断源,将它们从0到256编号,即赋以一个中断类型码n,Intel把这个8位的无符号整数叫做一个向量,因此,也叫中断向量。所有256种中断可分为两大类:异常和中断。异常又分为故障(Fault)和陷阱(Trap),它们的共同特点是既不使用中断控制器,又不能被屏蔽。中断又分为外部可屏蔽中断(INTR)和外部非屏蔽中断(NMI),所有I/O设备产生的中断请求(IRQ)均引起屏蔽中断,而紧急的事件(如硬件故障)引起的故障产生非屏蔽中断。
非屏蔽中断的向量和异常的向量是固定的,而屏蔽中断的向量可以通过对中断控制器的编程来改变。Linux对256个向量的分配如下:
• 从0~31的向量对应于异常和非屏蔽中断。
• 从32~47的向量(即由I/O设备引起的中断)分配给屏蔽中断。
• 剩余的从48~255的向量用来标识软中断。Linux只用了其中的一个(即128或0x80向量)用来实现系统调用。当用户态下的进程执行一条int 0x80汇编指令时,CPU就切换到内核态,并开始执行system_call( )内核函数。

系统调用是一种中断,中断号为128(0x80)。通过int 0x80指令,就可以调用。system_call函数(实际上是汇编代码)是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号,其他参数依次由ebx,ecx,edx,esi,edi,ebp传入。
寄存器传递参数具有如下限制:
• 1)每个参数的长度不能超过寄存器的长度,即32位
• 2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)

五 系统调用运行机制
系统调用的执行过程:
1、程序调用libc库中封装的系统调用函数。
2、调用中断int 0x80 陷入内核。
3、在内核中执行system_call函数(实际上是一段汇编代码),将系统调用号(eax)和可以所有相关寄存器保存到内核堆栈中(由SAVE_ALL完成),然后根据系统调用号在系统调用表中查找到对应的系统调用服务例程。
4、执行该服务例程。
5、执行完毕后,转入ret_from_sys_call 例程,从系统调用返回

六 进程的描述与创建
进程是程序执行的一个实例,它是最小的系统资源分配基本单元,在Linux内核代码中,常把进程称为任务(task)或线程(thread)。也就是说在linux中没有线程的概念,有的只是轻量级进程(相当于线程)。
对于多任务处理的操作系统而言,通常系统上都会运行着多个进程(任务),为了管理进程,系统必须对每个进程所做的事情进行详细地描述。在linux系统中,进程描述符是由内核定义的一个task_struct类型数据结构,它在/include/linux/sched.h文件中定义,内容非常复杂,由四百多行的c代码组成。它包含了进程的所有信息,包括:进程状态、进程内核堆栈、进程标志符、优先级、进程链表、内存管理、进程标识符、线程组标识符、父子进程、兄弟进程、该进程中对应cpu相关寄存器信息、文件系统、相关信号及处理等等。
在linux中,fork(),vfork(),clone都可以创建一个新进程,并且它们都是通过do_fork()函数(文件路径:linux-3.18.6/kernel/fork.c)实现的。一个新的进程创建的时候,从用户态来看,fork语句的下一条语句即为子进程执行的开始;从内核态看,子进程的执行从ret_from_fork(文件路径:/arch/x86/kernel/entry_32.S)处开始。

七 如何装载和启动一个可执行程序
linux系统中,可执行文件的格式为elf(Executable and Linking Format)格式。它有三种类型:
(1)可重定位文件
也就是通常称的目标文件,后缀为.o。链接器将它作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件。
(2)共享文件
这些就是所谓的动态库文件,也即 .so 文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。
(3)可执行文件

execve函数用于执行可执行文件,根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

八 进程调度与切换
为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
进程上下文包含了进程执行需要的所有信息:
(1) 用户地址空间: 包括程序代码,数据,用户堆栈等
(2) 控制信息 :进程描述符,内核堆栈等
(3) 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)

最一般的情况:正在运行的用户态进程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

以上就是这段时间学习linux内核的个人心得。That is all!Thank you!

=========== 王杰 原创作品转载请注明出处==============
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

1 0
原创粉丝点击