Linux的启动过程分析_第1页
Linux的启动过程分析_第2页
Linux的启动过程分析_第3页
Linux的启动过程分析_第4页
Linux的启动过程分析_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 嵌入式系统期中大作业 题目:Linux的启动过程分析 学 院:信息工程 专 业:网络工程 姓 名:梁法成 学 号:8 2016.4.15 目 录1.从开机加电到执行main函数之前的过程1.1启动BIOS,准备实模式下的中断向量表和中断服务程序1.1.1BIOS的启动原理1.1.2BIOS在内存中加载中断向量表和中断服务程序1.2加载操作系统内核程序并为保护模式做准备1.2.1加载第一部分内核代码引导程序(bootsect)1.2.2加载第二部分内核代码setup1.2.3加载第三部分内核代码system模块1.3开始向32位模式转变,为main函数的调用做准备1.3.1关中断并将syste

2、m移动到内存地址起始位置0x000001.3.2设置中断描述符表和全局描述符表1.3.3打开A20,实现32位寻址1.3.4为保护模式下执行head.s做准备1.3.5head.s开始执行2.从main到怠速2.1 设备环境初始化及激活进程02.1.1 与建立人机交互界面相关的外设的中断服务程序挂接2.1.2初始化进程02.1.3初始化硬盘2.1.4开启中断2.2 进程1的创建及执行2.2.1 进程0创建进程12.2.2 内核第一次做进程调度2.2.3轮转到进程1执行2.3 进程2的创建及执行2.3.1、打开终端设备文件及复制文件句柄2.3.2、进程1创建进程2并切换到进程22.3.3、加载s

3、hell程序2.3.4、实现系统怠速1.从开机加电到执行main函数之前的过程从开机到main函数的执行分三步完成,目的是实现从启动盘加载操作系统程序,完成执行main函数所需要的准备工作。第一步,启动BIOS,准备实模式下的中断向量表和中断服务程序;第二步,从启动盘加载操作系统程序到内存,加载操作系统程序的工作就是利用第一步中准备的中断服务程序实现的;第三步,为执行32位的main函数做过渡工作。本章将详细分析这三步在计算机中是如何完成的,以及每一步在内存中都做了些什么。1.1启动BIOS,准备实模式下的中断向量表和中断服务程序问题:在RAM中什么程序也没有的时候,谁来完成加载软盘中操作系统

4、的任务呢?答案是:BIOS。1.1.1BIOS的启动原理从硬件角度看,Intel 80x86系列的CPU可以分别在16位实模式和32位保护模式下运行。为了兼容,也为了解决最开始的启动问题,Intel 将所有80x86系列的CPU,包括最新型号的CPU的硬件都设计为加电即进入16位实模式状态运行。同时,还有一点非常关键的是,将CPU硬件逻辑设计为加电瞬间强行将CS的值置为0xF000、IP的值置为0xFFF0,这样CS:IP就指向0xFFFF0这个地址位置。1.1.2BIOS在内存中加载中断向量表和中断服务程序BIOS程序在内存最开始的位置(0x00000)用1 KB的内存空间(0x000000

5、x003FF)构建中断向量表,在紧挨着它的位置用256字节的内存空间构建BIOS数据区(0x004000x004FF),并在大约57 KB以后的位置(0x0E05B)加载了8 KB左右的与中断向量表相应的若干中断服务程序。中断向量表中有256个中断向量,每个中断向量占4字节,其中两个字节是CS的值,两个字节是IP的值。每个中断向量都指向一个具体的中断服务程序。1.2加载操作系统内核程序并为保护模式做准备从现在开始,就要执行真正的boot操作了,即把软盘中的操作系统程序加载至内存。对于Linux 0.11操作系统而言,计算机将分三批逐次加载操作系统的内核代码。第一批由BIOS中断int0x19把

