“内核版本:linux-2.6”
linux系统基本启动流程如下:
开机->BOOT引导->grub引导->加载内核->启动系统
本文重点分析加载内核过程及系统启动流程。
(1)内核入口
系统进入内核的入口是/init/main.c的start_kernel()函数,这个相当于系统执行的第一个进程:
/*
* Activate the first processor. 启动第一个进程。
*/
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern char saved_command_line[];
extern struct kernel_param __start___param[], __stop___param[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
setup_per_zone_pages_min();
setup_per_cpu_areas();
/*
* Mark the boot cpu "online" so that it can call console drivers in
* printk() and can access its per-cpu storage.
*/
smp_prepare_boot_cpu();
build_all_zonelists();
page_alloc_init();
/* linux内存管理初始化 ,建立初始化节点和内存域的数据结构,建立内核的内存分配器,见下文1介绍 */
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
trap_init();
rcu_init();
/* 初始化 RCU同步机制 */
pidhash_init();
/* PID hash链表初始化,见下文2 */
sched_init();
/* 进程调度相关初始化 */
time_init();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
profile_init();
local_irq_enable();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
page_address_init();
mem_init();
kmem_cache_init();
if (late_time_init)
late_time_init();
calibrate_delay();
pidmap_init();
pgtable_cache_init();
pte_chain_init();
fork_init(num_physpages);
proc_caches_init();
buffer_init();
security_scaffolding_startup();
vfs_caches_init(num_physpages);
radix_tree_init();
/* 基数树初始化,与文件系统内存管理相关 */
/* rootfs populating might need page-writeback */
page_writeback_init();
populate_rootfs();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
check_bugs();
printk("POSIX conformance testing by UNIFIX\n");
/*
* We count on the initial thread going ok
* Like idlers init is an unlocked kernel thread, which will
* make syscalls (and thus be locked).
*/
init_idle(current, smp_processor_id());
/* 初始化 idle进程 */
/* Do the rest non-__init'ed, we're now alive */
rest_init();
/* 初始化 init进程并启动 */
}
(2)init进程
init 进程通过kernel_thread建立,并传递CLONE_KERNEL标志继承了内核进程属性,这是内核下第一个进程,我们先看这个函数实现:
这里重点关注run_init_process("/sbin/init"),
内核init进程通过execve调用/sbin/init程序启动了用户第一个进程/sbin/init(1号进程),并开始执行系统初始化流程。
然后内核进入cpu_idle进程,如下:
无休止运行直到有进程需要调度。
关于2号进程kthreadd,高级版本内核在rest_init中创建启动:
该进程始终运行在内核空间,负责所有内核线程的调度和管理
(3)系统初始化阶段
当init启动后,它通过执行各种启动事务来完成引导进程(检查监视文件系统,启动后台程序daemons等),直至完成用户操作环境的配置工作。
这里主要涉及4个程序:init、getty、login和shell程序。
init进程根据/etc/rc文件进行配置信息的设置;
附录:
1. linux内存管理层次
(1)存储节点(node):CPU被划分为多个节点(node),内存则被分簇,每个CPU对应一个本地物理内存,即一个CPU-node对应一个内存簇bank,每个内存簇被认为是一个节点。
(2)管理区(zone):每个物理内存节点node被划分为多个内存管理区域,用于表示不同范围的内存,内核可以使用不同的映射方式映射物理内存;低端16M被描述为ZONE_DMA,可直接映射内核的普通内存域ZONE_NORMAL,超出内核段的物理地址域ZONE_HIGHMEM。
(3)页面(page):内存被细分为多个页面帧,页面是最基本的内存分配单位
2. pid hash链表
内核通过pid hash链表从进程的PID导出对应的进程描述符指针,这样快速访问进程。