linux内核编译的实质

 广工
如果有意转载请标明来源:http://blog.csdn.net/shiyi_2012/article/details/7396785
当你解压一个linux内核源代码的时候,里面没有vmlinux文件的。但你对linux编译为某个平台,无论是ARM还是PC都行,都可以发现,你编译后就根目录比原来多了一个文件vmlinux,这就是内核镜像文件。他是如何产生的呢?
大家可能知道内核就是通过Makefile把整个内核里的文件联系起来,编译起来的。今天就说说如何串起来的。
我看了下,顶层Makefile有1500多行,一行行搞懂,不可能的吧,你也不会像这么做。其实把关键的代码拿出来,搞懂就基本知道编译内核其实内核是如何运作的啦。
...
include $(srctree)/arch/$(ARCH)/Makefile
...
init-y := init/
drivers-y := drivers/ sound/
net-y := net/
libs-y := lib/
core-y := usr/
....
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/



对于init-y\drivers-y\net-y\libs-y\core-y\是编译过程的灵魂,他是告知顶层Makefile那些根目录需要编译。其实除了include、Documentation、script这三个目录之外,其他目录都逃不了要编译,大家可以看看是不是。知道那些目录要编译后就会告知编译器进入对应的目录进行编译(根据对应目录下Makefile),编译OK之后,再连接为一个目标文件。其实结果就是把“:=”右边对应目录下的文件编译好,连接成build-in.o文件(libs-y所列的可能特殊点,很可能产生lib.a文件)例如:编译好的时候:init-y对应的就是init/build-in.o。
而include $(srctree)/arch/$(ARCH)/Makefile这句本质和上面一样,其实就是吧$(srctree)/arch/$(ARCH)目录也纳入进内核编译。如果你在编译前,配置顶层Makefile的ARCH=arm,就是无意间就把arch/arm目录纳入内核编译啦。简而言之,顶层Makefile指定编译那些目录,好了现在编译器就要进入对应的目录进行编译啦。
 我们就认为大家配置ARM平台的。那么arch/arm就是编译的其中一个字目录啦,那我们就去哪个目录看看。
当然进入arch/arm目录后编译器就要依靠Makefile编译。
 /arch/arm/Makefile:
...
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += $(MACHINE)
core-$(CONFIG_ARCH_S3C2410)+= arch/arm/mach-s3c2400/
core-$(CONFIG_ARCH_S3C2410)+= arch/arm/mach-s3c2412/
core-$(CONFIG_ARCH_S3C2410)+= arch/arm/mach-s3c2440/
core-$(CONFIG_ARCH_S3C2410)+= arch/arm/mach-s3c2442/
core-$(CONFIG_ARCH_S3C2410)+= arch/arm/mach-s3c2443/
core-$(CONFIG_FPE_NWFPE)+= arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE)+= $(FASTFPE_OBJ)
core-$(CONFIG_VFP)+= arch/arm/vfp/

  这个也很好说,跟上面是不是差不多。首先多了一个head-y类,最重要的是多了很多个$(****),啥意思,这个就是选项问题啦,其实大家编译之前一定得先make menuconfig配置一下的,先产生个.config配置文件。而$(***)就是根据你的.config是否有配该选项生效来决定用y或者n或者m代替$(***)。其中$(MMUEXT)特殊一点,但本质一样,就是根据.config是否有配置MMU处理器支持来决定是nommu或者mmu代替$(MMUEXT)。
还有一点说明就是:大家看看head-y后面不是目录而是文件,就是他是直接由arch/arm/kernel/head$(MMUEXT).S,arch/arm/kernel/init_task.S编译而来。而对于剩下的都是扩大core-y所要编译的目录啦。
好啦,还是以arch/arm子目录为例,依据上面的解析,我们又得还要进一步编译arch/arm的子目录,现在进入其子目录看看如何编译文件。
其实对于文件编译都是以obj-$(***)格式,这不是和上面有点类似$(***)格式一样吗?也正是$(***)格式,成就linux很优秀的地方,通过你配置的.config文件是否有配置该选项来决定$(***)=y或者n或者m,进而决定是否决定吧文件编入内核,还是编译成模块。。。举个例子吧:
在arch/arm/kernel/Makefile:
obj-$(CONFIG_ISA_DMA_API)+=dma.o
如果你有在配置时候如果在有关ISA_DMA_API那个选项有选上,那么在.config里就会有
CONFIG_ISA_DMA_API=y;那么上面的obj-$(CONFIG_ISA_DMA_API)+=dma.o就变为obj-y +=dma.o那么就会编译dma.c(或者dma.s)啦。


这里有一点提醒一下就是obj-$(***)后面也可以加上目录,如obj-$(CONFIG_JFFS2_FS)+=jiffs2/。那么编译原理和上面的core-y等是一样,生成build-in.o或则模块。


到这里,大家应该清楚很多,就是顶层Makefile决定编译根目录下那些子目录,而那些子目录对应的Makefile决定又编译其子目录或则文件,最后就是根据.config来编译文件,其实也有一些是整个目录要根据.config来决定是否编译的,前面就有个例子啦。
经过这几步之后,编译就完成了,接下来就是怎么连接成一个目标文件vmlinux的啦。当然回到顶层目录Makefile:
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)
...
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

...
对于前七个,举一个就行其他类推。有前面知道,init-y对应的就是/init/build-in.o,所以init-y:= $(patsubst %/, %/built-in.o, $(init-y))其实就是把init-y变为/init/build-in.o。
而对于vmlinux-init := $(head-y) $(init-y)和vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)以及vmlinux-all  := $(vmlinux-init) $(vmlinux-main)就是把所有编译完的目标文件 $(head-y) $(init-y) $(core-y) $(libs-y) $(drivers-y) $(net-y)构成了“终极文件:vmlinux,好了过程就是这样。


这里还温馨提醒一下:
有人会问Makefile如何会懂得,依靠.config文件来编译,它又不是人。其实在Makefile里面有一句:
-include include/linux/autoconf.h这个文件就是在配置完之后根据产生的.config在对应目录产生的,里面的本质是一样的。



你可能感兴趣的:(linux内核编译的实质)