bootsect.S,系统引导程序,一般不超过512字节。
在PC系统结构中,线性地址0xA0000以上,即640K以上用于图形接口卡和BIOS自身,640K以下为系统的基本内存。如果配置更多的内存,则0x100000,即1MB处开始称为高内存。当BIOS引导一个系统时,总是把引导扇区读入到基本内存地址为0x7c00的地方,然后跳转到此执行引导扇区的代码。这段代码将自身搬运到0x90000处,并跳转到那继续执行,然后通过BIOS提供的读磁盘调用“int 0x13”从磁盘上读入setup和内核映像。其中setup的映像读入到0x90200处,然后跳转到setup的代码中。
从0x90000到0xA0000一共64K,bootsect仅占512字节,所以setup大小理论上可到63.5KB。
在Linux2.4版本以前,在最前面的512字节里保护了一个mini “boot loader”,只要拷贝启动代码运行就可从软盘启动;但在2.6版本中不再保护这样的”boot loader”,所以必须在第一个磁盘分区上存储一个合适的boot loader才能从软盘启动,软盘、硬盘和光驱启动都是一样的过程。
setup进行映像的解压缩,从BIOS收集一些数据,在控制台显示一些信息。
基本内存中开头一部分空间是保留给BIOS自己用的,另一方面对于Linux内核的引导也需要保留一些运行空间,一共保存了64K。基本内存中用于内核映像的就是8*64K=512K,其中顶端留4K用于引导命令行及从BIOS获取需要传递给内核的数据。内核映像一般都经过压缩,压缩后的映像和引导扇区及辅助引导程序的映像拼接在一起,成为内核的引导映像。大小不超过508K的映像称为小映像zImage,早期版本放在0x10000位置处,否则称为大内核bzImage,放在0x100000位置处。
CPU在bootsect时处于16位实地址模式,然后在setup的执行过程中转入32位保护模式。
Setup从BIOS中读取系统数据(内存大小、显卡模式、磁盘等参数),将数据保存在0x90000-0x901FF,覆盖了bootsect的内容。设置32位运行方式:加载中断描述表寄存器IDTR、全局描述表寄存器GDTR;临时设置IDT表和GDT表,并在GDT表中设置内核代码段和数据段的描述符,在Head.S中会根据内核的需要重新设置这些描述符表;开启A20地址线;重新设置两个中断控制器8259A,将硬件中断号重新设置为0x20和0x2f;最后设置CPU的控制寄存器CR0(机器状态字)的保护模式比特(PE)位,从而进入32位保护模式运行;然后跳转到head.S中的startup_32执行。
对于小内核映像放在0x10000处,Setup会把system从0x10000移到0x0000开始处。对于大内核映像,vmlinux中普通内核代码被编译成以PAGE_OFFSET+1MB为起始地址,在Head.S中初始化代码把虚拟地址减去PAGE_OFFSET就能得到以1MB为起始位置的物理地址,这也正是内核映像在物理内存中的存放位置。
Head.S中的startup_32主要用于开启页面单元。初始化工作在编译过程中开始进行,它先定义一个称为swapper_pg_dir的数组,使用链接器指示在地址0x00101000。然后分别为两个页面pg0和pg1创建页表项。第一组指向pg0和pg1的指针放在能覆盖1~9MB内存的位置,第二组指针放在PAGE_OFFSET+1MB的位置。一旦开始页机制,在上述页表和页表项指针建立后可以保证,在内核映像中不论是采用物理地址还是虚拟地址,都可以进行正确的页面映射。内核其他部分的页表初始化在paging_init()中完成。映射建立后,通过设置cr0寄存器中的某位开启页面映射,然后通过一个跳转指令保证指令指针的正确性。
1.Bootsect启动过程:
假设用LILO启动,启动时用户可以选择启动哪个操作系统。LILO将boot loader分为两部分,一部分放到启动分区的第一个扇区;
1)        BIOS将MBR或启动分区的第一个扇区的启动部分加载到地址0x00007c00处;
2)        该程序将自身移到0x00096a00,建立实模式栈(从0x00098000到0x000969ff),将LILO的第二部分加载到0x00096c00处,然后跳转到此执行;
3)        然后第二部分程序从磁盘读取一个可启动的操作系统列表让用户选择,最后用户选择每个OS后,boot loader可以拷贝不启动分区或者之间拷贝内核映像到RAM中去;
4)        加载Linux内核映像时,LILO boot loader首先调用BIOS例程显示”Loading …”信息;
5)        调用BIOS例程加载内核映像的初始化部分到RAM上,内核映像的前512字节放在0x00090000位置,setup()函数代码放在0x00090200位置;
6)        接着调用BIOS例程装载内核映像的其余部分,映像可能放在低地址0x00010000(使用make zImage编译的小内核映像)或者高地址0x00100000(使用make bzImage编译的大内核映像)。
7)        然后跳至刚刚setup部分。
2.Setup.S分析
setup()汇编函数被连接器放在内核映像文件中的0x200偏移处。Setup函数必须初始化计算机中的硬件设备并为内核程序的执行建立环境。
1)        在ACPI兼容的系统中,调用BIOS例程建立描述系统物理内存布局的表。在早期系统中,它调用BIOS例程返回系统可以的RAM大小;
2)        设置键盘的重复延迟和速率;
3)        初始化显卡;
4)        检测IBM MCA总线、PS/2鼠标设备、APM BIOS支持等;
5)        如果BIOS支持Enhanced Disk Drive Services (EDD),将调用正确的BIOS例程建立描述系统可用硬盘的表;
6)        如果内核加载在低RAM地址0x00010000,则把它移动到0x00001000处;如果映像加载在高内存1M位置,则不动;
7)        启动位于8042键盘控制器的A20 pin。
8)        建立一个中断描述表IDT和全局描述表GDT表;
9)        如果有的话,重启FPU单元;
10)    对可编程中断控制器进行重新编程,屏蔽所以中断,级连PIC的IRQ2不需要;
11)    设置CR0状态寄存器的PE位使CPU从实模式切换到保护模式,PG位清0,禁止分页功能;
12)    跳转到startup_32()汇编函数, jmpi 0x100000, __BOOT_CS,终于进入内核Head.S;
3.Head.S分析
有两个不同的startup_32()函数,一个在arch/i386/boot/compressed/head.S文件中,setup结束后,该函数被放在0x00001000或者0x00100000位置,该函数主要操作:
1)        首先初始化段寄存器和临时堆栈;
2)        清除eflags寄存器的所有位;
3)        将_edata和_end区间的所有内核未初始化区填充0;
4)        调用decompress_kernel( )函数解压内核映像。首先显示"Uncompressing Linux..."信息,解压完成后显示 "OK, booting the kernel."。内核解压后,如果时低地址载入,则放在0x00100000位置;否则解压后的映像先放在压缩映像后的临时缓存里,最后解压后的映像被放置到物理位置0x00100000处;
5)        跳转到0x00100000物理内存处执行;
   
    解压后的映像开始于arch/i386/kernel/head.S 文件中的startup_32()函数,因为通过物理地址的跳转执行该函数的,所以相同的函数名并没有什么问题。该函数未Linux第一个进程建立执行环境,操作如下:
1)         初始化ds,es,fs,gs段寄存器的最终值;
2)        用0填充内核bss段;
3)        初始化swapper_pg_dir数组和pg0包含的临时内核页表:
l          将swapper_pg_dir(0x1000)和pg0(0x2000)清空,swapper_pg_dir作为整个系统的页目录;
l          将pg0作为第一个页表,将其地址赋到swapper_pg_dir的第一个32位字中。
l          同时将该页表项也赋给swapper_pg_dir的第3072个入口,表示虚拟地址0xc0000000也指向pg0。
l          将pg0这个页表填满指向内存前4M。
l          在cr3寄存器中存放PGD的地址,并设置cr0寄存器中的PG位,启用分页支持。
4)        建立进程0idle进程的内核模式的堆栈;
5)        再次清除eflags寄存器的所有位;
6)        调用setup_idt()用非空的中断处理函数填充IDT表;
7)        将从BIOS获取的系统参数传递到操作系统的第一个页面帧;
8)        识别处理器的模式;
9)        将GDT和IDT表的地址加载到gdtr和idtr寄存器中;
10) 跳转到start_kernel函数,这个函数是第一个C编制的函数,内核又有了一个新的开始。
4.start_kernel()分析:
1)        调度器初始化,调用sched_init();
2)        调用build_all_zonelists函数初始化内存区;
3)        调用page_alloc_init()和mem_init()初始化伙伴系统分配器;
4)        调用trap_init()和init_IRQ()对中断控制表IDT进行最后的初始化;
5)        调用softirq_init() 初始化TASKLET_SOFTIRQ和HI_SOFTIRQ;
6)        Time_init()对系统日期和时间进行初始化;
7)        调用kmem_cache_init()初始化slab分配器;
8)        调用calibrate_delay()计算CPU时钟频率;

通过调用 kernel_thread() 启动进程 1init 进程的内核线程,然后该线程再创建其他的内核线程执行 /sbin/init 程序。

