2.3 (完结)分析内核启动——分析内核的第一个文件head.S和第一个c文件

我们在 本栏目第一节介绍到的start.S分析到了linux进入了theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

这里我们猜测一下内核启动要做什么事情:

1.处理u-boot传入的参数

2.最终目的:运行应用程序(应用程序在根文件系统,所以后期我们要挂载根文件系统)

 

我们从分析第一个文件head.S入手

linux-2.6.22.6\arch\arm\kernel\head.S

1.我们一开始看到__lookup_processor_type(得到机器Id)和__lookup_machine_type(得到单板类型)

_lookup_processor_type:先确认我这个内核能不能支持你这个处理器,不能就error

__lookup_machine_type:由上面u-boot传进的参数bi_arch_number(单板类型)确认我的内核是否支持你这个单板

 

 

3.进去__lookup_machine_type

如果看汇编的话,会知道r1就是u-boot传进来的机器ID=362

因为这个时候还没有启动MMU(助于构建虚拟地址和物理地址的架构),所以现在的r3=真实的地址

__arch_info_begin和__arch_info_end可以在链接脚本中找到(vmlinux.lds)

*(.arch.info.init)表示架构相关的初始化信息,开始地址和结束地址是__arch_info_begin和__arch_info_end

 

现在我们要知道arch.info.init是在哪里被定义的(/include/asm-arm/arch.h)

这里有个宏MACHINE_START,用sci全局搜索,会发现有很多arch/arm/mach-单板都有使用到这个宏

我们打开我们开发板对应的文件linux-2.6.22.6\arch\arm\mach-s3c2440\Mach-smdk2440.c

这里我们看到这个结构体和上面的相识,猜测这个结构体被强制被设置一个属性,把它的段设置为.arch.info.init,然后被整合进vmlinux.lds中的*(.arch.info.init)

这个结构体被定义在

这样,这个有关于2440的结构体被编译进vmlinux.lds,内核就支持这个单板

 

4.跳出__lookup_machine_type回到head.S继续读

建立页表

为什么要建立页表,看到vmlinux.lds中

我们在lds中的是虚拟地址,但是我们的真实地址是0x30000000开始的,所以我们要建立一个页表,启动MMU

下面就有说到enable MMU,使能MMU后,就会跳到__switch_data

进去_switch_data 往下看,发现

start_kernel是内核的第一个c函数,所以head.S工作到这里交给了start_kernel

我们回忆head.S做了什么:

        1.判断是否支持这个CPU(机器ID)

        2.判断是否支持这个单板

        3.建立页表,启动MMU

        4.跳到start_kernel

但是我们想到u-boot传进的参数中机器ID被使用了,但是启动参数还没有分析,于是我们还要进去

start_kernel分析

这里我们叫head.S的工作为内核引导阶段

 

5.跳到start_kernel(内核的第一个c函数)

这里有各种初始化,中断,打印(打印内核版本信息等)等

注:c语言常用标准输出printf,但是内核是用系统调用printk

 

就是内核来处理u-boot传进的启动参数)

图为u-boot传进的启动参数

我们进去setup_arch(&command_line);看到

boot_params不就是theKernel (0, bd->bi_arch_number, bd->bi_boot_params);中的bi_boot_params,这里传进去的0x30000100

再下面是一些tag的处理(u-boot传进的tag)

这里有一个

parse_cmdline(cmdline_p, from);  ——(from在开头=default_command_line默认命令参数)

这函数的功能是解析命令行参数,对应的是u-boot参数中的

在我自己的开发板是(挂载根文件系统,这里可以让root=/dev/mtdblockX,也可以root=/dev/nfs用于NFS挂载共享文件根文件系统)——这里是用于让内核知道根文件系统的存放的路径,后面再分析

 

这样u-boot的启动参数就处理完了

现在要到我们的终极目的——应用程序(首先要挂载根文件系统)

 

6.寻找挂载根文件系统的函数

我们初始化了一系列东西,继续看下去,整理出如下关系,找到了mout_root()——挂载根文件系统

在linux-2.6.22.6\init\Do_mounts.c中

 

 

假设我们挂载好了根文件系统,按道理可以执行应用程序了

退回上一个文件,看到init_post();

init_post()做了什么事:

(1)sys_open((const char __user *) "/dev/console", O_RDWR, 0)——打开控制台

(2)——执行应用程序 

     run_init_process("/sbin/init");

    run_init_process("/etc/init");

    run_init_process("/bin/init");

    run_init_process("/bin/sh");

 

但是如果我们没有挂载根文件系统,怎么办

我们想到上面我们说的参数bootargs,是用来让内核知道根文件系统存放的位置

那么内核源码中怎么找到这个bootargs

我们在图片看到有一个ROOT_DEV,那这个ROOT_DEV等于谁

 

看到saved_root_name,我们查找它的定义

 

我们内核在分析bootargs,发现了root=/dev/xxx,然后调用root_dev_setup把这个root=/dev/xxx保存到saved_root_name

 

在继续分析找到__serup的结构体,这个结构体把里面的属性定义为.init.setup(在vmlinux有定义)

 

现在我们知道了这个mout_root()是由bootargs决定的

但是我们想linux的Flash是没有分区的,显然是代码中写死的

在linux-2.6.22.6\arch\arm\plat-s3c24xx\Common-smdk.c有定义,我们可以在里面修改

于是,内核启动的第二阶段:

 

总结内核启动:

1.head.s

        1.判断是否支持这个CPU(机器ID)

        2.判断是否支持这个单板

        3.建立页表,启动MMU

        4.跳到start_kernel

2.解析u-boot传进的参数

3.挂载根文件系统、启动init进程

4.分区

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值