linux内核源码编译过程分析之Kconfig,Makefile

关注”技术简说“,带你由浅入深学习linux内核源码。linux内核开发100讲免费教程,每天晚上九点准时更新,敬请收看。进我主页点”视频“栏目即可观看。

在之前的课程里,当我们在编译linux内核源码的时候,不知道大家会不会有一些疑问:

1.linux内核源码那么多(大概800M),编译的时候它(编译系统)怎么知道应该要编译哪些文件呢?

2.怎样保证源码的编译顺序?比如,先编译A模块,再编译B模块?

3.怎么样把这些编译出来的一个一个的目标文件,最终形成一个内核镜像文件?

所有这些,都是通过Makefile来完成的。

本文所用内核源码为 linux-4.9.229,ARCH=x86

linux内核源码里的Makefile分为三层:

  • 顶层Makefile(源码根目录下)
  • 体系相关的Makefile(arch/$(ARCH)/Makefile)
  • 各级子目录下面的Makefile。
linux内核源码编译过程分析之Kconfig,Makefile

那我们就先来看顶层Makefile,先摘几段关键代码:

<code> 262 SRCARCH     := 

$(ARCH)

... 546

include

arch/

$(SRCARCH)

/Makefile/<code>

这样体系相关的Makefile就被include进来了。

而下面的代码则间接的把各级子目录下面的Makefile包含进来了。

<code> 

570

init-y := init/

571

drivers-y := drivers/ sound/ firmware/

572

net-y := net/

573

libs-y := lib/

574

core-y := usr/

575

virt-y := virt//<code>

这样顶层的Makefile就把体系相关的Makefile和各个子目录的Makefile都包含进来了。

那接下来我们深入到某个具体的子目录的Makefile,看它又是如何编译这个子目录下面的源代码的呢?

linux内核源码编译过程分析之Kconfig,Makefile

具体到某个源码目录中,编译系统是通过obj-y, obj-m,obj-n和lib-m来组织的。

  • obj-y用来定义哪些源文件被编进内核。在当前目录,这些被编译到内核里的.o文件,会被链接成一个built.o文件。大家可以看看,等内核编译完成后,是不是每个子目录下,都有一个built-in.o文件呢?
  • obj-m用来定义哪些文件被编译成可加载模块,也就是驱动文件。
  • obj-n表示当前源文件既不被编译到内核,也不会编译成驱动,也就是不做处理。
  • lib-y用来定义哪些文件被编译成库文件。
  • obj-y, obj-m,lib-y,lib-m还可以指定要包含的下一级目录。这样层层包含,保证能够编译到所有的内核源代码。

我以drivers/tty子目录为例,其Makefile的摘要如下:

<code>obj-$(CONFIG_TTY)       += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
                   tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o
obj-$(CONFIG_HVC_DRIVER)    += hvc//<code>

以上表示:

如果通过make menuconfig配置了CONFIG_TTY为y,那么 tty_io.c n_tty.c tty_ioctl.c tty_ldisc.c

tty_buffer.c tty_port.c tty_mutex.c tty_ldsem.c 这些c文件就会被编译到内核镜像里;

如果通过make menuconfig配置了CONFIG_HVC_DRIVER,那么还会进入到hvc子目录进一步执行它的Makefile。

所以,当某个子目录下的makefile执行一遍以后,所有的需要编译到内核里的.o文件就都罗列在obj-y这个变量里了,而所有需要编译成驱动的.o文件就都罗列在obj-m里了。

另外,某个子目录下obj-y变量里的所有 .o文件会被链接成built-in.o文件,并最终被链接到内核镜像文件。

下面代码是drivers/tty/built-in.o 生成的过程。

<code>ld -m elf_x86_64  -z 

max

-page-size=

0x200000

-r -o drivers/tty/built-

in

.o drivers/tty/tty_io.o drivers/tty/n_tty.o drivers/tty/tty_ioctl.o drivers/tty/tty_ldisc.o drivers/tty/tty_buffer.o drivers/tty/tty_port.o drivers/tty/tty_mutex.o drivers/tty/tty_ldsem.o drivers/tty/pty.o drivers/tty/tty_audit.o drivers/tty/sysrq.o drivers/tty/vt/built-

