cuter

【笔记】Linux内核配置与编译

0
阅读(3453)

最近想给Miz702开发板配一块液晶显示屏,目标是在裸机和Linux下点亮该液晶屏,所以搞了一段时间的裸机开发,又回到了Linux驱动学习的道路上来。

之前制定的《Linux学习规划》基本上完成了,成功驱动了自主GPIO IP核。但仍停留在浅尝辄止的层次,近期尝试驱动LCD,发现比较吃力,所以必须踏上新的学习进程。

本文学习目标是搞清楚下面两个问题:

Linux内核里那么多文件,很多是和Zynq无关的,难道都会参与编译?

参与编译的驱动程序又是以何种形式进行编译,内建还是模块?


经过学习,基本上可以把解决问题的关键定位到Kconfig和Makefile文件,所以首先对这两种文件进行扫盲。


1) Kconfig文件

1.1) Kconfig文件的作用

Linux 内核源码树的每个目录下都有两个文档Kconfig和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个 Kconfig分别描述了所属目录源文档相关的内核配置菜单。在执行内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文档中。在内核编译时,主Makefile调用这 个.config,就知道了用户的选择。这个内容说明了,Kconfig就是对应着内核的每级配置菜单。

1.2) Kconfig的语法:
一个典型的内核配置菜单如下:

menu "Network device support"     
config NETDEVICES     
        bool "Enable Net Devices"     
        depends on NET     
        default y     
       help     
               This is help desciption。     
...     
endmenu

包含在menu/endmenu中的内容会成为Network device support的子菜单。每一个子菜单项都是由config来定义的。congfig下方的那些bool、depends on、default、help等为config的属性,用于定义该菜单项的类型、依赖项、默认值、帮助信息等。

1.2.1) 菜单项的类型

每个config菜单项都要有类型定义,bool布尔类型、 tristate三态(内建、模块、移除)、string字符串、 hex十六进制、 integer整型。有两个基本类型:tristate 和 string,其他类型都是基于这两个基本类型。

bool类型的只能选中或不选中,显示为[ ];

tristate类型的菜单项多了编译成内核模块的选项,显示为< >;

    假如选择编译成内核模块,则会在.config中生成一个CONFIG_HELLO_MODULE=m的配置;

    假如选择内建,会在.config 中生成一个CONFIG_HELLO_MODULE=y的配置。驱动程序会被直接编译进内核映像。

1.2.2) 依赖项定义

依赖项定义depends on或requires,指此菜单的出现和否依赖于另一个定义。

config HELLO_MODULE
bool "hello test module"
depends on ARCH_PXA

这个例子表明HELLO_MODULE这个菜单项只对XScale处理器有效。

1.2.3) 帮助性定义

只是增加帮助用关键字help或---help---。

举一个完整实例如下,例如添加一个I2C芯片:

config QL_VEE
tristate "QL Visual Enhancement Engine (VEE)"
default y
depends on I2C && EXPERIMENTAL

help
QL Visual Enhancement Engine (VEE) v1.0 with I2C-Compatible Interface and 24-Bit RGB Support Rev.

1.2.4) select:反向依赖

如果当前项选中,那么也选中select后的选项。

1.2.5) range:范围,用于hex和integer

range A B表示当前值不小于A,不大于B

1.2.6) comment:注释


1.4) 目录层次迭代

在Kconfig中有类似语句:source "drivers/usb/Kconfig"    
用来包含(或嵌套)新的Kconfig文件,这样便可以使各个目录管理各自的配置内容,不必把配置都写在同一个文件里,方便修改和管理。


至此,第二个问题已经被圆满回答了,Kconfig的配置决定了驱动是以内建还是模块的形式参与编译。虽然可以通过Kconfig文件在配置内核的时候进行选择,但实际上此时执行编译内核还是不能把源代码编译进去的,还需要在Makefile中把内核配置选项和真正的源代码联系起来。


2) Makefile

