2345技术员联盟

详解linux系统启动过程

  • 来源:www.it892.com 原创,转载,摘录,分享
  • 时间:2012-05-01
  • 阅读:
  • 本文标签:linux系统启动过程

Bootloader
     
在Alpha/AXP平台上引导Linux通常有两种方法,一种是由MILO及其他类似的引导程序引
导,另一种是由Firmware直接引导。MILO功能与i386平台的LILO相近,但内置有基本的磁盘
驱动程序(如IDE、SCSI等),以及常见的文件系统驱动程序(如ext2,iso9660等),
firmware有ARC、SRM两种形式,ARC具有类BIOS界面,甚至还有多重引导的设置;而SRM则具
有功能强大的命令行界面,用户可以在控制台上使用boot等命令引导系统。ARC有分区
(Partition)的概念,因此可以访问到分区的首扇区;而SRM只能将控制转给磁盘的首扇区。
两种firmware都可以通过引导MILO来引导Linux,也可以直接引导Linux的引导代码。
     
“arch/alpha/boot”下就是制作Linux
Bootloader的文件。“head.S”文件提供了对 OSF
PAL/1的调用入口,它将被编译后置于引导扇区(ARC的分区首扇区或SRM的磁盘0扇区),
得到控制后初始化一些数据结构,再将控制转给“main.c”中的start_kernel(),
start_kernel()向控制台输出一些提示,调用pal_init()初始化PAL代码,调用openboot()
打开引导设备(通过读取Firmware环境),调用load()将核心代码加载到START_ADDR(见
“include/asm-alpha/system.h”),再将Firmware中的核心引导参数加载到ZERO_PAGE(0)
中,最后调用runkernel()将控制转给0x100000的kernel,bootloader部分结束。
     
Bootloader中使用的所有“srm_”函数在“arch/alpha/lib/”中定义。
     
以上这种Boot方式是一种最简单的方式,即不需其他工具就能引导Kernel,前提是按照
Makefile的指导,生成bootimage文件,内含以上提到的bootloader以及vmlinux,然后将
bootimage写入自磁盘引导扇区始的位置中。
     
当采用MILO这样的引导程序来引导Linux时,不需要上面所说的Bootloader,而只需要
vmlinux或vmlinux.gz,引导程序会主动解压加载内核到0x1000(小内核)或0x100000(大
内核),并直接进入内核引导部分,即本文的第二节。
     
        对于I386平台
       
i386系统中一般都有BIOS做最初的引导工作,那就是将四个主分区表中的第一个可引导
分区的第一个扇区加载到实模式地址0x7c00上,然后将控制转交给它。
     
     
在“arch/i386/boot”目录下,bootsect.S是生成引导扇区的汇编源码,它首先将自己
拷贝到0x90000上,然后将紧接其后的setup部分(第二扇区)拷贝到0x90200,将真正的内核
代码拷贝到0x100000。以上这些拷贝动作都是以bootsect.S、setup.S以及vmlinux在磁盘上
连续存放为前提的,也就是说,我们的bzImage文件或者zImage文件是按照bootsect,setup,
vmlinux这样的顺序组织,并存放于始于引导分区的首扇区的连续磁盘扇区之中。
     
bootsect.S完成加载动作后,就直接跳转到0x90200,这里正是setup.S的程序入口。
setup.S的主要功能就是将系统参数(包括内存、磁盘等,由BIOS返回)拷贝到
0x90000-0x901FF内存中,这个地方正是bootsect.S存放的地方,这时它将被系统参数覆盖。
以后这些参数将由保护模式下的代码来读取。
     
除此之外,setup.S还将video.S中的代码包含进来,检测和设置显示器和显示模式。最
后,setup.S将系统转换到保护模式,并跳转到0x100000(对于bzImage格式的大内核是
0x100000,对于zImage格式的是0x1000)的内核引导代码,Bootloader过程结束。
     
        对于2.4.x版内核
       
没有什么变化。
     
     

Kernel引导入口
     
     
        对于I386平台
       
在i386体系结构中,因为i386本身的问题,在"arch/alpha/kernel/head.S"中需要更多的设置,但最终也是通过call
SYMBOL_NAME(start_kernel)转到start_kernel()这个体系结构无关的函数中去执行了。
     
     
所不同的是,在i386系统中,当内核以bzImage的形式压缩,即大内核方式
(__BIG_KERNEL__)压缩时就需要预先处理bootsect.S和setup.S,按照大核模式使用$(CPP)
处理生成bbootsect.S和bsetup.S,然后再编译生成相应的.o文件,并使用
"arch/i386/boot/compressed/build.c"生成的build工具,将实际的内核(未压缩的,含
kernel中的head.S代码)与"arch/i386/boot/compressed"下的head.S和misc.c合成到一起,其中的head.S代替了"arch/i386/kernel/head.S"的位置,由Bootloader引导执行
(startup_32入口),然后它调用misc.c中定义的decompress_kernel()函数,使用
"lib/inflate.c"中定义的gunzip()将内核解压到0x100000,再转到其上执行
"arch/i386/kernel/head.S"中的startup_32代码。
     
        对于2.4.x版内核
       