in

.o drivers/tty/serial/built-

in

.o drivers/tty/ipwireless/built-

in

.o /<code>
linux内核源码编译过程分析之Kconfig,Makefile

我们来看看内核镜像是如何生成的。

还是看顶层的Makefile:

<code>core-y      += kernel/ certs/ 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) $(virt-y))) vmlinux-alldirs

:

= $(sort $(vmlinux-dirs) $(patsubst

%/,%,$(filter %/

, \ $(init-) $(core-) $(drivers-) $(net-) $(libs-) $(virt-)))) 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) virt-y

:

= $(patsubst

%/, %/

built-

in

.o, $(virt-y)) export KBUILD_ALLDIRS

:

= $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation

include

samples>

patsubst和filter是Makefile的内置函数,说明如下:

$(patsubst %/, %/built-in.o, $(init-y))

寻找$(init-y)中符合格式“%/”的字符串,用“%/built-in.o”替换它们。在之前我们有定义init-y的值为init/,所以,经过这么转换,init-y的值就变成了init/built-init.o了。其他同理。

而 $(filter pattern..., text)表示返回在“text”中由空格隔开且匹配格式“pattern...”的字符串,去掉不符合格式“pattern...”的字符串。例如:

<code>$(

filter

%.

c

%.s, foo.

c

bar.

c

jin.s xin.h) 结果为:foo.

c

bar.

c

jin.s/<code>

根据以上的分析,最终vmlinux-dirs的值为:

<code>

init

usr arch/x86 kernel certs mm fs ipc security crypto block drivers sound firmware \ arch/x86/pci arch/x86/power arch/x86/video arch/x86/ras net lib arch/x86/lib virt/<code>

最终vmlinux-alldirs的值为:

<code>  

arch/x86/lib arch/x86/math-emu arch/x86/oprofile arch/x86/pci \ arch/x86/power arch/x86/ras arch/x86/video block certs crypto drivers firmware \ fs init ipc kernel lib mm net security sound usr virt

/<code>

linux内核代码的链接脚本为:arch/$(SRCARCH)/kernel/vmlinux.lds。 对于x86,则位于
arch/x86/kernel/vmlinux.lds, 它是由
arch/x86/kernel/vmlinux.lds.S文件生成的。

在编译内核的时候指定:make V=1,则可以打印出具体的编译细节,最终编译出linux内核镜像的编译过程为:

<code>

ld

-m elf_x86_64 -z max-page-size=0x200000 --build-id -o vmlinux \ -T ./arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o \

arch/x86/kernel/ebda.o \ arch/x86/kernel/platform-quirks.o init/built-in.o \ --start-group usr/built-in.o arch/x86/built-in.o \ kernel/built-in.o certs/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 lib/lib.a \ arch/x86/lib/lib.a lib/built-in.o arch/x86/lib/built-in.o \ drivers/built-in.o sound/built-in.o firmware/built-in.o \ arch/x86/pci/built-in.o arch/x86/power/built-in.o \ arch/x86/video/built-in.o arch/x86/ras/built-in.o \ net/built-in.o virt/built-in.o --end-group .tmp_kallsyms2.o

/<code>
linux内核源码编译过程分析之Kconfig,Makefile

linux内核源码编译过程分析之Kconfig,Makefile

总结一下,linux内核源码的编译过程为:

  • 顶层Makefile决定了linux内核源码各个目录和子目录的编译顺序
  • make menuconfig通过对内核配置,生成了一系列CONFIG_XXX的配置
  • 各层级的Makefile结合这些CONFIG_XXX配置来决定哪些文件被编译到内核、哪些文件被编译成驱动
  • 顶层Makefile按照链接脚本链接所有的.o文件并最终打包成一个完整的内核镜像文件vmlinux。

关注”技术简说“,带你由浅入深学习linux内核源码。linux内核开发100讲免费教程,每天晚上九点准时更新,敬请收看。进我主页点”视频“栏目即可观看。


分享到:


相關文章: