Linux下makefile和kconfig简单解析

时间:2022-09-01 21:09:09

  想要研究linux下的驱动开发,makefile和kconfig是我们必须关注的,kbuild系统更是我们必须了解的重要部分,那我们就先看看kbuild的英文文档吧!

MostMakefiles within the kernel are kbuild Makefiles that use the kbuildinfrastructure. This chapter introduces the syntax used in the kbuildmakefiles.
The preferred name for the kbuild files are 'Makefile' but 'Kbuild' can beused and if both a 'Makefile' and a 'Kbuild' file exists, then the 'Kbuild'
file will be used.

Section3.1 "Goal definitions" is a quick intro, further chaptersprovide more details, with real examples.

---3.1 Goal definitions

 Goaldefinitions are the main part (heart) of the kbuild Makefile.These lines definethe files to be built, any special compilation options, and anysubdirectories to be entered recursively.

 Themost simple kbuild makefile contains one line:

 Example:
 obj-y += foo.o

 Thistells kbuild that there is one object in that directory,named  foo.o. foo.o will be built from foo.c or foo.S.

 Iffoo.o shall be built as a module, the variable obj-m is used.
 Therefore the following pattern is often used:

 Example:
  obj-$(CONFIG_FOO) += foo.o

 $(CONFIG_FOO)evaluates to either y (forbuilt-in) or m (for module). If CONFIG_FOO isneither y nor m, then the file will not be compiled nor linked.

---3.2 Built-in object goals - obj-y

 Thekbuild Makefile specifies object files for vmlinux in the $(obj-y) lists. These lists depend on the kernel  configuration.

 Kbuildcompiles all the $(obj-y) files.  It then calls  "$(LD) -r" tomerge these files into one built-in.o file.  built-in.ois later linked into vmlinux bythe parent Makefile.

Theorder of files in $(obj-y) is significant.  Duplicates in the listsare allowed: the first instance will be linked into built-in.o andsucceeding instances will be ignored.

Linkorder is significant, because certain functions (module_init() / __initcall)will be called during boot in the order they appear. So keep in mind thatchanging the link order may e.g. change the order in which your SCSIcontrollers are detected, and thus your disks are renumbered.

---3.3 Loadable module goals - obj-m

$(obj-m) specifyobject files which are built as loadable kernel modules.A module may bebuilt from one source file or several source files. In the case of one sourcefile, the kbuild makefile simply adds the file to $(obj-m).

Example:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

Note:In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm'

Ifa kernel module is built from several source files, you specify that youwant to build a module in the same way as above.Kbuild needs to know which theparts that you want to build your module from, so you have to tell it bysetting an

$(<module_name>-objs) variable.

Example:

 #drivers/isdn/i4l/Makefile
 obj-$(CONFIG_ISDN) += isdn.o
 isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

Inthis example, the module name will be isdn.o. Kbuild will compile theobjects listed in $(isdn-objs) and then run "$(LD) -r" on thelist of these files to generate isdn.o. Kbuild recognises objects used forcomposite objects by the suffix -objs, and the suffix -y. This allows theMakefiles to use

thevalue of a CONFIG_ symbolto determine if an object is part of a composite object.

Example:
#fs/ext2/Makefile
 obj-$(CONFIG_EXT2_FS) += ext2.o
  ext2-y := balloc.o bitmap.o
  ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o

 Inthis example, xattr.o is only part of the composite object ext2.o if$(CONFIG_EXT2_FS_XATTR) evaluates to 'y'.Note: Of course, when you are buildingobjects into the kernel,the syntax above will also work. So, if you haveCONFIG_EXT2_FS=y, kbuild will build an ext2.o file for you out of theindividual

parts and then link this into built-in.o,as you would expect.

在内核编译中如何将各个目录树中的文件组织起来编译是一个很重要的问题,并且要根据用户配置来编译特有的内核。为了解决这个问题,内核使用两种文件,Makefie和Kconfig。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文档相关的内核配置菜单,就是我们使用命令 make menuconfig(或者xconfig)后产生的配置菜单,此菜单包含多层,每个层次都是由各个目录中的Kconfi*生的。用户根据需求来选择如何编译内核,然后将配置结果保存到.config中,然后执行Makefile时就会根据.config的结果来实现内核的编译。

这个过程是由kbuild系统来完成的,Linux编译系统会两次扫描Linux的Makefile:首先编译系统会读取Linux内核顶层的Makefile,然后根据读到的内容第二次读取Kbuild的Makefile来编译Linux内核。内核编译系统或者说kbuild,是一种在编译内核时,可以对内核配置选项进行选择的机制。2.6内核树中已经更新了这种机制,新版本的kbuild 不仅高速而且备有更完善的文档。Kbuild机制完全依赖于源代码的层次结构。

Makefile文件