6、第一扇区bootsect的内容加载到内存;第二批、第三批在bootsect的指挥下,分别把其后的4个扇区和随后的240个扇区的内容加载至内存。1.2.1加载第一部分内核代码引导程序(bootsect)经过执行一系列BIOS代码之后,计算机完成了自检等操作(这些和我们讲的启动操作系统没有直接的关系,读者不必关心)。由于我们把软盘设置为启动设备,计算机硬件体系结构的设计与BIOS联手操作,会让CPU接收到一个int 0x19中断。CPU接收到这个中断后,会立即在中断向量表中找到int 0x19中断向量。接下来,中断向量把CPU指向0x0E6F2,这个位置就是int0x19相对应的中断服务程序的入口

7、地址。这个中断服务程序的作用就是把软盘第一扇区中的程序(512 B)加载到内存中的指定位置。这个中断服务程序的功能是BIOS事先设计好的,代码是固定的,与Linux操作系统无关。无论Linux 0.11的内核是如何设计的,这段BIOS程序所要做的就是“找到软盘”并“加载第一扇区”,其余的它什么都不知道,也不必知道。按照这个简单、“生硬”的规则,int 0x19中断向量所指向的中断服务程序,即启动加载服务程序,将软驱0号磁头对应盘面的0磁道1扇区的内容复制至内存0x07C00处。1.2.2加载第二部分内核代码setupbootsect对内存的规划BIOS已经把bootsect也就是引导程序载入内

8、存了,现在它的作用就是把第二批和第三批程序陆续加载到内存中。为了把第二批和第三批程序加载到内存中的适当位置,bootsect首先做的工作就是规划内存。通常,我们是用高级语言编写应用程序的,这些程序是在操作系统的平台上运行的。我们只管写高级语言的代码、数据。至于这些代码、数据在运行的时候放在内存的什么地方,是否会相互覆盖,我们都不用操心,因为操作系统和高级语言的编译器替我们做了大量的看护工作,确保不会出错。现在我们讨论的是,操作系统本身使用的是汇编语言,没有高级语言编译器替操作系统提供保障,只有靠操作系统的设计者把内存的安排想清楚,确保无论操作系统如何运行,都不会出现代码与代码、数据与数据、代码

9、与数据之间相互覆盖的情况。为了更准确地理解操作系统的运行机制,我们必须清楚操作系统的设计者是如何规划内存的。在实模式状态下,寻址的最大范围是1 MB。这些源代码的作用就是对后续操作所涉及的内存位置进行设置,包括将要加载的setup程序的扇区数(SETUPLEN)以及被加载到的位置(SETUPSEG);启动扇区被BIOS加载的位置(BOOTSEG)及将要移动到的新位置(INITSEG);内核(ker-nel)被加载的位置(SYSSEG)、内核的末尾位置(ENDSEG)及根文件系统设备号(ROOT_DEV)。设置这些位置就是为了确保将要载入内存的代码与已经载入内存的代码及数据各在其位,互不覆盖,并

10、且各自有够用的内存空间。下面,bootsect程序要执行它的第二步工作:将setup程序加载到内存中。加载setup这个程序,要借助BIOS提供的int 0x13中断向量所指向的中断服务程序(也就是磁盘服务程序)来完成。执行int 0x13指令,产生0x13中断,通过中断向量表找到这个中断服务程序,将软盘第二扇区开始的4个扇区,即setup.s对应的程序加载至内存的SE-TUPSEG(0x90200)处。复制后的boot-sect的起始位置是0x90000,占用512字节的内存空间。不难看出0x90200紧挨着bootsect的尾端,所以bootsect和setup是连在一起的。现在,操作系统

11、已经从软盘中加载了5个扇区的代码。等bootsect执行完毕后,setup这个程序就要开始工作了。1.2.3加载第三部分内核代码system模块接下来,bootsect程序要执行第三批程序的载入工作,即将系统模块载入内存。bootsect借着BIOS中断int 0x13,将240个扇区的system模块加载进内存。加载工作主要是由bootsect调用read_it子程序完成的。到此为止,第三批程序已经加载完毕,整个操作系统的代码已全部加载至内存。bootsect的主体工作已经做完了。下面要通过执行“jmpi 0, SETUPSEG”这行语句跳转至0x90200处,就是前面讲过的第二批程序set

