linux3.0.1内核启动分析

一、第一阶段:(arch/arm/boot/compressed/head.S)
1、获得zImage的起始地址和结束地址,体系结构ID
.word 0x016f2818 @ Magic numbers to help the loader帮助加载程序
.word start @ absolute load/run zImage address加载/运行zImage地址
.word _edata @ zImage end addresszImage结束地址
 THUMB( .thumb )
1: mov r7, r1 @ save architecture ID保存体系结构ID
mov r8, r2 @ save atags pointer保存atags指针


2、进入SVC模式
#ifndef __ARM_ARCH_2__
/*
* Booting from Angel - need to enter SVC mode and disable
* FIQs/IRQs (numeric definitions from angel arm.h source).
* We only do this if we were in user mode on entry.
*引导时需要进入SVC模式并禁用FIQs /终端请求
*/
mrs r2, cpsr @ get current mode得到当前模式
tst r2, #3 @ not user?
bne not_angel
mov r0, #0x17 @ angel_SWIreason_EnterSVC设置为SVC模式
 ARM( swi 0x123456 ) @ angel_SWI_ARM
 THUMB( svc 0xab ) @ angel_SWI_THUMB
not_angel:
mrs r2, cpsr @ turn off interrupts to关闭中断,
orr r2, r2, #0xc0 @ prevent angel from running阻止angel运行
msr cpsr_c, r2
#else
teqp pc, #0x0c000003 @ turn off interrupts关闭中断
#endif


3、确定最终的内核映像的地址
.text
#ifdef CONFIG_AUTO_ZRELADDR
@ determine final kernel image address确定最终的内核映像的地址
mov r4, pc
and r4, r4, #0xf8000000
add r4, r4, #TEXT_OFFSET
#else
ldr r4, =zreladdr
#endif


4、打开高速缓存
bl cache_on  /*打开高速缓存*/


