四 通用目标之make modules的执行过程分析

发布时间 2023-10-20 17:45:04作者: imxiangzi

搜索顶层makefile发现会有两个modules目标,它们的定义分别如图3.13和3.14,查看代码发现它们分别位于ifeq ($(KBUILD_EXTMOD),)和else的条件中。KBUILD_EXTMOD的定义可以参考图2.5,即若编译的为外部模块(含有M=xxx参数),则将其设置为1,否则为0,所以第一个modules目标会在编译非外部模块时调用,而第二个modules在编译外部模块时才调用。显然,现在没有指定M=xxx,故会使用第一个modules目标的定义。

                                          图3.13

                                          图3.14
    它依赖于vmlinux-dirs,modules.builtin,若KBUILD_BUILTIN值为非0时,它还需要依赖vmlinux,我们先不考虑vmlinux的情况。vmlinux-dirs的定义可参考图3.5,它是kernel顶层目录中编译vmlinux所需的子目录。
    该目标的规则与生成vmlinux时相同,具体可参考图3.7,故实际的命令为:
    $(MAKE) $(build)=$@
    其中$@是目标vmlinux-dirs的值,因此该命令就是进入vmlinux-dirs指定的各个子目录,然后执行递归编译,直到所有定义的目标都编译完成。我们再回顾下makefile.build中如何编译模块相关的目标。它的规则可参考图6.12,此处我们再贴一下(图3.15)。
                                          图3.15
    而KBUILD_MODULES和KBUILD_BUILTIN的定义如图3.16,根据我们的编译命令可知此时KBUILD_MODULES为1,而KBUILD_BUILTIN为0。结合上图可看出它的依赖为obj-m,modorder-target和subdir-ym。

                                          图3.16
    obj-m是子目录中包含进来的makefile中定义的,它又被分为multi-used-m和single-used-m两部分,具体的生成规则可参考第六章的第5条。即single目标会通过cc定义的交叉编译器将单个文件编译成目标文件,multi目标除了编译之外,还需要调用ld定义的交叉链接器将各个目标文件链接起来。

                                          图3.17
    subdir-ym目标的定义可参考图6.9,它是当前目录所有子目录的集合。它的规则定义可参考图6.30,即会对每个子目录分别调用makefile.build,且其obj参数为目录名。它的作用就是在子目录下的obj-m目标按上面一样的规则编译出来,然后再对子目录的子目录分别调用makefile.build,直到不存在子目录为止。通过这一过程就会把子目录所包含的所有obj-m目标都编译出来。
    modorder-target位于makefile.build文件中,它的定义如下,即是传给makefile.build的参数obj后面再加上modules.order构成。显然,它的意思为在每个调用makefile.build的子目录下都创建一个modules.order文件。
    modorder-target := $(obj)/modules.order
    我们再来看下该目标的规则如图3.18,它的依赖subdir-ym上面已经介绍过了,接下来重点看下命令。

                                          图3.18
    cat /dev/null > test实际上可以用于生成一个空文件test,我们可以做一个如图3.19的实验。该命令还会调用modorder-cmds,并把它们的输出写入modorder-target中,modorder-target就是上面介绍的每个目录下的modules.order文件。

                                          图3.19
    我们再看modorder-cmds的定义,它会遍历modorder变量,然后对符合 %/modules.order的变量,会输出该文件的内容。而对其它的变量,则会在其开头位置加上kernel/后打印变量信息。
    modorder变量的定义如图3.20,它的构成分两部分,一个是过滤出当前目录下obj-y定义的目录(即需要编译的子目录),然后在其后添加上modules.order。另一部分为将obj-m目标的.o后缀修改为.ko后缀的值。

                                          图3.20
    结合上面分析可知,该命令作用是遍历本目录的子目录的modules.order文件,和本目录下obj-m定义的目标,然后将它们的内容按顺序写入本目录的modules.order文件。由于编译过程是对目录递归实现的,故先生成的ko目标就位于文件的前面,后生成的目标位于文件后面,该文件就是本目录及其子目录下所有obj-m目标的编译顺序。
    对于modules.builtin依赖,大家猜一下就知道了它里面包含的内容应该是编译到内核中的模块。我们知道在配置内核时,模块相关的配置是有三个选项y,m和n的,当配置为n表示该模块不编译,配置为m表示它会编译成模块,而配置为y就会编译进内核编译。这个依赖就是在各个目录下生成modules.builtin文件,文件内容就是搜集该目录及其子目录所包含配置为y的模块信息。
    它具体的生成规则与modules.order生成方式很类似,只是会调用scripts/makefile.modbuiltin文件,在该文件中会搜集本目录下的builtin模块,然后分别用其子目录做为目标,递归调用其自身(scripts/makefile.modbuiltin),以完成所有builtin目标的搜集。
    ok,依赖都分析完成了,再看它调用的命令,我们把它们重新贴一下,如图3.21。

                                          图3.21
    第一条awk命令主要是将modules.order文件中重复的行删除。第二条kecho命令只是一条打印信息。重点内容在后面两条。
    第三条命令调用了scripts/makefile.modpost文件,我们看下该文件做了什么工作。从文件可看到ko最终的生成规则如图3.22,它依赖于同名的.o文件和.mod.o文件,依赖满足后,会调用cmd_ld_ko_o命令将它们链接起来,从而产生最后的ko文件。

                                          图3.22
    其中.o文件在前面stage 1的时候已经生成了,剩下来就是.mod.o的生成了,它的规则如图3.23,即其会依赖于相应的mod.c文件,若依赖满足后,就调用交叉编译命令将该.c文件编译成.o文件。

                                          图3.23
    接下来自然是寻找mod.c文件的出处了,它的定义如图3.24,即它会依赖__modpost。它的依赖关系如图3.25,即它最终会调用modpost命令。该命令是由scripts/mod/modpost.c编译出来的,感兴趣的同学可以研究下它的实现。

                        ;图3.24

                                          图3.25
————————————————
版权声明:本文为CSDN博主「lgjjeff」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lgjjeff/article/details/90489331