面对树状结构的内核源码目录,内核编译采用了各个子目录拥有自己目录相关的Makefile(被称为sub-Makefile或kbuild Makefile),内核编译依赖于各个子目录下的子makefile(sub-Makefile)文件,这些sub-Makefile定义了根据该子目录下的源码文件构建目标文件的规则,并且仅对该目录下的文件作适当的修改。顶层Makefile采用递归的方式调用位于init/, drivers/, sound/,net/, lib/ ,usr/等目录下的各个子目录中的Makefile文件。在递归调用之前,kbuild首先要确定是否已经满足一些必要的条件,包括在必要时更新include/Linux/version.h文件,并设置符号链接include/asm,使之指向与目标体系结构相关的文件。例如,如果为PPC编译代码,则include/asm指向include/asm-ppc。kbuild还要对文件include/Linux/autoconf.h和include/Linux/config进行编译。之后,从根目录开始进行递归。

各个子Makefile文件比较简单,指出了该如何编译目标文件,例如/mm目录下的Makefile片段:

16obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o 
17 obj-$(CONFIG_BOUNCE)    += bounce.o 
18 obj-$(CONFIG_SWAP)      += page_io.o swap_state.oswapfile.o thrash.o 
19 obj-$(CONFIG_HAS_DMA)   += dmapool.o 
20 obj-$(CONFIG_HUGETLBFS) += hugetlb.o

Kconfig文件 
       Kconfig的作用就是为了让用户配置内核,在Kconfig中定义了一些变量,用户通过设置变量的值来选择如何个性化自己的系统内核。定义的变量将在 
每个菜单都有一个关键字标识,最常见的就是config 
语法: 
config 
symbol是个新的标记的菜单项,options是在这个新的菜单项下的属性和选项 
其中options部分有: 
1、类型定义: 
每个config菜单项都要有类型定义,bool布尔类型、 tristate三态:内建、模块、移除 string字符串、 hex十六进制、 integer整型 
例如config HELLO_MODULE 
bool “hello test module” 
bool 类型的只能选中或不选中,tristate类型的菜单项多了编译成内核模块的选项,假如选择编译成内核模块,则会在.config中生成一个 CONFIG_HELLO_MODULE=m的配置,假如选择内建,就是直接编译成内核影响,就会在.config中生成一个 CONFIG_HELLO_MODULE=y的配置. 
2、依赖型定义depends on或requires 
指此菜单的出现和否依赖于另一个定义 
config HELLO_MODULE 
bool “hello test module” 
depends on ARCH_PXA 
这个例子表明HELLO_MODULE这个菜单项只对XScale处理器有效。 
3、帮助性定义 
只是增加帮助用关键字help或—help—

.config文件 

      上面提到了利用内核配置工具自动生成名为.config的内核配置文件,这是编译内核的第一步。.config文件位于源代码根目录下,描述所有内核配置选项,可以借助内核配置工具来选择这些选项。每个内核配置选项都有相关的名字和变量值。其名字形如CONFIG_<NAME>,其中<NAME>是对相关选项的标识,在Kconfig文件中定义;变量可以有三个值:y,m或n。y代表“yes”,表示该选项将会被编译到内核源代码中,或者说会被编译到系统中。m代表“module” ,表示该选项将会以模块的方式编译到内核中。如果未选择该选项(即将该选项的变量值设为n,代表“no”),那么.config文件中就会出现下列注释:“CONFIG_<NAME> is not set”。.config文件中选项的位置根据它们在内核配置工具中的位置进行排序,注释部分说明该选项位于哪个菜单下。我们来看看一个.config文件的节选: 
1 # 
2 # Automatically generated make config: don’t edit 
3 # 
4 CONFIG_X86=y 
5 CONFIG_MMU=y 
6 CONFIG_UID16=y 
7 CONFIG_GENERIC_ISA_DMA=y 

9 # 
10 # Code maturity level options 
11 # 
12 CONFIG_EXPERIMENTAL=y 
13 CONFIG_CLEAN_COMPILE= 
14 CONFIG_STANDALONE=y 
15 CONFIG_BROKEN_ON_SMP=y 
16 
17 # 
18 # General setup 
19 # 
20 CONFIG_SWAP=y 
21 CONFIG_SYSVIPC=y 
22 #CONFIG_POSIX_MQUEUE is not set 
23 CONFIG_BSD_PROCESS_ACCT=y 
上述.config文件指出第4到第7行的选项位于顶层菜单中,第12到第15行的选项位于代码成熟度选项菜单中,第20行到第23行的选项位于通用设置选项菜单中。 
所有配置工具都会产生上述菜单,并且已经看到前几个选项、代码成熟度选项、及通用设置选项都位于顶层。后面两个选项被扩展为包含多个选项的子菜单。这些菜单都是在调用xconfig命令时,由qconf配置工具提供的。配置工具显示的菜单都默认用于X86体系结构。 
DIY:向内核添加自己的程序 

      A.在Linux内核中增加自己的程序步骤(注意这里只是程序文件): 
