linux系统(二):内核及系统启动流程

“内核版本: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介绍 */

printk("Kernel command line: %s\n", saved_command_line);
parse_args("Booting kernel", command_line, __start___param,
  __stop___param - __start___param,
  &unknown_bootoption);
trap_init();

rcu_init();

        /* 初始化 RCU同步机制 */

init_IRQ();

pidhash_init();

        /* PID hash链表初始化,见下文2 */

sched_init();

        /* 进程调度相关初始化 */

softirq_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();

        /* 基数树初始化,与文件系统内存管理相关 */

signals_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导出对应的进程描述符指针,这样快速访问进程。

猜你喜欢

转载自blog.csdn.net/shilion163/article/details/80379820