很好的linux启动说明( bootsect.S、setup.S、head.S)相关推荐

  1. Linux启动过程综述(转)

    本文转自:http://www.ibm.com/developerworks/cn/linux/kernel/startup/ 2001 年 5 月 01 日 本文以Redhat 6.0 Linux ...

  2. Linux 启动过程详解

    随着Linux的应用日益广泛,特别是在网络应用方面,有大量的网络服务器使用Linux操作系统.由于Linux的桌面应用和Windows相比还有一定的差距,所以在企业应用中往往是Linux和Window ...

  3. linux启动过程(参考内核0.11)

    当PC启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFFF0处的代码,也就是ROM-BIOS起始位置的代码.BIOS先进行一系列的系统自检,然后初始化位于地址0的中断向量表 ...

  4. Linux 启动详解之init

    1.init初探 init是Linux系统操作中不可缺少的程序之一.init进程,它是一个由内核启动的用户级进程,然后由它来启动后面的任务,包括多用户环境,网络等. 内核会在过去曾使用过init的几个 ...

  5. linux启动时间极限优化,Linux启动时间的极限优化

    在上次完成嵌入式应用的Linux裁减后,Linux的启动时间仍需要7s左右,虽然勉强可以接受,但仍然没有达到我个人所追求的目标--2s以内.况且,在实际的商用环境中,设备可靠性的要求可是"5 ...

  6. Linux启动跟windows启动,Windows,Linux启动机制简介

    前言 本文内容只集中在操作系统启动原理的讲解上,不涉及启动的技术细节,因为这些细节都可以通过网络或者相关代码了解.只有了解了启动原理,才能在分析和解决有关启动的问题时具有针对性,不会有无从下手的感觉. ...

  7. linux 启动流程图

    代码的执行 代码执行过程中,CS:IP始终指向下一条指令的地址~ 顺序执行:总是指向地址连续的下一条指令 跳转/分支:执行这样的指令的时候,cs : eip的值会根据程序需要被修改 call:将当前c ...

  8. linux 主进程 等待,Linux启动与进程

    操作系统中,前台进程和后台进程有什么区别?特征是什么? 后台程序基本上不和用户交互,优先级别稍微低一点 前台的程序和用户交互,需要较高的响应速度,优先级别稍微高一点 直接从后台手工启动一个进程用得比较 ...

  9. 服务器系统加速,系统加速技巧:让Linux启动更加快速

    #5: 使用轻型发行版 不要装载重型的Fedora,为什么不试一下Gentoo.Arch或Puppy Linux呢?这些较小的发行版的启动时间比更加臃肿的Fedora(甚至Ubuntu)要快很多.在较 ...

最新文章

  1. 2019年这50个Kafka面试题,你知道答案么?
  2. Nginx其他配置——日志管理、网页压缩、访问控制
  3. 零基础编程入门python视频-编程零基础应当如何开始学习 Python?
  4. Text-CNN-文本分类-keras
  5. 蓝桥杯练习系统习题-算法训练2
  6. MongoDB数据库(8.Python中使用mongodb数据库以及pymongo模块用法)
  7. linux 编译3g驱动_linux下使用3G拨号上网 以及3g驱动设置
  8. 95-35-010-Topic-Topic的新建 源码解析
  9. 基于tkinter模块创建GUI程序(python)
  10. kafka producer发送消息 Failed to update metadata after问题
  11. python判断火车票座位是否靠窗_python如何判断购买火车有没有对应的列车,没有就输出未查询到对应的车次?...
  12. lnmp php 5.4,linux下搭建LNMP(linux+nginx+mysql+php)环境之php5.4安装
  13. Quartus ll显示30天到期
  14. 金融分析与风险管理——投资组合收益率、波动率计算
  15. js系列七:生命周期
  16. Having dreams is what makes life tolerable.
  17. 微信小程序 - 公农历通用时间选择器组件
  18. C++ 读取 HDF5
  19. Kafka消息积压案例分析
  20. Redis操作str、list、hash、set、sortedset、bitmap

热门文章

  1. 2016-8-18晨型养成第三天
  2. 关于WPS页面横向问题
  3. 购买绝版书的好地方——淘宝
  4. 选择Data类型在Access与Sql中的区别
  5. dubbo自定义异常传递信息丢失问题解决
  6. SecureFX连接Linux后文件夹中文乱码问题解决
  7. Windows 10累积更新发布:RS3正式版前最后一更
  8. idea 使用正则表达式 进行匹配替换
  9. MySQL中INSERT IGNORE INTO和REPLACE INTO的使用
  10. 《Excel与VBA开发》一书上市时间