12、up程序加载的位置。CS:IP指向setup程序的第一条指令,意味着由setup程序接着bootsect程序继续执行。setup程序现在开始执行。它做的第一件事情就是利用BIOS提供的中断服务程序从设备上提取内核运行所需的机器系统数据,其中包括光标位置、显示页面等数据,这些机器系统数据被加载到内存的0x900000x901FC位置。图1-15标出了其内容及准确的位置。这些数据将在以后main函数执行时发挥重要作用。到此为止,操作系统内核程序的加载工作已经完成。接下来的操作对Linux 0.11而言具有战略意义。系统通过已经加载到内存中的代码,将实现从实模式到保护模式的转变,使Linux 0.1

13、1真正成为“现代”操作系统。1.3开始向32位模式转变,为main函数的调用做准备接下来,操作系统要使计算机在32位保护模式下工作。这期间要做大量的重建工作,并且持续工作到操作系统的main函数的执行过程中。在本节中,操作系统执行的操作包括打开32位的寻址空间、打开保护模式、建立保护模式下的中断响应机制等与保护模式配套的相关工作、建立内存的分页机制,最后做好调用main函数的准备。1.3.1关中断并将system移动到内存地址起始位置0x00000如图所示,这个准备工作先要关闭中断,即将CPU的标志寄存器(EFLAGS)中的中断允许标志(IF)置0。这意味着,程序在接下来的执行过程中,无论是否

14、发生中断,系统都不再对此中断进行响应,直到main函数中能够适应保护模式的中断服务体系被重建完毕才会打开中断,而那时候响应中断的服务程序将不再是BIOS提供的中断服务程序,取而代之的是由系统自身提供的中断服务程序。下面,setup程序做了一个影响深远的动作:将位于0x10000的内核程序复制至内存地址起始位置0x00000处!这样做能取得“一箭三雕”的效果:1)废除BIOS的中断向量表,等同于废除了BIOS提供的实模式下的中断服务程序。2)收回刚刚结束使用寿命的程序所占内存空间。3)让内核代码占据内存物理地址最开始的、天然的、有利的位置。1.3.2设置中断描述符表和全局描述符表setup程序继

15、续为保护模式做准备。此时要通过setup程序自身提供的数据信息对中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR)进行初始化设置。GDT(Global Descriptor Table,全局描述符表),在系统中唯一的存放段寄存器内容(段描述符)的数组,配合程序进行保护模式下的段寻址。它在操作系统的进程切换中具有重要意义,可理解为所有进程的总目录表,其中存放每一个任务(task)局部描述符表(LDT,Local Descriptor Table)地址和任务状态段(TSS,TaskStructure Segment)地址,完成进程中各段的寻址、现场保护与现场恢复。IDT(Interru

16、pt Descriptor Table,中断描述符表),保存保护模式下所有中断服务程序的入口地址,类似于实模式下的中断向量表。1.3.3打开A20,实现32位寻址下面是标志性的动作打开A20!打开A20,意味着CPU可以进行32位寻址,最大寻址空间为4 GB。打开A20现在看来,Linux 0.11还显得有些稚嫩,最大只能支持16 MB的物理内存,但是其线性寻址空间已经是不折不扣的4 GB。1.3.4为保护模式下执行head.s做准备setup程序将CPU工作方式设为保护模式。将CR0寄存器第0位(PE)置1,即设定处理器工作方式为保护模式。1.3.5head.s开始执行head程序除了做一些

17、调用main的准备工作之外,还做了一件对内核程序在内存中的布局及内核程序的正常运行有重大意义的事,就是用程序自身的代码在程序自身所在的内存空间创建了内核分页机制,即在0x的位置创建了页目录表、页表、缓冲区、GDT、IDT,并将head程序已经执行过的代码所占内存空间覆盖。这意味着head程序自己将自己废弃,main函数即将开始执行。这些工作完成后,内存中的布局如图所示。可以看出,只有184字节的剩余代码。由此可见,在设计head程序和system模块时,其计算是非常精确的,对head.s的代码量的控制非常到位。开机时的16位实模式与main函数执行需要的32位保护模式之间有很大的差距,这个差距