没有变化。
     
     

核心数据结构初始化--内核引导第一部分
     
     
start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。
这些动作有的是公共的,有的则是需要配置的才会执行的。
      在start_kernel()函数中,
     
       

输出Linux版本信息(printk(linux_banner))
设置与体系结构相关的环境(setup_arch())
页表结构初始化(paging_init())

使用"arch/alpha/kernel/entry.S"中的入口点设置系统自陷入口(trap_init())
使用alpha_mv结构和entry.S入口初始化系统IRQ(init_IRQ())

核心进程调度器初始化(包括初始化几个缺省的Bottom-half,sched_init())

时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等,time_init())

提取并分析核心启动参数(从环境变量中读取参数,设置相应标志位等待处理,(parse_options())
控制台初始化(为输出信息而先于PCI初始化,console_init())
剖析器数据结构初始化(prof_buffer和prof_len变量)
核心存储器' target='_blank'>Cache初始化(描述Cache信息的Cache,kmem_cache_init())

延迟校准(获得时钟jiffies与CPU主频ticks的延迟,calibrate_delay())
内存初始化(设置内存上下界和页表项初始值,mem_init())

创建和设置内部及通用cache("slab_cache",kmem_cache_sizes_init())
创建uid taskcount SLAB
cache("uid_cache",uidcache_init())
创建文件cache("files_cache",filescache_init())
创建目录cache("dentry_cache",

dcache_init())
创建与虚存相关的cache("vm_area_struct","mm_struct",vma_init())

块设备读写缓冲区初始化(同时创建"buffer_head"cache用户加速访问,buffer_init())
创建页cache(内存页hash表初始化,page_cache_init())
创建信号队列cache("signal_queue",signals_init())
初始化内存inode表(inode_init())
创建内存文件描述符表("filp_cache",file_table_init())
检查体系结构漏洞(对于alpha,此函数为空,check_bugs())

SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核,此函数为空,smp_init())

启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle()
等待调度,init())

至此start_kernel()结束,基本的核心环境已经建立起来了。
     
     
        对于I386平台
       
i386平台上的内核启动过程与此基本相同,所不同的主要是实现方式。
     
     
        对于2.4.x版内核
       
2.4.x中变化比较大,但基本过程没变,变动的是各个数据结构的具体实现,比如Cache。
     
     

外设初始化--内核引导第二部分
     
init()函数作为核心线程,首先锁定内核(仅对SMP机器有效),然后调用
do_basic_setup()完成外设及其驱动程序的加载和初始化。过程如下:
     
       

总线初始化(比如pci_init())

网络初始化(初始化网络数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,将调用protocols结构中包含的所有协议的初始化过程,sock_init())

创建bdflush核心线程(bdflush()过程常驻核心空间,由核心唤醒来清理被写过的内存缓冲区,当bdflush()由kernel_thread()启动后,它将自己命名为kflushd)

创建kupdate核心线程(kupdate()过程常驻核心空间,由核心按时调度执行,将内存缓冲区中的信息更新到磁盘中,更新的内容包括超级块和inode表)

设置并启动核心调页线程kswapd(为了防止kswapd启动时将版本信息输出到其他信息中间,核心线调用kswapd_setup()设置kswapd运行所要求的环境,然后再创建
kswapd核心线程)

创建事件管理核心线程(start_context_thread()函数启动context_thread()过程,并重命名为keventd)

设备初始化(包括并口parport_init()、字符设备chr_dev_init()、块设备
blk_dev_init()、SCSI设备scsi_dev_init()、网络设备net_dev_init()、磁盘初始化及分区检查等等,device_setup())
执行文件格式设置(binfmt_setup())

启动任何使用__initcall标识的函数(方便核心开发者添加启动函数,do_initcalls())
文件系统初始化(filesystem_setup())
安装root文件系统(mount_root())

至此do_basic_setup()函数返回init(),在释放启动内存段(free_initmem())并给内核解锁以后,init()打开
/dev/console设备,重定向stdin、stdout和stderr到控制台,最后,搜索文件系统中的init程序(或者由init=命令行参
数指定的程序),并使用 execve()系统调用加载执行init程序。
init()函数到此结束,内核的引导部分也到此结束了,这个由start_kernel()创建的第一个线程已经成为一个用户模式下的进程了。此时系统中存在着六个运行实体:

本文来自电脑技术网www.it892.com),转载本文请注明来源.
本文链接:http://www.it892.com/content/system/Linux/20120501/2482.html
热点排行
无觅相关文章插件,快速提升流量