内核配置裁剪及启动流程

Posted 今天 天气真好

tags:

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

文章目录

一、内核分析之编译初体验

1.解压缩

2.配置
内核的配置有三种方法
(1)直接执行make menuconfig,从头到尾每一条都自己配置,有成千上万的配置项,比较复杂

(2)使用默认的配置,在这个基础上进行修改
先在内核目录下进行查找:find -name "*defconfig*"

进入对应的arch/arm/configs目录,可以看到:

那么就可以直接执行make s3c2410_defconfig进行配置
执行完之后会提示所有的配置项都被写到.config里面,因此这条命令的执行结果是保存在.config里面的
后面再执行make menuconfig,他肯定会去读取这个.config,然后出现一个菜单,可以在这个菜单上修改配置项

注意执行make menuconfig之前,需要安装ncrses,他是一个提供功能键定义、屏幕绘制以及基于文本终端的图形互动功能的动态库。

sudo apt-get install bc
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install zlib1g:i386
sudo apt-get install libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5

之后执行make menuconfig就可以出现如下界面

(3)使用厂家提供的配置文件
方法就是直接将厂家的config文件复制为.config文件,然后make menuconfig进行配置

输入Y,就会被编译进内核里面
输入N,就不编译
输入M,就被做为一个模块,也就是一个驱动程序
输入*,build-in,表示编译进内核
输入M,表示被作为一个模块

3.编译
编译直接执行make命令即可
或者当我们想生成uImage,uImage就是在真正的内核前面加一个头部(uboot支持的内核有一个头部)
如果想编译一个内核给uboot用,那就执行make uImage

将编译好的UImage烧到单板上
菜单项里面的K是烧写UImage,然后打开dnw,进行发送
如下:

在源码目录下看到cmd_menu.c中命令K
他是先用usb命令来接受dnw发送的数据,收到之后就去擦除内核分区,然后再把30000000位置的东西烧写filesize大小到内核分区

4.启动内核
倒数计时之后,就会去执行bootcmd这个环境变量里面定义的两条命令,这两条命令一个是从flash中读出内核,一个是启动内核
烧写完之后可以使用b命令来进行启动,如下:

当然,我们这里没有烧写文件系统,所以最终会卡着

二、内核分析之配置

通过前面我们知道配置最终生成的是.config文件,里面有很多配置项,以DM9000为例,进行搜索可以看到:

y也就是会被编译进内核里面,继续搜索CONFIG_DM9000,可以看到如下:

1.C源码里面用到了CONFIG_DM9000,也就是一个宏,宏只能在头文件或者C文件中定义,因此是在include/linux/autoconf.h定义的

2.driver/net/Makefile,也就是子目录的Makefile里面有
对于子目录的Makefile,格式比较简单,

obj-y += xxx.o	表示xxx.c这个文件最终会被编译进内核里面
obj-m += yyy.o	表示yyy.c这个文件最终会被编译成一个可加载的模块yyy.ko

3.include/config/auto.config
对于子目录下的CONFIG_DM9000则是在include/config/auto.config中进行定义
同样,auto.config也是自动生成的,内容来源于.config
而include/config/auto.config则是被顶层的Makefile包含

4.include/generated/autoconf.h 自动生成的(make机制),里面的内容来源于.config,打开autoconf.h看时都是被定义为1的

因此我们执行make uImage的时候,发生了如下事情:
1、.config---->auto.config,这个文件是被源代码使用的
2、.config---->autoconf.h,这个文件是被顶层Makefile包含的,子目录下的Makefile来使用

三、内核分析之Makefile

首先我们从最简单的角度总结Makefile的作用:
(1)决定编译哪些文件
(2)怎么编译这些文件
(3)怎么链接这些文件,最重要的是他们的顺序如何

Linux内核Makefile文件分类如下:

1.分析子目录下的Makefile,以driver/char目录下的Makefile进行分析为例,可以看到:

如果CONFIG_SGI_SNSC这个变量在配置文件中被定义为y,则snsc.c snsc_event.c会被编译为snsc.o snsc_event.o,最后链接到内核里去

如果被定义为m,则snsc.c snsc_event.c会被编译为模块.ko文件

那么对于a.c和b.c两个文件
被编译进内核很简单,obj-y += a.o b.o
如果这两个要组成一个模块,则是:

obj-m += ab.o
ab-objs := a.o b.o

