一、.config讲解
1、通过make menuconfig配置内核命令生成的.config为内核的配置的文件,里面CONFIG_xxx_xxx=y表示该驱动直接编译到内核里面去,CONFIG_xxx_xxx=m/=xxx(某个数值)表示该驱动作为可加载模块编译到内核。
2、CONFIG_xxx_xxx=y、CONFIG_xxx_xxx=m/=xxx(某个数值)经过编译最终会在include/generated/autoconf.h生成#define CONFIG_ZLIB_INFLATE 1的宏定义,此autoconf.h会在代码里面调用;以CONFIG_ZLIB_INFLATE为例,可以通过grep “CONFIG_ ZLIB_INFLATE” * -nwR 命令搜索到。
注意:这里对y、m/xxx(某个数值)没有做区分;
3、在lib/zlib_inflate/Makefile文件所在的Makefile里面可以找到obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate.o,此处的CONFIG_ZLIB_INFLATE被别的文件定义;在include/config/auto.conf可以找到有CONFIG_ZLIB_INFLATE=y 该定义就会用在Makefile里面,并有y、m/xxx(某个数值)的区分;
说明:内核的子目录Makefile内容很简单,一般为obj-y += xxx.o、obj-m += yyy.o,-y直接编译到内核,编译后的文件类型为.o;-m为可记载模块,编译后的文件类型为.ko。
二、Makefile讲解
1、子目录Makefile内容很简单,一般为obj-y += xxx.o、obj-m += yyy.o,-y直接编译到内核,编译后的文件类型为.o;-m为可记载模块,编译后的文件类型为.ko;
2、make uImage,在顶层Makefile并未搜索到uImage,反而在arch/arm下的Makefile找到,说明该Makefile会被顶层的Makefile所包含。在顶层Makefile也能找到include
(
s
r
c
t
r
e
e
)
/
a
r
c
h
/
(srctree)/arch/
(srctree)/arch/(SRCARCH)/Makefile
3、在顶层Makefile搜索auto.conf能找到include/config/auto.conf,说明之前.config生产的auto.conf有包含到里面;
4、在arch/arm的Makefile搜索uImage可以看到,生成uImage依赖于vmlinux。在uboot学习可知,uImage由头+真正的内核构成,而vmlinux就是生成这个真正的内核,而vmlinux是在顶层的Makefile生产的;
BOOT_TARGETS = zImage Image xipImage bootpImage uImage
INSTALL_TARGETS = zinstall uinstall install
PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS)
$(BOOT_TARGETS): vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
$(INSTALL_TARGETS):
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
5、vmlinux是如何生产的呢?可参考这里
就这这些路径下面的相关文件
ifeq ($(KBUILD_EXTMOD),)
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \
$(init-) $(core-) $(drivers-) $(net-) $(libs-))))
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
export LDFLAGS_vmlinux
# used by scripts/pacmage/Makefile
export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools virt)
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
# Final link of vmlinux
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
quiet_cmd_link-vmlinux = LINK $@
# Include targets which we want to
# execute if the rest of the kernel build went well.
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
+$(call if_changed,link-vmlinux)
# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
经过分析内核的
第一个文件位于arch/arm/kernel/head.S
链接脚本位于arch/arm/kernel/vmlinux.lds
head.S 主要配置信息如下:
设置arm工作模式在SVC并关闭FIRQ IRQ
探测processor 类型
解析uboot传进来的atags
__fixup_smp __fixup_pv_table
__create_page_tables
执行由proc_info_list 的__cpu_flush;指定的函数。如__v7_setup
开启MMU
__mmap_switched
b start_kernel 位于init\main.c
三、start_kernel讲解
1、打印内核版本信息
pr_notice("%s", linux_banner);
2、如下函数处理uboot传进来的tag参数,如内存信息、命令行参数
setup_arch(&command_line);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
3、rest_init();
------kernel_init();// init=1号进程创建、内核驱动模块注册、启动默认控制台/dev/console
----------kernel_init_freeable();//功能:kernel_init_freeable主要功能是,等待内核线程创建完wait_for_completion(&kthreadd_done)、注册内核驱动模块do_basic_setup()、启动默认控制台/dev/console,标准输入输出,错误信息都在这个控制台里。
------------run_init_process();//启动uboot指定的init=xxx运用程序,如果uboot没有指定则,运行程序内部指定的
---------------do_basic_setup();//功能:初始化cpuset子系统、创建khelper线程队列、内核模块驱动注册
--------------------do_initcalls();//功能:初始化initcall_levels,完成编译进内核的驱动模块注册,详见这里
至此,内核的初始化结束,正式进入了用户空间的初始化过程,在kernel_init线程中调用的do_basic_setup()函数会去初始化设备驱动,完成其他驱动程序(直接编译进内核的模块)的初始化。内核中大部分的启动数据输出(都是各设备的驱动模块输出)都是这里产生的。是我们驱动工程师需要重点关注的函数。
用户空间的初始化结束,就会进入根文件系统,运行init第一个运用程序。