Linux kernel启动过程

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux kernel启动过程相关的知识,希望对你有一定的参考价值。

0. 声明

kernel版本号3.10.53

1. 链接脚本

arch/arm/kernel/vmlinux.lds.S ==>vmlinux.lds

生成的链接脚本(vmlinux.lds)中的重点内容如下:

OUTPUT_ARCH(arm)
ENTRY(stext)
······

根据链接脚本可知kernel执行入口为stext,搜索stext即可得到入口函数的定义。

 

2. 入口函数(stext)实现

函数stext是一个汇编函数,其定义在arch/arm/kernel/head.S中,stext函数主要做了以下几件事:

(1) 关闭irq和fiq,设置svc管理模式
(2) 判断是或支持这个CPU
(3) 判断是否支持这个单板(通过uboot传入的机器ID判断)
(4) 创建页表,为后面的MMU做准备
(5) 使能MMU并跳到__switch_data处,复制数据段,清除bss段,设置栈,调用start_kernel第一个C函数

简化后的代码如下:

ENTRY(stext)
ldr     r13, =__mmap_switched     @将__mmap_switched函数地址存放到r13寄存器 
1:      b       __enable_mmu
ENDPROC(stext)
--------------------------------------------------------------------------
 __enable_mmu:
b       __turn_mmu_on
 ENDPROC(__enable_mmu)
--------------------------------------------------------------------------
ENTRY(__turn_mmu_on)
mov     r3, r13    @将r13寄存器中的值存放到r3寄存器
mov     pc, r3     @将PC指针指向r3寄存器中存放的地址
ENDPROC(__turn_mmu_on)
-------------------------------------------------------------------------
 __mmap_switched:          @ arch/arm/kernel/head-common.S
 b       start_kernel
ENDPROC(__mmap_switched)

//调用流程总结:stext--->__enable_mmu--->__turn_mmu_on--->__mmap_switched--->start_kernel

3.start_kernel函数

函数start_kernel是kernel启动过程中调用的第一个C函数,其定义在 init/main.c 中;在start_kernel函数中执行了大量的初始化工作,以下是挑选出来的一小部分常见和常用的初始化内容:

asmlinkage void __init start_kernel(void)
{
    /*打印以下linux版本信息:       
    “Linux version 2.6.22.6(gcc version 3.4.5) #1 Fri Jun 16 00:55:53 CST 2017” */
     printk(linux_banner);
    //系结构相关的内核初始化过程,处理uboot传递进来的atag参数 
    setup_arch(&command_line);
    /* 进程调度器初始化 */
    sched_init(); 
    /* 禁止内核抢占 */  
    preempt_disable();
    /* 打印Linux启动命令行参数 */   
    printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
    //初始化定时器Timer相关的数据结构  
    init_timers();  
    //软中断初始化    
    softirq_init(); 
    //初始化时钟源  
    timekeeping_init()
    //初始化控制台以显示printk的内容,在此之前调用的printk,只是把数据存到缓冲区里,  
    //只有在这个函数调用后,才会在控制台打印出内容  
    //该函数执行后可调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。  
    console_init(); 
    /* 虚拟文件系统的初始化 */
     vfs_caches_init_early();  
    //测试CPU的各种缺陷,记录检测到的缺陷,以便于内核的其他部分以后可以使用他们工作。  
    check_bugs();  
    //电源相关的初始化  
    //http://blogold.chinaunix.net/u/548/showart.php?id=377952  
    acpi_early_init(); /* before LAPIC and SMP init */ 
    //接着进入rest_init()创建init进程,创建根文件系统,启动应用程序
    rest_init(); 
}

static noinline void __init_refok rest_init(void)
{
    //启动init进程,然后进入kernel_init中启动应用程序
    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
}

static int __ref kernel_init(void *unused)
{
    if (execute_command) {   //解析并挂载根文件系统
        if (!run_init_process(execute_command))
            return 0;
        pr_err("Failed to execute %s.  Attempting defaults...\\n",execute_command);
    }
    if (!run_init_process("/sbin/init") ||  //执行应用程序
        !run_init_process("/etc/init") ||   //执行应用程序
        !run_init_process("/bin/init") ||   //执行应用程序
        !run_init_process("/bin/sh"))       //执行应用程序
        return 0
}

4. 小发现

在分析第二步的时候调用了head-common.S文件中的__mmap_switched函数,但是head-common.S却并没有被编译为.o文件???

验证步骤:

4.1 执行命令 grep -nr "head-common" *,发现head.S文件末尾有如下包含语句

#include "head-common.S"

4.2 执行反汇编命令将head.o反汇编

arm-linux-androideabi-objdump -d head.o > head.txt

4.3 打开反汇编文件head.txt可以发现其中有__mmap_switched函数的定义

0000005c <__mmap_switched>:
  5c:   e28f3044        add     r3, pc, #68     ; 0x44
  60:   e8b300f0        ldm     r3!, {r4, r5, r6, r7}
  64:   e1540005        cmp     r4, r5
  68:   11550006        cmpne   r5, r6
  6c:   1494b004        ldrne   fp, [r4], #4
  70:   1485b004        strne   fp, [r5], #4
  74:   1afffffb        bne     68 <__mmap_switched+0xc>
  78:   e3a0b000        mov     fp, #0
  7c:   e1560007        cmp     r6, r7
  80:   3486b004        strcc   fp, [r6], #4
  84:   3afffffc        bcc     7c <__mmap_switched+0x20>
  88:   e89320f0        ldm     r3, {r4, r5, r6, r7, sp}
  8c:   e5849000        str     r9, [r4]
  90:   e5851000        str     r1, [r5]
  94:   e5862000        str     r2, [r6]
  98:   e3570000        cmp     r7, #0
  9c:   13c04002        bicne   r4, r0, #2
  a0:   18870011        stmne   r7, {r0, r4}
  a4:   eafffffe        b       0 <start_kernel>
000000a8 <__mmap_switched_data>:

4.4 总结:head.o 中好像只包含了head-common.S文件中的部分函数

以上是关于Linux kernel启动过程的主要内容,如果未能解决你的问题,请参考以下文章

实验三:跟踪分析Linux内核的启动过程

内核启动流程

linux 重新编译kernel后不能正常启动

Linux系统启动过程及其修复过程简析

Linux Kernel系列一:开篇和Kernel启动概要

跟踪分析Linux内核的启动过程