下载

1下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 LINUX 内核解读1

LINUX 内核解读1.pdf

LINUX 内核解读1

chinawei070
2011-04-06 0人阅读 举报 0 0 暂无简介

简介:本文档为《LINUX 内核解读1pdf》,可适用于IT/计算机领域

LINUX内核解读()内容:LINUX系统引导和初始化撰稿:刘立参加讨论:Linux内核研讨小组修订日期:智能中心体系结构组Linux的系统引导和初始化Linux内核解读之一一、系统引导和初始化概述l相关代码(引导扇区的程序及其辅助程序以x体系为例):linuxarchibootbootsectS:Linux引导扇区的源代码字节linuxarchibootsetupS:辅助程序linuxarchibootvideoS:辅助程序用于引导过程中的屏幕显示。linuxarchibootcompressedheadS,linuxarchibootcompressedmiscc:用于对内核映像解压缩linuxarchikernelheads系统初始化入口linuxinitmainc系统初始化入口l参考文档:linuxDocumentationiboottxt过程描述系统加电CPURESET跳到地址xFFFFFFF此处是BIOSBIOS完成它的操作把第一扇区的内容读入到xc就是bootsectS把自己移到绝对地址x处并调转到那里继续执行通过BIOS提供的读磁盘调用“intx”从磁盘上读入setup和内核的映像将(bootsetupS)kB字节的代码读入内存x处然后跳转到setup的代码中做执行内核映像的准备从x开始执行startup()进行初始化(heads)〉startkernel()maincàcpuidle()startkernel()创建的进程init()被调度执行完成、指令的跳转及其机理lxPentium的地址映射–KBMSDOSArea–KBVideoBufferArea–KBinKBsections(totalofeightsections)ExpansionAreaKBinKBsections(totaloffoursections)ExtendedSystemBIOSAreaKB–MBmemory(BIOSArea)SystemBIOSArea扩展内存区:由M到GB-HighBIOSareafromGBtoGB–MBIntelPMemoryControllerHub(MCH)memery的监测和初始化:在对内存接口做操作前必须初始化MCHDRAM寄存器。MCH必须配制成针对所安装的内存的类型进行操作。对内存类型和大小的检测是通过ICH上的SystemManagementBus(SMBus)来完成。这个两线的总线通过DRAMDIMM上的SerialPresenceDetect端口获得DRAM的类型和大小信息。BIOS需要确定每行内存的大小和类型来配置MCH内存接口。、xMPU启动时的初始化复位输入提供一种初始化的硬件手段。通过复位接口电路向mpu提供信号,Reset要保持至少个CLK周期当返回后MPU启动内部初始化程序进入实地址模式。初始化完成后标志寄存器设为xUUUU(u代表未定义实模式下位标志可用这里是奇偶标志为)指令指针设为xFFF,CS寄存器设为xF,DS,SS,ES,FS和GS寄存器都设为x指令队列清空。实模式下地址的形成:段基址指令指针偏移MPU在识别出Reset信号后把数据总线设在高阻状态地址线强行设为。由于清空中断标志是初始化的一部分外部中断被禁止。因为代码段寄存器为xF指令指针为xFFF地址线AA全部是从而复位后实模式程序从地址xFFFFFFF开始(只用于实模式高地址位忽略从地址xFFFF开始。该地址处可以包含一条转移指令跳到启动程序处。物理地址为xfffffff的代码将被执行。这个地址被存储在一个只读存储器(ROM)里。BIOS(基本输入输出系统)实际上是一段存储在ROM里的程序。它包含了一系列可以被某些操作系统调用用于处理计算机各种硬件设备的中断驱动和低级程序。其中微软的DOS就是这样的一种操作系统。所有的BIOS程序都是在实模式下运行的。但是Linux内核是在保护模式下运行而不是在实模式下。因此一旦初始化完成后Linux就不再使用BIOS而是完全由自己来为计算机上的所有硬件提供驱动程序。那么什么时候Linux使用保护模式?为什么BIOS不能使用相同的模式?BIOS使用实模式是因为其在操作过程中使用的是实模式地址并且在计算机刚打开电源时只有实模式地址可用。一个实模式地址由段地址和偏移地址组成因此相应的物理地址就为段地址×(×)偏移。这是不是意味着在整个启动过程中Linux就从来不使用BIOS了呢?答案是否定的。在启动阶段Linux从硬盘或者其它外部设备加载内核时需要使用BIOS。、BIOS的作用:BIOS要对硬件进行一系列彻底的检测。这个步骤主要是检查系统安装有哪些设备,以及它们工作是否正常。通常把这个步骤叫做自检(PowerOnSelfTest,POST)这时会显示版本及其它很多相关的硬件信息。BIOS要对硬件进行初始化。这一步非常重要因为它要保证所有的硬件设备在IRQ(中断请求)和IO端口操作时都没有冲突。等这步完成以后它会显示一个已经安装的PCI设备表。接着到了操作系统BIOS将查找一个可以引导的操作系统。这取决于BIOS的设置它可以从软盘、硬盘或者光盘启动。一旦发现一个合法的设备BIOS就会把其第一扇区的内容复制到物理地址即从xc开始的内存中然后跳至刚加载的地址并执行之。BIOS调用一个专门的程序这个程序的任务就是把操作系统的内核调入内存。这个程序就叫做自举程序(BootLoader)。、详细启动和初始化过程描述)从软盘启动Linux从软盘启动时存储在软盘第一扇区的指令将被加载并执行。这个指令然后就会把其余的内核复制到内存中。Linux内核可以装在MB的软盘里不过为了减少磁盘占用量它们都进行了压缩。这个压缩过程是在编译时完成的而解压缩的过程则由自举程序完成。从软盘启动Linux时自举程序要做的工作非常简单。它是一个位于usrsrclinuxarchibootbootsectS的汇编语言文件。当我们编译Linux内核源代码或者获取一个新的内核时这个可执行的汇编代码就会被放在内核程序的前端。由此可见要制作一个可启动的Linux软盘其实很简单。我们只要从磁盘的第一个扇区拷贝Linux内核就可以创建一个可启动软盘。当BIOS加载软盘的第一个扇区时它实际上拷贝的是自举程序。自举程序由BIOS调用(跳到物理地址为xc的位置)然后执行以下的操作:()把自已从地址xc移动到x()使用地址xff创建“实模式”栈()设置磁盘参数表这里使用的是BIOS提供的软盘驱动程序通过调用BIOS程序显示“Loading”信息()自举程序调用BIOS程序来加载软盘上内核的setup()函数并把它放在起始地址为x的内存中()接下来自举程序调用一个BIOS程序这个程序从软盘加载剩余的内核程序并将其放入起始地址为x(所谓的低地址)或者x(所谓的高地址)()然后跳转到setup()函数。)从硬盘启动Linux当系统从硬盘启动时启动过程又有所不同。硬盘的第一个扇区(头道扇区)叫做MBR(MasterBootRecord)其上存储着分区表和一个小程序。这个程序加载存储由操作系统的第一扇区来开始启动。Linux是一个高度灵活且非常优秀的软件所以在MBR里它使用一个叫做LILO的程序来代替上述的那个程序。LILO允许用户选择所要启动的操作系统。BootSector也就是硬盘的第一个扇区,它由MBR(MasterBootRecord),DPT(DiskPartitionTable)和BootRecordID三部分组成MBR又称作主引导记录占用BootSector的前个字节(toxBD),存放系统主引导程序(它负责从活动分区中装载并运行系统引导程序)DPT即主分区表占用个字节(xBEtoxFD),记录了磁盘的基本分区信息主分区表分为四个分区项,每项字节,分别记录了每个主分区的信息(因此最多可以有四个主分区)BootRecordID即引导区标记占用两个字节(xFEandxFF),对于合法引导区,它等于xAA,这是判别引导区是否合法的标志BootSector的具体结构如下图所示:|||||MasterBootRecord||||主引导记录(字节)|BD||BE||CD|分区信息(字节)|CE||DD|分区信息(字节)|DE||ED|分区信息(字节)|EE||FD|分区信息(字节)||||FE|FF|||AA|||当PC的电源打开后x结构的CPU将自动进入实模式并从地址xFFFF开始自动执行程序代码这个地址通常是ROMBIOS中的地址。PC机的BIOS将执行某些系统的检测在物理地址处开始初始化中断向量。BIOS首先检查:dfe的位置是不是xAA,若不是就去启动其他介质。一般来说Linux是从硬盘启动的。这就需要不同的自举程序。在Intel系统里用得最多的自举程序就是LILO。对于其它的体系结构还存在着别的自举程序。LILO可以安装在MBR上(请注意:在安装RedHatLinux时有一个步骤会让用户选择把LILO安装到MBR或者引导扇区)或一个活动分区的引导扇区上。由于LILO太大MBR无法容纳所以它被分成两部分。MBR(或者磁盘分区的引导扇区)包含有一个小的自举程序它被BIOS载入到起始地址为xc的内存中。然后这个小程序再把自己移到xa地址处接着设置实模式栈最后加载第二部分的LILO自举程序(请注意:实模式栈地址范围是xb到xa)。第二部分的LILO会从磁盘读取所有可用的操作系统并且给用户列出以选择所要启动的系统。一旦用户选择完成自举程序就会加载相应的扇区内容到内存中并且执行之。l自举程序bootsectS自举程序bootsectS被BIOS调用时(跳到物理地址为xc处)要执行以下操作:()把自已从地址xc移动到x()使用地址xff创建“实模式”栈()设置磁盘参数表。()通过调用BIOS程序显示“LoadingLinux”信息()自举程序BIOS调用来加载的setup()函数并把它放在起始地址为x的内存中()自举程序BIOS调用加载剩余的内核程序并将其放入起始地址为x或者x(根据内核类型对于小内核zImage放在x大内核bzImage放在x)此处是如何判断要加载的内核是什么的????()然后跳转到setup()函数。lSetup()函数的功用Setup()函数可以在linuxarchibootsetupS文件中找到。Setup()函数代码是在完整的内核自举程序加载以后才会跳到相应的函数代码处。在内核文件中其偏移地址是x。这使得自举程序很容易找到这段代码并将其拷贝到起始物理地址为x的内存中。这个Setup()文函数到底是做什么用的?在计算机时里内核要正确地操作所有硬件就必需首先要检测到它们并且以一种有序的方式进行初始化。Setup()函数初始化所有的硬件设备从而为内核操作它创造了一个环境。但是前面我们不是已经提到过BIOS会检测所有的硬件吗?虽然BIOS初始化了所有的硬件但是Linux内核并不放心它还要以自己的方式对所有的硬件进行初始化。Linux内核之所以要设计成这样是为了增强可移植性和稳定性。这也是Linux内核要优于很多目前可用的Unix和类Unix内核的原因之一并且也使得它在很多方面表现的非常出众。Setup()函数主要完成以下任务:()首先是检测系统可用内存的总量它是通过BIOS程序来完成检测的()设置键盘重复延迟时间和重复速度()检测视频卡()重新初始化硬盘控制器和硬盘参数()检测一个MCA()检测一个PS定点设备(鼠标总线)()检测高级电源管理器(APM)BIOS支持()检测内核在内存中的位置如果在低地址x就将其移到高地址x如在高地址则不做任何移动()设置设备中断描述表(IDT)和全局描述表(GDT)()如已经有了浮点单位(FPU)则重置之()重新调用程序中断控制器()通过设置cr状态寄存器的PE位把CPU从“实模式”切换到“保护模式”()跳转到stratup()汇编语言函数。因为在内核中不能做BIOS调用内存信息由setup通过INTX来加以查询并根据获得的信息生成一张物理内存构成图称为e图再通过参数块传给内核使内核知道系统中内存资源的配置。因为在做intx来查询内存构成是要把调用参数之一设置成xe所以叫e图。l第一个stratup()函数在启动过程中要用到两个stratup()函数虽然它们都是汇编语言函数但是却是两个完全不同的函数。我们这里所说的函数包含在usrsrclinuxarchibootcompressedheadS文件里。Setup()文件执行后这个函数就被加载到物理地址为x或者物理地址为x的内存中(取决于内核是载入高或者低内存)。当执行这个函数时会执行以下的操作:()初始化段寄存器和一个临时栈。()内核中没有初始化的数据都用填充。它是通过symbolsedata和end来识别的。()执行decompresskernel()函数。这个函数用于对Linux内核解压缩。这个时候屏幕上将显示“UncompressingLinux⋯⋯”信息。解压缩完成后就会显示“OK,bootingthekernel”信息。现在有一个问题,就是解完压缩的内核被放置在什么位置?答案是如果Linux内核被加载低地址那么解压缩的内核将被置于物理地址为x的地方。如果在高地址则内核会被先解压到一个临时缓冲区中待完成后再将其加载到物理地址为x的地方。()最后跳转到物理地址为x的地方执行。到此为止代码执行操作就由另外一个startup()函数来接管。也就是说第二个startup()函数接管了启动过程。l第二个startup()函数完成的功能解压缩Linux内核的工作由另外一个startup()函数来完成。该函数位于usrsrclinuxarchikernelheadS文件中。这时你可能会说两个不同的函数用同一个名字不会出错吗?答案是不会的。因为两个函数都是到自己初始地址去执行并且都有自己的执行环境所以不会出错。下面我们来看一下第二个startup()函数的功能。当执行这个函数时实际上是为第一个Linux进程(process)设置环境。这个函数将执行下面的操作:()寄存器将以最后的值进行初始化()为process设置内核模式栈()调用并且执行setupidt()函数该函数将把所有的IDT填充空值()把从BIOS中获得的参数放在第一页的框架中()识别处理器的模式()使用GDT和IDT表加载gdtr和idtr寄存器()最后跳到startkernel()函数。lstartkernel()函数功能startkernel()函数完成Linux内核的初始化工作。这个函数执行后所有的基本内核组件都将被初始化。这也是整个启动过程的最后一步。该函数将完成以下的功能:()输出Linux版本信息(printk(linuxbanner))()设置与体系结构相关的环境(setuparch())――>页表结构初始化(paginginit())()提取并分析核心启动参数(从环境变量中读取参数设置相应标志位等待处理(parseoptions())()使用"archalphakernelentryS"中的入口点设置系统自陷入口(trapinit())()使用alphamv结构和entryS入口初始化系统IRQ(initIRQ())()核心进程调度器初始化(包括初始化几个缺省的Bottomhalfschedinit())()时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等timeinit())()控制台初始化(为输出信息而先于PCI初始化consoleinit())()初始化可安装模块机制,计算出内核符号表的大小initmodules()()剖析器数据结构初始化(profbuffer和proflen变量)()核心Cache初始化(描述Cache信息的Cachekmemcacheinit())()延迟校准(获得时钟jiffies与CPU主频ticks的延迟calibratedelay())()内存初始化(设置内存上下界和页表项初始值meminit())()创建和设置内部及通用cache("slabcache"kmemcachesizesinit())()创建页cache(内存页hash表初始化pgtablecacheinit())()根据物理内存的大小计算出允许创建线程(包括进程)的数量forkinit(nummappedpages)()proccachesinit()vfscachesinit(numphyspages)bufferinit(numphyspages)。都是为有关的管理机制建立起专用的slab缓冲区队列。()分配空间建立起缓冲页面杂凑表pagehashtable(pagecacheinit())()对SysV进程间通信机制的初始化ipcinit()()创建信号队列cache("signalqueue"signalsinit())()检查体系结构漏洞(对于alpha此函数为空checkbugs())()SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核此函数为空smpinit())启动init过程(创建第一个核心线程调用init()函数原执行序列调用cpuidle()等待调度init())至此startkernel()结束基本的核心环境已经建立起来了linit()函数作用init()函数作为核心线程首先锁定内核(仅对SMP机器有效)然后调用dobasicsetup()完成外设及其驱动程序的加载和初始化。过程如下:总线初始化(比如pciinit())网络初始化(所有协议的初始化过程sockinit())创建事件管理核心线程(startcontextthread()函数启动contextthread()过程并重命名为keventd)启动任何使用initcall标识的函数(方便核心开发者添加启动函数doinitcalls())doinitcalls()àpartionsetup()àdeviceinit()是所有外设初始化的总入口文件系统初始化(filesystemsetup()主要是devfs)安装root文件系统(mountroot())至此dobasicsetup()函数返回init()在释放启动内存段(freeinitmem())并给内核解锁以后init()打开devconsole设备重定向stdin、stdout和stderr到控制台最后搜索文件系统中的init程序(或者由init=命令行参数指定的程序)并使用execve()系统调用加载执行init程序。init()函数到此结束内核的引导部分也到此结束了这个由startkernel()创建的第一个线程已经成为一个用户模式下的进程了。此时系统中存在着六个运行实体:startkernel()本身所在的执行体这其实是一个"手工"创建的线程它在创建了init()线程以后就进入cpuidle()循环了它不会在进程(线程)列表中出现、模式向保护模式的转换以及内核使用物理地址向虚拟地址的转换CPU在跳转到bootsect时还处于位实地址模式然后在setup的执行过程中转入位保护模式的段式寻址方式。在bootsect和setup中都利用BIOS提供的调用来完成一些比较大的操作如读磁盘取得BIOS在加电自检是搜集到的有关内存的信息等等。一旦转入内核映像本身的执行就不再需要BIOS了。l模式转换的控制:x保护模式比实模式多了几个寄存器:全局描述符表寄存器(GDTR),中断描述符表寄存器(IDTR)局部描述符表寄存器(LDTR)和任务寄存器(TR)。另外一些寄存器的功能得到了扩展例如指令指针成为EIP,长度位标志寄存器(EFLAGS)的更多位得到了利用。四个控制寄存器CR-CR都得到了利用。GDTR(位)在物理存储器地址空间定义了全局描述符表GDT(每个描述符字节)。IDTR(长度同GDTR)定义了中断描述符表IDT。(个中断每个中断门字节)在从实模式转到保护模式前必须将GDTR的基址BASE和限长limit(位)的值装入GDTRIDTR也是。这两个表的装入和保存有特殊的指令LGDTSGDTLIDTSIDT位的LDTR并不直接定义一个局部描述符表他只是一个指向GDT中LDT描述符的选择符。选择符装入LDTR相应的描述符就能从全局存储器中读出来装入局部描述符表高速缓存该描述符为当前任务创建了一个LDT。控制寄存器CR的低五位是机器状态字MSW包含保护模式配置和状态信息。PE(保护模式允许)位重启时清零允许实模式操作为进入保护模式将PE设为。一旦处于保护模式就不能再设回到实模式除非重启。MP(数学协处理器存在)设为表示该系统有一个数学协处理器。如果用到了软件模拟器执行数学运算那么EM(模拟)位应设为一次只能设置其中的位。R(扩展类型)表示用的是还是R为表示。TS(任务切换)在做任务切换的时候自动设置。在保护模式下支持分页CR的PG位设为表示允许分页CR包含页目录基址寄存器PDBR。任务寄存器(TR)是在保护模式下的任务切换中用到。该寄存器存放位的选择符用来指示全局描述符表中描述符的位置当选择符装入TR相应的任务状态段(TSS)描述符自动由存储器读出并装入到任务描述符缓存中。几条重要的系统指令:LGDTSGDTLIDTSIDTLMSWSMSW指令LMSW和SMSW分别用于装入和保存机器状态字信息。他们是用于从实模式转换到保护模式的指令需要将MSW的最右位设为。SMSWAXORAXLMSWAXl地址的转换在正常运行时整个内核映像都应该在系统空间系统空间地址实连续的线性的虚拟地址和物理地址间有个固定的位移:xC。内核影像的起点是stext,引导核压缩后的整个映像放在内存中从x即Mb开始的空间。CPU执行内核映像的入口start就在内核映像开头的地方因此物理地址是x,虚拟地址就成了xC。CPU在进入start时运行于保护模式下的段式寻址方式。段描述表中与KERNELCS和KERNELDSCr页目录基址寄存器(PDBR)缺页线性地址保留保留保留PGTSEMMPPERCrCrCr相对应的描述项所提供的基地址都是所以实际产生的是线性地址。其中代码段寄存器CS已在进入start之前设置成KERNELCS数据段寄存器尚未设置为KERNELDS。不过虽然代码段已设置从而start的地址为xC。但是在转入这个入口时使用的指令是“ljmpX”而不是“ljmpstartup”所以装入COU寄存器IP的地址是物理地址x而不是虚拟地址CPU在进入startup后会继续以物理地址取指令。只要不在代码段中引用某个地址例如项某个地址做绝对转移或者调用某个子程序就可以一直这样运行下去而与CS的内容无关此外CPU的中断已在进入startup前关闭。

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/11

LINUX 内核解读1

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利