18、谁来填补? head.s做的就是这项工作。这期间,head程序打开A20,打开pe、pg,废弃旧的、16位的中断响应机制,建立新的32位的IDT这些工作都做完了,计算机已经处在32位的保护模式状态了,调用32位main函数的一切条件已经准备完毕,这时顺理成章地调用main函数。后面的操作就可以用32位编译的main函数完成。至此,Linux 0.11内核启动的一个重要阶段已经完成,接下来就要进入main函数对应的代码了。特别需要提示的是,此时仍处在关闭中断的状态!2.从main到怠速系统达到怠速状态前所做的一切准备工作的核心目的就是让用户程序能够以“进程”的方式正常运行。能够实现这一目的的标准

19、包括三方面的内容:1)用户程序能够在主机上进行运算;2)能够与外设进行交互;3)能够让用户以它为媒介进行人机交互。2.1 设备环境初始化及激活进程02.1.1 与建立人机交互界面相关的外设的中断服务程序挂接Linus在操作系统源代码中本来设计了chr_dev_init()函数,明显是要用这个函数初始化字符设备,但我们可以看到这是一个空函数。Linus又设计了tty_init()函数,内容就是初始化字符设备。有人解释tty是teletype。字符设备的初始化为进程与串行口(可以通信、连接鼠标)、显示器以及键盘进行I/O通信准备工作环境,主要是对串行口、显示器、键盘进行初始化设置,以及与此相关的中

20、断服务程序与IDT挂接。2.1.2初始化进程0进程0是Linux操作系统中运行的第一个进程,也是Linux操作系统父子进程创建机制的第一个父进程。下面讲解的内容对进程0能够在主机中正常运算的影响最为重要和深远,主要包含如下三方面的内容。1)系统先初始化进程0。进程0管理结构task_struct的母本(init_task = INIT_TASK,)已经在代码设计阶段事先设计好了,但这并不代表进程0已经可用了,还要将进程0的task_struct中的LDT、TSS与GDT相挂接,并对GDT、task64以及与进程调度相关的寄存器进行初始化设置。2)Linux 0.11作为一个现代操作系统,其最重

21、要的标志就是能够支持多进程轮流执行,这要求进程具备参与多进程轮询的能力。系统这里对时钟中断进行设置,以便在进程0运行后,为进程0以及后续由它直接、间接创建出来的进程能够参与轮转奠定基础。3)进程0要具备处理系统调用的能力。每个进程在运算时都可能需要与内核进行交互,而交互的端口就是系统调用程序。系统通过函数set_system_gate将system_call与IDT相挂接,这样进程0就具备了处理系统调用的能力了。这个system_call就是系统调用的总入口。进程0只有具备了以上三种能力才能保证将来在主机中正常地运行,并将这些能力遗传给后续建立的进程。2.1.3初始化硬盘硬盘的初始化为进程与硬

22、盘这种块设备进行I/O通信建立了环境基础。在hd_init()函数中,将硬盘请求项服务程序do_hd_request()与blk_dev控制结构相挂接,硬盘与请求项的交互工作将由do_hd_request()函数来处理,然后将硬盘中断服务程序hd_interrupt()与IDT相挂接,最后,复位主8259A int2 的屏蔽位,允许从片发出中断请求信号,复位硬盘的中断请求屏蔽位(在从片上),允许硬盘控制器发送中断请求信号。2.1.4开启中断现在,系统中所有中断服务程序都已经和IDT正常挂接。这意味着中断服务体系已经构建完毕,系统可以在32位保护模式下处理中断,重要意义之一是可以使用系统调用。可以开启中断了!2.2 进程1的创建及执行现在,计算机中已经有了一个名副其实的、3特权级的进程进程0。下面我们要详细讲解进程0做的第一项工作创建进程1。2.2.1 进程0创建进程1进程0作为父进程调用fork函数创建第一个子进程进程1。执行sys_fork(),在task64中为进程1申请一个空闲位置并获取进程号。进程0将在copy_process()函数中做非常重要的、体现父子进程机制的工作:(1)为进程1创建tast_struct,将进程0的tast_struct内容复制给进程1;(2)为进程1的tast_struct、tss做个性化设置;(3)为进程1创建第一个页表,将进程0的页表内容

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论