最终的结果就是a.c–>a.o,b.c–>b.o,然后这两者链接为ab.ko

注意到我们前面编译内核的时候使用的命令时make uImage。搜索发现uImage的时候发现uImage是在arch/arm/Makefile中定义
2.架构相关的Makefile
因为uImage是在这里定义,最终也会生成uImage,那么可以知道架构相关的Makefile肯定会被包含进顶层的Makefile


uImage依赖于vmlinux,uImage = 头部 + 真正的内核
因此我们制作uImage的时候要先编译出真正的内核,真正的内核就是vmlinux
vmlinux的依赖在顶层Makefile里面

3.顶层Makefile
在顶层Makefile里面可以看到是包含了架构相关的Makefile的

同时还会包含auto.config配置文件

分析vmlinux可以看到:


具体这里不详细介绍,可以分别进行搜索找出原材料。

为了更方便知道结果,可以指向make uImage V=1命令,V=1是将命令详细的列出来,执行这个命令来看最终是做了什么,执行了什么。

总结分析Makefile最终知道:
1.第一个文件:arch/arm/kernel/head.s
2.链接脚本:arch/arm/kernel/vmlinux.lds

四、内核分析之启动过程

从前面我们分析u-boot启动过程可以知道,先是设置参数,然后启动内核。怎么启动?执行thekernel这个函数。
theKernel就是内核的入口地址
第一个参数是0
第二个参数是机器ID
第三个参数是那些参数存放的地址

那么我们的内核首先就会去处理u-boot传过来的参数

注意内核的最终目的是运行应用程序
对于pc机来说应用程序在C盘,D盘。对于Linux来说是在根文件系统,因此需要挂接根文件系统,然后启动应用程序。

知道最开始和最终目的,下面来分析中间做了什么

补充一点:对于最终生成的很大的内核,可以进行压缩,最终得到一个很小的内核,在压缩后的内核前面加上一段自解压代码,最后内核就从自解压代码这里开始运行,自解压代码的作用就是将压缩后的内核解压出来,然后再去执行解压缩后的内核。

首先分析arch/arm/kernel/head.s文件
100ask/uboot-2.6/linux-2.6.22.6/arch/arm/kernel/head.s
所做的工作:
1.判断是否支持这个CPU
2.判断是否支持这个单板,也就是启动内核时传入的R1(机器ID)
3.建立页表
4.使能mmu

5.跳转执行start_kernel,这是kernel的第一个C函数,在这个C函数里面去处理启动参数

里面主要是进行一些初始化工作,前面我们已经处理了传进来的机器ID,但是启动参数还没有处理,这个函数里面的下面这两个函数就是用来处理传入的参数的

如果uboot没有传入命令行参数(也就是bootargs这个环境变量对应的参数),那就回使用默认的命令行参数default_command_line

同时也会调用rest_init函数,这个函数里面会去创建一个内核线程,想当于调用kernel_init这个函数

u-boot传过来的参数如下:

内核启动流程大致调用关系:

start_kernel
    setup_arch	//解析uboot传入的启动参数
    setup_command_line  //解析uboot传入的启动参数
    parse_early_param
    	do_early_param	//从_setup_start到_setup_end,调用early函数
    unknown_bootoption
    	obsolote_checksetup	//从_setup_start到_setup_end,调用非early函数
	rest_init
		kernel_init
			prepare_namespace
				mount_root	//挂接根文件系统
			init_post
				//执行应用程序

以命令行参数为例来进行分析:
进入u-boot可以看到:

分析代码可以发现:命令行参数最开始是被保存在某个字符串里面,对于不同的"root=",对应不同的处理函数root_dev_setup,这两者被定义在一个结构体里面,这些很多的结构体被链接脚本放到一块,在arch/arm/kernel/vmlinux.lds里面

*(.init.setup),调用的时候就从start一直搜索到end,具体怎么调用在前面内核调用流程里面

在flash里面没有分区表,但是从命令行参数得知有root = /dev/mtdblock3,那么整个分区怎么体现?

—>在代码里面写死

以上是关于内核配置裁剪及启动流程的主要内容,如果未能解决你的问题,请参考以下文章

内核配置裁剪及启动流程

Centos启动流程及grub legacy

centos6系统裁剪

配置Linux Kernel时make menuconfig执行流程分析

OpenHarmonyLiteOS-M内核启动流程

OpenHarmonyLiteOS-M内核启动流程