2.1) 2.6内核的Makefile分为5个组成部分:    
       1. 最顶层的Makefile    
     2. 内核的.config配置文件    
     3. 在arch/$(ARCH) 目录下的体系结构相关的Makefile    
     4. 在s目录下的 Makefile.* 文件,是一些Makefile的通用规则    
     5. 各级目录下的大概约500个kbuild Makefile文件

顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 s目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。

2.2) Kbuild Makefile

对于Makefiles的不同组成部分,有一些不同的语法规则。针对的对象也不同,对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各 层目录下基于kbuild架构的kbuild Makefile文件。Kbuild Makefile核心内容主要包括:

2.2.1) 目标定义

目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。如:

obj-y += foo.o

表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。 除了y,m以外的obj-x形式的目标都不会被编译。而更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式(该变量如何起作用见文末另一篇文章的链接),如:    
obj-$(CONFIG_EXT2) += ext2.o

除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下。


3) Kconfig和Makefile的配合使用

当我们在内核源码目录下执行make (或者make menuconfig等命令)命令时,实际上是根据makefile 来进行编译的。例如,我们编写了一个按键控制led灯的驱动。文件名为buttons_leds.c,属于字符驱动。因此在/driver/char/目录下的makefile部分最后添加一行

obj-$(CONFIG_BUTTONS_LEDS)      += buttons_leds.o

整个文件内容如下:

obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o  tty_ldisc.o tty_buffer.o tty_port.o 
obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o 
obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o 
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o 
obj-$(CONFIG_AUDIT) += tty_audit.o
obj-$(CONFIG_BUTTONS_LEDS)    += buttons_leds.o

其中第一行obj-y  中的y表示编译进内核,而obj-$(CONFIG_LEGACY_PTYS)  中的CONFIG_LEGACY_PTYS则表示一个变量,类似于C语言中的变量,用$( )来表示,它一般可以取三种值y ,m ,n。y表示编译进内核,m表示以模块的方式进行编译,n表示不参与编译。obj-y += 等号后面的.o后缀文件则是由该目录下的对应名称的.c文件编译而来。而上面CONFIG_LEGACY_PTYS变量的取值则是通过.config文件来集中赋值的。

.config部分内容如下

# Character devices 
# 
CONFIG_VT=y 
CONFIG_CONSOLE_TRANSLATIONS=y 
CONFIG_VT_CONSOLE=y 
CONFIG_HW_CONSOLE=y 
# CONFIG_VT_HW_CONSOLE_BINDING is not set 
# CONFIG_DEVKMEM is not set 
CONFIG_MINI2440_HELLO_MODULE=m 
CONFIG_BUTTONS_LEDS_ZHAO=m 
CONFIG_LEDS_MINI2440=m 
CONFIG_MINI2440_BUTTONS=m 
CONFIG_MINI2440_BUZZER=y 
CONFIG_MINI2440_ADC=y 
# CONFIG_SERIAL_NONSTANDARD is not set

从上面几行我们可以看到,在makefile里面的变量都是在.config中赋值的。当我们在源代码目录下输入make命令时,都是默认从.config中读入。

因此,在输入make之前,用ls  -a 查看一下是否有该文件。对于只包含几个文件的工程来说,手动写.configmakefile并不是一件很难的事情,但如果是一个包含有几百个文件的项目来说,则是一件比较困难的事情。可以用autoconf来自动生成.configautomake来制作makefile。看起来问题是解决了,但实际上,这种做法缺乏一定的灵活性——不能实现按需定制的要求。如果要添加或删掉某个驱动,将要在.config文件中找到相应的项进行修改。非常的不方便。因此,便出现了Kconfig

至此,第1个问题也得到了圆满的回答。真正负责编译的仍然是Makefile文件,但是Kconfig的配置决定了程序是否参与编译以及编译的形式。

接下来就可以借助Kconfig文件缩小目标范围,专心研究那些参与编译的源码了。


参考资料:

http://www.cnblogs.com/taomaomao/archive/2012/01/05/2312816.html

http://www.cnblogs.com/image-eye/archive/2011/08/28/2156005.html

http://blog.csdn.net/tangkegagalikaiwu/article/details/8508721