Hi, everyone! I'm CrazyCatJack. Recently I am learning the configuration, compilation and Makefile of the Linux kernel. Today I will summarize the learning results and share them with you ^_^
1. Decompress and patch
The first thing is to decompress the Linux kernel you obtained. Here I am using the linux.2.22.6 version of the kernel. Use the command line under Linux to decompress the kernel through tar xjf linux.2.22.6.tar.bz2. Then, if you need to patch this kernel, use the patch command: patch -px 7995132709046759b8938669c0b76682 Directly make menuconfig. This is the most troublesome method, and all configurations need to be done by you.
2> Modify the default configuration yourself, that is, modify the defconfig file. Use find -name "*defconfig*" to find the default configuration file for your architecture. I found the default configuration file of my board in arch/arm/configs. Execute the defconfig file: make XXX_defconfig. XXX is the specific board model you are using. After performing this operation, the results are saved in the .config file. Then execute the make menuconfig command. The configuration at this time is just a slight modification to the default configuration.
3>Use the manufacturer's configuration file. It is easiest if your hardware has a config file provided by the manufacturer. Directly cp XXX.config. Then execute make menuconfig.
Here I will tell you about the kernel configuration in detail. Linux kernel configuration is to generate .config files. Because you need to use the .config file to generate other related configuration files during compilation. Most of our configuration items are, for example, CONFIG_XXXDRIVER, where XXXDRIVER refers to various drivers. We need to tell the kernel whether these drivers are compiled into the kernel or compiled into modules. By looking for CONFIG_XXXDRIVER, we can find that it appears in four places:
1>C source code
2>Subdirectory Makefile: drivers/XXX/Makefile
3>include/config/auto.conf
4> include/linux/autoconf.h
Here is the first explanation: the .config file generates include/config/auto.conf and include/linux/autoconf.h when compiling the kernel (make uImage). By looking at the C source code, we found that CONFIG_XXXDRIVER is a macro definition, which is equal to a constant. The macro definition CONFIG_XXXDRIVER in include/linux/autoconf.h is a constant, which may be 0 or 1. So now there is a question, is CONFIG_XXXDRIVER compiled into the kernel or compiled into a module? This cannot be distinguished in C language. Where is this distinction reflected? This distinction is reflected in the Makefile file in the subdirectory. In the Makefile of the subdirectory, if there is obj -y += XXX.o, it means that XXX.c is compiled into the kernel; obj -m +=XXX.o means that XXX is compiled into a module, which is XXX.ko. The include/config/auto.conf file assigns a value to CONFIG_XXXDRIVER. When it is y, it means compiled into the kernel, and when it is m, it means compiled into an independent module.
#这里是include/config/auto.conf的部分内容 # Automatically generated make config: don't edit # Linux kernel version: 2.6.22.6 # Sun Nov 27 18:34:38 2016 # CONFIG_CPU_S3C244X=y CONFIG_CPU_COPY_V4WB=y CONFIG_CRYPTO_CBC=y CONFIG_CPU_S3C2410_DMA=y CONFIG_CRYPTO_ECB=m CONFIG_SMDK2440_CPU2440=y
#这里是drivers/i2c/Makefile # Makefile for the i2c core. # obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-y += busses/ chips/ algos/ ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUG endif
3. Compile the kernel
Through the above description, we can know that under each driver, there is a Makefile file. To define whether this driver is compiled into the kernel or compiled into a module. A little mention here. Above we talked about how a single file in the Makefile is compiled into the kernel and compiled into a module. But how to write it if there are more than two files? Here is an example: obj -y += a.o b.o means compiling a.c and b.c into the kernel.
obj -m += ab.o
ab -objs := a.o b.o
can mean compiling a.c and b.c into a module. The process is that a.c generates a.o, and b.c generates b.o. a.o and b.o together generate ab.ko. In the previous code analysis of uboot startup kernel, we mentioned that the uImage generated by compiling the kernel is composed of two parts: header + Linux kernel. This header contains a lot of initialization parameter information, such as the kernel's loading address and entry address. When compiling the kernel, we can directly execute make uImage. So how is uImage defined in the file? How to generate uImage?
First, by searching the Makefile, we found that uImage is in arch/arm/Makefile, and the Makefile in this architecture directory is included in the Makefile in the top-level directory. In this way, when we execute make uImage, the Makefile in the top directory can call the Makefile in the architecture subdirectory to compile the kernel and generate uImage.
顶层目录下Makefile中相关命令: include $(srctree)/arch/$(ARCH)/Makefile 架构目录下Makefile相关命令: zImage Image xipImage bootpImage uImage: vmlinux
This is what I just said, the top-level Makefile calls the Makefile in the architecture directory, and the Makefile in the architecture directory generates uImage, and it depends on the vmlinux file. Next we will start to explain how to generate vmlinux files. In the top-level Makefile, we find most of the commands for generating vmlinux.
顶层目录Makefile: init-y := init/ init-y := $(patsubst %/, %/built-in.o, $(init-y)) core-y := usr/ core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ core-y := $(patsubst %/, %/built-in.o, $(core-y)) libs-y := lib/ libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) libs-y := $(libs-y1) $(libs-y2) drivers-y := drivers/ sound/ drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) net-y := net/ net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE 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 export KBUILD_VMLINUX_OBJS := $(vmlinux-all) 架构目录Makefile: zImage Image xipImage bootpImage uImage: vmlinux head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
我已经把顶层目录和架构目录下生成vmlinux的命令摘选出来。首先,我们看要想生成vmlinux,需要vmlinux-lds文件、vmlinux-init文件、vmlinux-main文件。其中,vmlinux-lds是链接脚本文件,定义了代码段,数据段的存放位置。这里我们接着往下看,vmlinux-init需要head-y和init-y,通过查看两个Makefile,我们可以得到经过转换后的结果:
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o init-y := $(patsubst %/, %/built-in.o, $(init-y)) = init/built-in.o core-y := $(patsubst %/, %/built-in.o, $(core-y)) = usr/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 libs-y := $(libs-y1) $(libs-y2) =lib/lib.a lib/built-in.o drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) = drivers/built-in.o sound/built-in.o net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
现在已经分析了内核编译的全部过程。那怎样知道我们分析的到底对不对,通过实际执行make uImage我们就可以看到执行过程。这是执行make uImage过程中的部分相关命令:
arm-linux-ld -EL -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o
可以看到,首先目标要生成vmlinux,然后是链接脚本为vmlinux.lds。开始生成第一个文件:head.o,第二个文件:init_task.o。这和我们分析的完全一致。接下来以此类推,和我们分析的相同,也就是说我们分析的是正确的。
SECTIONS { . = (0xc0000000) + 0x00008000; .text.head : { _stext = .; _sinittext = .; *(.text.head) } .init : { /* Init code and data */ *(.init.text) _einittext = .; __proc_info_begin = .; *(.proc.info.init) __proc_info_end = .; __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; __tagtable_begin = .; *(.taglist.init) __tagtable_end = .; . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __early_begin = .; *(.early_param.init) __early_end = .; __initcall_start = .;
这是链接脚本vmlinux.lds中的部分内容。首先定义了虚拟地址:(0xc0000000) + 0x00008000。 然后是首先执行头部文件,这与我们分析的完全一致。代码段,初始化代码段等等。
这就是Linux内核的从配置到编译的全部分析了^_^