zoukankan      html  css  js  c++  java
  • Linux Kbuild工作原理分析(以DVSDK生成PowerVR显卡内核模块为例)

    一、引文

    前篇博文Makefile之Linux内核模块的Makefile写法分析》,介绍了Linux编译生成内核驱动模块的Makefile的写法但最近在DVSDK下使用Linux2.6.37生成PowerVR 2D/3D加速器的内核模块时,发现其Makefile中并未向其他内核模块Makefile指定内核模块的目标对象(obj-m:= pvrsrvkm.o),但最后却在模块目录下生成了pvrsrvkm.ko内核模块。一开始感觉非常奇怪,决定把其中的原由弄明白,并将该过程记录了下来。

     

    /**********************************************************************************************************************************/

          原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb666/article/details/9054413,作者:gqb666  
        /**********************************************************************************************************************************/

    二、追踪

    既然模块文件夹下的Makefile文件没有指定,那肯定会有其他有相同功能的文件会指定内核模块的目标对象pvrsrvkm.o,于是查看该目录下的文件,果然在Kbuid文件发现有与Makefile文件类似的内容,将Kbuild的关键内容贴出来:

    obj-m	:= pvrsrvkm.o
    FILES := 
    services4/srvkm/common/queue.c 
    ……
    services4/system/$(TI_PLATFORM)/sysconfig.c 
    
    EXTRA_CFLAGS += -I$(src)/include4
    EXTRA_CFLAGS += -I$(src)/services4/include
    ……
    EXTRA_CFLAGS += -I$(src)/services4/srvkm/bridged/sgx
    EXTRA_CFLAGS += $(ALL_CFLAGS)
    
    pvrsrvkm-y	:= $(FILES:.c=.o)
    ifeq ($(TI_PLATFORM),ti8168)
    obj-y := services4/3rdparty/dc_ti8168_linux/
    else
    obj-y := services4/3rdparty/dc_omap3430_linux/
    endif
    obj-y += services4/3rdparty/bufferclass_ti/

    三、真相:Linux Kbuild子系统

    实际上从Linux内核2.6开始,Linux内核的编译采用Kbuild系统,这同过去的编译系统有很大的不同, 尤其对于Linux内核驱动模块的编译。在新的系统下,Linux编译系统会两次扫描LinuxMakefile:首先编译系统会读取Linux内核顶层的Makefile,然后根据读到的内容第二次读取KbuildMakefile来编译Linux内核。

    查看内核文档:../Documentation/kbuild/makefiles.txt我们可以获取到以下信息:
    Linux内核Makefile分类(五类)

    ·Kernel Makefile 
    Kernel Makefile位于Linux内核源代码的顶层目录,也叫 Top Makefile。它主要用于指定编译Linux Kernel目标文件(vmlinux)和模块(module)。这编译内核或模块是,这个文件会被首先 读取,并根据读到的内容配置编译环境变量。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。

    ·.config

    此文件为Kbuid构建子系统从..archarmconfigs目录下读了内核的相关配置或者通过make menuconfig而生成的内核临时配置文件。
    ·sripts/Makefile.*

       sripts/Makefile.* 包含了使用Kbuild Makefile子系统所用的所有规则的定义。
    ·Kbuild Makefile 
         Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后,Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile文件。
    ·ARCH Makefile 
    ARCH Makefile位于ARCH/$(ARCH)/Makefile,是系统对应平台的Makefile。Kernel Top Makefile会包含这个文件来指定平台相关信息。只有平台开发人员会关心这个文件。
        Kbuild Makefile
    的文件名不一定是Makefile,尽管推荐使用Makefile这个名字。 大多的Kbuild文件的名字都是Makefile。为了与其他Makefile文件相区别,你也可以指定Kbuild Makefile的名字为Kbuild。而且如果“Makefile”和“Kbuild”文件同时存在,则Kbuild系统会使用“Kbuild”文件。

    上面的几句来自对内核文档的翻译,然而上面标红的一段话,经过验证感觉不太准确,博主将模块目录中的Makefile删除,仅留下Kbuild文件却模块编译会出错。这到底是怎么回事呢?还是跟下代码流程来分析。

    四、根本解决(Linux Kbuild 工作原理)

    实际上执行了make命令后,Kbuild子系统还是要找到模块目录下的Makefile,如下:

     

    all:
    	$(MAKE) -C $(KERNELDIR) M=`pwd` $*

    其中 KERNELDIR 变量值为内核源码目录根目录

    可见,Kbuild子系统将进行内核源码根目录下去执行make,并将模块所在的目录名以参数的形式传进去。

     

    实际上该过程总共分为两个stage:

    1.编译出pvrsrvkm.o文件。

    2.生成pvrsrvkm.mod.o pvrsrvkm.ko,并内核模块文件拷加原模块目录。见如下打印log:

     

    make -C /home/ss/ti-dvsdk_dm3730-evm_4_02_00_06/psp/linux-2.6.32-psp03.00.01.06.sdk M=`pwd` 
    Building objects, stage 1.
    make[3]: Entering directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/psp/linux-2.6.32-psp03.00.01.06.sdk'
    ……
      LD [M]  /home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/GFX_Linux_KM/pvrsrvkm.o
      Building modules, stage 2.
      MODPOST 3 modules
      CC      /home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/GFX_Linux_KM/pvrsrvkm.mod.o
      LD [M]  /home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/GFX_Linux_KM/pvrsrvkm.ko
      ……
    make[3]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/psp/linux-2.6.32-psp03.00.01.06.sdk'
    make[2]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/GFX_Linux_KM'
    copying the sgx kernel modules to appropriate folder...
    building devmem2...
    make -C /home/ss/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2 clean && make -C /home/ss/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2 && make -C /home/ss/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2 install
    make[2]: Entering directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    rm -rf ./Obj *.o
    make[2]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    make[2]: Entering directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    mkdir -p ./Obj
    ……
    arm-none-linux-gnueabi-g++ -o ./Obj/devmem2 ./Obj/devmem2.o    -lm -ldl -L/lib -Wl  
    /usr/local/arm/arm-2009q1/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.3/../../../../arm-none-linux-gnueabi/bin/ld: warning: library search path "/lib" is unsafe for cross-compilation
    make[2]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    make[2]: Entering directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    cp ./Obj/devmem2 ../../targetfs/
    make[2]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01/tools/devmem2'
    make[1]: Leaving directory `/home/ss/develop_environment/v2/sys/dvsdk/ti-dvsdk_dm3730-evm_4_02_00_06/omap35x_graphics_sdk_4.00.00.01'

    stage 1:

       内核Makefile根据时间戳进行比较看哪些目标文件需要编译,这个过程将生成pvrsrvkm.o文件。

    stage 2:

    这个obj-m :pvrsrvkm.o什么时候会执行到呢?

    在执行:

     

    $(MAKE) -C $(KERNELDIR) M=`pwd` $*

    时,Kbuild去内核根目录下的Makefile中寻找目标modules,代码贴上:

     

    PHONY += modules
    modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
    	$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
    	@$(kecho) '  Building modules, stage 2.';
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild

    在这过程中,会调用

    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

    而在 scripts/Makefile.modpost中会包含很多文件如

     

    include $(if $(wildcard $(KBUILD_EXTMOD)/Kbuild), 
                 $(KBUILD_EXTMOD)/Kbuild, $(KBUILD_EXTMOD)/Makefile)

    由上面可看出:内核根目录Makefile相当于以头文件的形式包含了模块目录下的Kbuild文件和Makefile

    所以执行的是:obj-m:=pvrsrvkm.o

    进而pvrsrvkm.mod.o pvrsrvkm.ko也就生成了,最后又将pvrsrvkm.ko内核模块拷贝到原模块目录。

  • 相关阅读:
    ASP.NET中JSON的序列化和反序列化
    Git 本地项目上传至托管平台(OsChina/GitHub)
    Android Gradle 完整指南(转)
    开发错误记录13:java.lang.UnsatisfiedLinkError: Couldn't load xxx.so: findLibrary returned null
    Android其它新控件 (转)
    一个Activity掌握Design新控件 (转)
    一个Activity掌握Android5.0新控件 (转)
    一个Activity掌握Android4.0新控件 (转)
    开发错误日记 12: Unsupported major.minor version 52.0
    Linux 下各个目录的作用及内容
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3181708.html
Copyright © 2011-2022 走看看