开发板上电运行的第一段程序是u-boot,为了使用应用程序,u-boot要启动内核,并且由内核挂接根文件系统,下面以linux-2.6.22.6为例,分析内核的启动过程
首先从内核的配置过程开始,从根目录的说明文件可以知道
配置命令:make menuconfig
编译命令:make uImage
在配置过程会根据用户配置在根目录生成一个.config文件,里面描述了各种配置项是否编译、编译到内核还是编译为一个模块,格式如下:
CONFIG_XXX=y
CONFIG_XXX=m
=y表示将XXX编进内核
=m表示将XXX作为模块编译,使用时动态加载
以CONFIG_DM9000这个配置项为例,命令行中输入
grep “CONFIG_DM9000” * -nwR
可以发现CONFIG_DM9000存在于四个位置:
1、C源码
2、子目录Makefile
3、include/config/auto.conf
4、include/linux/autoconf.h
编译内核时,根据.config自动生成autoconf.h,autoconf.h中有一系列的宏,若定义为1,表示该配置项会被编译(但这里不能区分编译为模块还是编进内核),同时这些宏会提供给C源码使用
auto.conf内容与.config相似,在编译时被顶层Makefile包含,然后用于各级子目录Makefile,区分配置项编译为模块还是编进内核,在子目录Makefile中内容格式如下:
obj-y += xxx.o
obj-m += xxx.o
其中:
-y表示编进内核
-m表示编译为模块
如果要将两个配置项编入一个模块,可以这么写:
obj-m += c.o
c-objs := a.o b.o
最后a.c、b.o编译为a.o、b.o,a.o和b.o链接为c.ko
搜索编译对象 uImage,可以发现它位于arch/arm/Makefile中
zImage Image xipImage bootImage uImage : vmlinux
uImage依赖于vmlinux,同时这个文件中还有一个以后会用到的定义
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
在顶层目录Makefile中有以下定义
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
#相当于init-y = init/built-in.o
drivers-y := drivers/ sound/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
相当于drivers-y = drivers/built-in.o sound/built-in.o
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y))
相当于net-y = net/built-in.o
libs-y := lib/
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
相当于net-y = lib/lib.a lib/built-in.o
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
相当于net-y = net/built-in.o kernel/built-in.o mm/built-in.o fs/ built-in.o ipc/ built-in.o security/ built-in.o crypto/built-in.o block/built-in.o
上面这些东西是vmlinux的依赖:
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
vmlinux : $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
$(call if_changed_rule,vmlinux__)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
$(Q)rm -f .old_version
也就是说,uImage最终依赖于head-y、init-y、core-y、libs-y等等
通过一系列替换可以还原出链接命令,也可以通过命令行输入下面命令,在最后找到链接命令
make uImage V=1
V=1选项:详细列出命令
链接命令的一部分:
arm-linux-ld -EL -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o ……
从链接命令可以看到
1、链接脚本是arch/arm/kernel/vmlinux.lds(链接脚本是根据vmlinux.lds.S生成的)
2、第一个链接的文件是arch/arm/kernel/head.o(源文件arch/arm/kernel/head.S)
从链接脚本可以知道,链接地址为:0xC0000000 + 0x0000800