5、初始化变量值
restart: adr r0, LC0
ldmia r0, {r1, r2, r3, r6, r10, r11, r12}
ldr sp, [r0, #28]


/*
* We might be running at a different address.  We need
* to fix up various pointers.
*/
sub r0, r0, r1 @ calculate the delta offset计算偏移值
add r6, r6, r0 @ _edata
add r10, r10, r0 @ inflated kernel size location内核位置


/*
* The kernel build system appends the size of the
* decompressed kernel at the end of the compressed data
* in little-endian form.
*内核构建系统附加的大小  *解压内核的压缩数据  *在低位优先的形式
*/
ldrb r9, [r10, #0]
ldrb lr, [r10, #1]
orr r9, r9, lr, lsl #8
ldrb lr, [r10, #2]
ldrb r10, [r10, #3]
orr r9, r9, lr, lsl #16
orr r9, r9, r10, lsl #24


#ifndef CONFIG_ZBOOT_ROM
/* malloc space is above the relocated stack (64k max) */
add sp, sp, r0
add r10, sp, #0x10000
#else
/*
* With ZBOOT_ROM the bss/stack is non relocatable,
* but someone could still run this code from RAM,
* in which case our reference is _edata.
*/
mov r10, r6
#endif


6、解压zImage
/*
 * Check to see if we will overwrite ourselves.
 *   r4  = final kernel address最后内核地址
 *   r9  = size of decompressed image解压图像的大小
 *   r10 = end of this image, including  bss/stack/malloc space if non XIP
 * We basically want:
 *   r4 - 16k page directory >= r10 -> OK
 *   r4 + image length <= current position (pc) -> OK
 */
add r10, r10, #16384
cmp r4, r10
bhs wont_overwrite
add r10, r4, r9
   ARM( cmp r10, pc )
 THUMB( mov lr, pc )
 THUMB( cmp r10, lr )
bls wont_overwrite


7、移动解压后的内核到r10
/*
 * Relocate ourselves past the end of the decompressed kernel.
 *   r6  = _edata
 *   r10 = end of the decompressed kernel
 * Because we always copy ahead, we need to do it from the end and go
 * backward in case the source and destination overlap.
 */
/*
* Bump to the next 256-byte boundary with the size of
* the relocation code added. This avoids overwriting
* ourself when the offset is small.
*/
add r10, r10, #((reloc_code_end - restart + 256) & ~255)
bic r10, r10, #255


/* Get start of code we want to copy and align it down. 得到我们想要的开始的代码复制和对齐*/
adr r5, restart
bic r5, r5, #31


sub r9, r6, r5 @ size to copy
add r9, r9, #31 @ rounded up to a multiple
bic r9, r9, #31 @ ... of 32 bytes
add r6, r9, r5
add r9, r9, r10


1: ldmdb r6!, {r0 - r3, r10 - r12, lr}
cmp r6, r5
stmdb r9!, {r0 - r3, r10 - r12, lr}
bhi 1b


/* Preserve offset to relocated code. 保存抵消搬迁代码*/
sub r6, r9, r6


#ifndef CONFIG_ZBOOT_ROM
/* cache_clean_flush may use the stack, so relocate it */
add sp, sp, r6
#endif


8、清空高速缓存
bl cache_clean_flush


9、跳转到移动后的内核开始处运行
adr r0, BSYM(restart)
add r0, r0, r6
mov pc, r0 //跳转到r6后运行

二、第二阶段:(arch/arm/boot/bootp/init.S)
1、移动initrd内存磁盘镜像
_start: add lr, pc, #-0x8 @ lr = current load addr当前加载地址
adr r13, data
ldmia r13!, {r4-r6} @ r5 = dest, r6 = length
add r4, r4, lr @ r4 = initrd_start + load addr
bl move @ move the initrd

2、设置initrd参数传递给内核。
ldmia r13, {r5-r9} @ get size and addr of initrd获得initrd的大小和地址
@ r5 = ATAG_CORE
@ r6 = ATAG_INITRD2
@ r7 = initrd start
@ r8 = initrd end
@ r9 = param_struct address


ldr r10, [r9, #4] @ get first tag获得第一个标签
teq r10, r5 @ is it ATAG_CORE?
/*如果没有找到一个有效的标记列表,创建一个虚拟ATAG_CORE
 * If we didn't find a valid tag list, create a dummy ATAG_CORE entry.
 */
movne r10, #0 @ terminator
movne r4, #2 @ Size of this entry (2 words)
stmneia r9, {r4, r5, r10} @ Size, ATAG_CORE, terminator


/*
 * find the end of the tag list, and then add an INITRD tag on the end.
 * If there is already an INITRD tag, then we ignore it; the last INITRD
 * tag takes precedence.
 */
taglist: ldr r10, [r9, #0] @ tag length
teq r10, #0 @ last tag (zero length)?
addne r9, r9, r10, lsl #2
bne taglist


mov r5, #4 @ Size of initrd tag (4 words)
stmia r9, {r5, r6, r7, r8, r10}

3、调用启动内核
b kernel_start @ call kernel启动内核

三、第三阶段(init/main.c)
1、启动内核
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];


//设置处理器ID
smp_setup_processor_id();


/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
//lockdep初始化
lockdep_init();
//调试对象提前初始化
debug_objects_early_init();


/*
* Set up the the initial canary ASAP:
*/
//引导初始化堆栈
boot_init_stack_canary();
//cgroup初始化
cgroup_init_early();
//关中断
local_irq_disable();
early_boot_irqs_disabled = true;


/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 //tick初始化
tick_init();
//引导CPU初始化
boot_cpu_init();
//页地址初始化
page_address_init();
printk(KERN_NOTICE "%s", linux_banner);
//设置arch
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
//设置命令行
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
//准备引导CPU
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */


build_all_zonelists(NULL);
//页分配初始化
page_alloc_init();


printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
//解析参数
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
  __stop___param - __start___param,
  &unknown_bootoption);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
//设置日志buf
setup_log_buf(0);
//进程标识符哈希列表初始化
pidhash_init();
//虚拟文件系统高速缓存初始化
vfs_caches_init_early();
sort_main_extable();
trap_init();
//内存管理初始化
mm_init();


/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
//进程调度程序初始化
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
//关闭内核抢占
preempt_disable();
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
//高速缓存idr初始化
idr_init_cache();
//perf时间初始化
perf_event_init();
//遥控设备初始化
rcu_init();
//根树初始化
radix_tree_init();
/* init some links before init_ISA_irqs() */
//中断请求初始化
early_irq_init();
init_IRQ();
//优先树初始化
prio_tree_init();
//定时器初始化
init_timers();
hrtimers_init();
//软中断软中断
softirq_init();
timekeeping_init();
//时间初始化
time_init();
profile_init();
//调用函数初始化
call_function_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_disabled = false;
//打开中断
local_irq_enable();


/* Interrupts are enabled now so all GFP allocations are safe. */
gfp_allowed_mask = __GFP_BITS_MASK;


kmem_cache_init_late();


/*
* 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();
if (panic_later)
panic(panic_later, panic_param);


lockdep_info();


/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
//上锁自检
locking_selftest();


#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
   page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
   "disabling it.\n",
   page_to_pfn(virt_to_page((void *)initrd_start)),
   min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
//打开调试页分配
enable_debug_pagealloc();
//调试对象内存分配初始化
debug_objects_mem_init();
//检查内核内存泄漏
kmemleak_init();
//设置每个CPU页
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
//进程调度时钟初始化
sched_clock_init();
//校准延时
calibrate_delay();
pidmap_init();
//虚拟存储地址初始化
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
//线程信息缓存初始化
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
//虚拟缓存初始化
proc_caches_init();
//缓冲区初始化
buffer_init();
//key初始化
key_init();
//加密初始化
security_init();
dbg_late_init();
//虚拟文件系统缓存初始化
vfs_caches_init(totalram_pages);
//信号初始化
signals_init();
/* rootfs populating might need page-writeback */
//页回写初始化
page_writeback_init();
#ifdef CONFIG_PROC_FS
//根目录初始化
proc_root_init();
#endif
cgroup_init();
cpuset_init();
//任务统计初始化
taskstats_init_early();
//延时启禁用初始化
delayacct_init();
//检查漏洞
check_bugs();
//电源管理初始化
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();


ftrace_init();


/* Do the rest non-__init'ed, we're now alive */
rest_init();
}


//设置内核内存分配器
static void __init mm_init(void)
{
/*
* page_cgroup requires countinous pages as memmap
* and it's bigger than MAX_ORDER unless SPARSEMEM.
*/
page_cgroup_init_flatmem();
mem_init();
kmem_cache_init();
percpu_init_late();
pgtable_cache_init();
//不连续内存管理 
vmalloc_init();
}


//激活第一个处理器
static void __init boot_cpu_init(void)
{
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_active(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
}


static noinline void __init_refok rest_init(void)
{
int pid;
//rcu调度程序开始
rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
//创建PID为1的kernel_init进程
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);


/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
//初始化空闲启动任务
init_idle_bootup_task(current);
//抢占打开不重新调度进程
preempt_enable_no_resched();
//调度进程
schedule();
//抢占关闭
preempt_disable();


/* Call into cpu_idle with preempt disabled */
//在抢占关闭时调用cpu_idle
cpu_idle();
}


2、初始化内核
static int __init kernel_init(void * unused)
{
/*等到kthreadd设置
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);
/*初始化可以在任何节点分配页面
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_HIGH_MEMORY]);
/*初始化可以在任何cpu上运行。
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);


//得到当前进程的标识号
cad_pid = task_pid(current);


//设置CPU数量
smp_prepare_cpus(setup_max_cpus);


do_pre_smp_initcalls();
//探测器加锁初始化
lockup_detector_init();


//多处理器初始化
smp_init();
//多处理器调度初始化
sched_init_smp();


//加载基本设置
do_basic_setup();


//打开控制台设备
/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");


(void) sys_dup(0);
(void) sys_dup(0);
/*
* check if there is an early userspace init.  If yes, let it do all
* the work
*/
//检查是否有一个早期的用户空间初始化
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";


if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
//准备命名空间
prepare_namespace();
}


/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/


init_post();
return 0;
}


//基本设置初始化函数
static void __init do_basic_setup(void)
{
//多处理器设置初始化
cpuset_init_smp();
//用户模式helper初始化
usermodehelper_init();
//初始化临时文件系统 
init_tmpfs();
//驱动程序初始化
driver_init();
//中断程序初始化
init_irq_proc();
do_ctors();
//执行初始化调用
do_initcalls();
}


//init_post函数
static noinline int init_post(void)
{
/* need to finish all async __init code before freeing the memory */
//需要完成所有异步__init代码之前释放内存
async_synchronize_full();
free_initmem();
//只读数据区标识为只读
mark_rodata_ro();
//系统状态设置为运行状态
system_state = SYSTEM_RUNNING;
//非一致性内存访问默认操作
numa_default_policy();


//当前进程的信号标志设为不可杀死
current->signal->flags |= SIGNAL_UNKILLABLE;


//运行内存虚拟盘初始化进程
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}


/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s.  Attempting "
"defaults...\n", execute_command);
}
//运行个各sh脚本
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");


panic("No init found.  Try passing init= option to kernel. "
     "See Linux Documentation/init.txt for guidance.");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值