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启动过程的主要内容,如果未能解决你的问题,请参考以下文章