1.将编写的源代码复制到Linux内核源代码的相应目录中。 
2.在目录的Kconfig文件中增加新源代码对应项目的编译配置选项 
3.在目录的Makefile文件中增加对新源代码的编译条目。 
B.在Linux内核drivers/目录中增加目录和子目录步骤: 
1.所加目录为myDriver,文件如下: 
myDriver$ tree 
|– Kconfig 
|– Makefile 
|– key 
|   |– Kconfig 
|   |– Makefile 
|   `– key.c 
|– led 
|   |– Kconfig 
|   |– Makefile 
|   `– led.c 
`— test.c 
#注意此时各个目录中的Makefile和Kconfig文件是空的 
2.在新增的相应目录添加Kconfig和Makefile文件,上面的目录中已经添加。 
3.修改新增目录的父目录的Kconfig和Makefile文件,以便新增的Kconfig和 
Makefile能被引用。向父目录中的Makefile添加: 
obj-y                          += myDriver/ 
表示在编译过程中包含子目录myDriver目录。然后修改Kconfig文件,添加: 
source “drivers/myDriver/Kconfig” 
表示在配置时引用子目录myDriver中的配置文件Kconfig。 
4.经过上面一步,父目录就可以找到所加的目录myDriver了,然后就是编辑各个目 录中的Makefile和Kconfig文件,在你添加的目录myDriver中的Makefile加入: 
obj-$(CONFIG_TEST) += test.o #因为在myDriver目录中要编译test.c文件 
     #所以会根据CONFIG_TEST来决定编译选项 
    obj-y += led/#编译myDriver目录中的子目录led 
    obj-y += key/#编译myDriver目录中的子目录key

然后Kconfig文件是: 
menu “TESTMyDriver”            #在make menuconfig时要显示的菜单入口                                                   
comment “Test myDriver”       #menu title 
config TEST 
        tristate “MyDriver test” 
source “drivers/myDriver/led/Kconfig” #将led目录下的Kconfig添加进来 
source “drivers/myDriver/key/Kconfig” 
endmenu 
再看led目录下的Makefile和Kconfig: 
Makefile为文件: 
obj-$(CONFIG_LED)+=led.o 
Kconfig文件: 
config LED 
      tristate “led support” 
key目录下的Makefile和Kconfig类似。 
5.现在可以make menuconfig来配置添加自己目录myDriver的内核了!

PS:------linux驱动加载

在类unix操作系统中,驱动加载方式一般分为:动态加载和静态加载,下面分别对其详细论述。

一、动态加载
动态加载是将驱动模块加载到内核中,而不能放入/lib/modules/下。
在2.4内核中,加载驱动命令为:insmod ,删除模块为:rmmod;
在2.6以上内核中,除了insmod与rmmod外,加载命令还有modprobe;
insmod与modprobe不同之处:

insmod 绝对路径/××.o,而modprobe ××即可,不用加.ko或.o后缀,也不用加路径;最重要的一点是:modprobe同时会加载当前模块所依赖的其它模块;

lsmod查看当前加载到内核中的所有驱动模块,同时提供其它一些信息,比如其它模块是否在使用另一个模块。
二、静态加载
(一)概念
    在执行make menuconfig命令进行内核配置裁剪时,在窗口中可以选择是否编译入内核,还是放入/lib/modules/下相应内核版本目录中,还是不选。
(二)操作步骤
    linux设备一般分为:字符设备、块设备和网络设备,每种设备在内核源代码目录树drivers/下都有对应的目录,其加载方法类似,以下以字符设备静态加载为例,假设驱动程序源代码名为ledc.c,具体操作步骤如下:
    第一步:将ledc.c源程序放入内核源码drivers/char/下;
    第二步:修改drivers/char/Config.in文件,具体修改如下:
按照打开文件中的格式添加即可;
 在文件的适当位置(这个位置随便都可以,但这个位置决定其在make menuconfig窗口中所在位置)加入以下任一段代码:
          tristate 'LedDriver'CONFIG_LEDC
         if [ "$CONFIG_LEDC" ="y" ];then
         bool '   Support for led onh9200 board' CONFIG_LEDC_CONSOLE
         fi
    说明:以上代码使用tristate来定义一个宏,表示此驱动可以直接编译至内核(用*选择),也可以编制至/lib/modules/下(用M选择), 或者不编译(不选)。

      bool'LedDriver' CONFIG_LEDC
         if [ "$CONFIG_LEDC" ="y" ];then
         bool '   Support for led onh9200 board' CONFIG_LEDC_CONSOLE
         fi
        
说明:以上代码使用tristate来定义一个宏,表示此驱动只能直接编译至内核(用*选择)或者不编译(不选),不能编制至/lib/modules/ 下(用M选择)。
第三步:修改drivers/char/Makefile文件
         在适当位置加入下面一行代码:
         obj-$(CONFIG_LEDC)  +=   ledc.o
         或者在obj-y一行中加入ledc.o,如:
         obj-y += ledc.o mem.o 后面不变;
      OK,经过以上的设置就可以在执行make menuconfig命令后的窗口中的characterdevices---> 中进行选择配置了。选择后重新编译就 OK了。