核心数据结构初始化--内核引导第一部分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变量)
- 核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())
- 延迟校准(获得时钟jiffies与CPU主频ticks的延迟,calibrate_delay())
- 内存初始化(设置内存上下界和页表项初始值,mem_init())
- 创建和设置内部及通用cache("slab_cache",kmem_cache_sizes_init())
- 创建uid taskcount SLABcache("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()创建的第一个线程已经成为一个用户模式下的进程了。此时系统中存在着六个运行实体:
- start_kernel()本身所在的执行体,这其实是一个"手工"创建的线程,它在创建了init()线程以后就进入cpu_idle()循环了,它不会在进程(线程)列表中出现
- init线程,由start_kernel()创建,当前处于用户态,加载了init程序
- kflushd核心线程,由init线程创建,在核心态运行bdflush()函数
- kupdate核心线程,由init线程创建,在核心态运行kupdate()函数
- kswapd核心线程,由init线程创建,在核心态运行kswapd()函数
- keventd核心线程,由init线程创建,在核心态运行context_thread()函数
对于I386平台
基本相同。 对于2.4.x版内核
这一部分的启动过程在2.4.x内核中简化了不少,缺省的独立初始化过程只剩下网络(sock_init())和创建事件管理核心线程,而其他所需要的初始化都使用__initcall()宏包含在do_initcalls()函数中启动执行。 |