内核简介
内核,是一个操作系统的核心。它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能和稳定性。
Linux的一个重要的特点就是其源代码的公开性,所有的内核源程序都可以在/usr/src/linux下找到,大部分应用软件也都是遵循GPL而设计的,你都可以获取相应的源程序代码。全世界任何一个软件工程师都可以将自己认为优秀的代码加入到其中,由此引发的一个明显的好处就是Linux修补漏洞的快速以及对最新软件技术的利用。而Linux的内核则是这些特点的最直接的代表。
想象一下,拥有了内核的源程序对你来说意味着什么?首先,我们可以了解系统是如何工作的。通过通读源代码,我们就可以了解系统的工作原理,这在Windows下简直是天方夜谭。其次,我们可以针对自己的情况,量体裁衣,定制适合自己的系统,这样就需要重新编译内核。在Windows下是什么情况呢?相信很多人都被越来越庞大的Windows整得莫名其妙过。再次,我们可以对内核进行修改,以符合自己的需要。这意味着什么?没错,相当于自己开发了一个操作系统,但是大部分的工作已经做好了,你所要做的就是要增加并实现自己需要的功能。在Windows下,除非你是微软的核心技术人员,否则就不用痴心妄想了。
内核版本号
由于Linux的源程序是完全公开的,任何人只要遵循GPL,就可以对内核加以修改并发布给他人使用。Linux的开发采用的是集市模型(bazaar,与cathedral--教堂模型--对应),为了确保这些无序的开发过程能够有序地进行,Linux采用了双树系统。一个树是稳定树(stable tree),另一个树是非稳定树(unstable tree)或者开发树(development tree)。一些新特性、实验性改进等都将首先在开发树中进行。如果在开发树中所做的改进也可以应用于稳定树,那么在开发树中经过测试以后,在稳定树中将进行相同的改进。一旦开发树经过了足够的发展,开发树就会成为新的稳定树。开发数就体现在源程序的版本号中;源程序版本号的形式为x.y.z:对于稳定树来说,y是偶数;对于开发树来说,y比相应的稳定树大一(因此,是奇数)。到目前为止,稳定树的最高版本是2.6.18,最新发布的Redhat9.0所采用的就是2.4.20的内核;开发树的最新版本是2.5.99。内核版本的更新可以访问http://www.kernel.org。
为什么重新编译内核
Linux作为一个自由软件,在广大爱好者的支持下,内核版本不断更新。新的内核修订了旧内核的bug,并增加了许多新的特性。如果用户想要使用这些新特性,或想根据自己的系统度身定制一个更高效,更稳定的内核,就需要重新编译内核。
通常,更新的内核会支持更多的硬件,具备更好的进程管理能力,运行速度更快、 更稳定,并且一般会修复老版本中发现的许多漏洞等,经常性地选择升级更新的系统内核是Linux使用者的必要操作内容。
为了正确的合理地设置内核编译配置选项,从而只编译系统需要的功能的代码,一般主要有下面四个考虑:
l 自己定制编译的内核运行更快(具有更少的代码)
l 系统将拥有更多的内存(内核部分将不会被交换到虚拟内存中)
l 不需要的功能编译进入内核可能会增加被系统攻击者利用的漏洞
l 将某种功能编译为模块方式会比编译到内核内的方式速度要慢一些
内核编译模式
要增加对某部分功能的支持,比如网络之类,可以把相应部分编译到内核中(build-in),也可以把该部分编译成模块(module),动态调用。如果编译到内核中,在内核启动时就可以自动支持相应部分的功能,这样的优点是方便、速度快,机器一启动,你就可以使用这部分功能了;缺点是会使内核变得庞大起来,不管你是否需要这部分功能,它都会存在,这就是Windows惯用的招数,建议经常使用的部分直接编译到内核中,比如网卡。如果编译成模块,就会生成对应的.o文件,在使用的时候可以动态加载,优点是不会使内核过分庞大,缺点是你得自己来调用这些模块。
内核编译详解
新版本内核的获取和更新
Linux内核版本发布的官方网站是http://www.kernel.org,国内各大ftp上一般都可以找到某些版本的内核。新版本的内核的发布有两种形式,一种是完整的内核版本,另外一种是patch文件,即补丁。完整的内核版本比较大,比如linux-2.4.0-test8.tar.bz2就有18M之多,网速快的用户可以下载使用。完整内核版本一般是.tar.gz(.tgz)文件或者是.bz2文件,二者分别是使用gzip或者bzip2进行压缩的文件,使用时需要解压缩。patch文件则比较小,一般只有几十K到几百K,极少的会超过1M,网速慢的用户可以使用patch文件来升级内核。但是patch文件是针对于特定的版本的,你需要找到自己对应的版本才能使用。
编译内核需要root权限,以下操作都假定你是root用户。请把你需要升级的内核拷贝到/usr/src/下(下文中以2.4.0test8的内核的linux-2.4.0test8.tar.gz为例),命令为
#cp linux-2.4.0test8.tar.gz /usr/src
让我们先来查看一下当前/usr/src的内容,注意到有一个linux的符号链接,它指向一个类似于linux-2.2.14(对应于你现在使用的内核版本号)的目录。首先删除这个链接:
#cd /usr/src
#rm -f linux
现在解压我们下载的源程序文件。如果所下载的是.tar.gz(.tgz)文件,请使用下面的命令:
#tar -xzvf linux-2.4.0test8.tar.gz
如果你所下载的是.bz2文件,例如linux-2.4.0test8.tar.bz2,请使用下面的命令
#bzip2 -d linux-2.4.0test8.tar.bz2
#tar -xvf linux.2.4.0.test8.tar
现在让我们再来看一下/usr/src下的内容,你会发现现在有了一个名为linux的目录,里面就是我们需要升级到的版本的内核的源程序。还记得那个名为linux的链接么?之所以使用那个链接就是防止在升级内核的时候会不慎把原来版本内核的源程序给覆盖掉了。我们也需要同样处理:
#mv linux linux-2.4.0test8
#ln -s linux-2.4.0test8 linux
这样我们也有了一个名为linux的符号链接,就不用担心以后会把它覆盖掉了(也许你会觉得重新建立linux的符号链接没有必要,但实际上这是必不可少的,下文中会有介绍)。如果你还下载了patch文件,比如patch-2.4.0test8,你就可以进行patch操作(下面假设patch-2.4.0test8已经位于/usr/src目录下了,否则你需要先把该文件拷贝到/usr/src下):
#patch -p0 < patch-2.4.0test8
现在,我们已经把内核源程序升级到最新版本了,下面就让我们开始内核编译的旅程吧。
准备工作
通常要运行的第一个命令是:
#cd /usr/src/linux;make mrproper
该命令确保源代码目录下没有不正确的.o文件以及文件的互相依赖。由于我们使用刚下载的完整的源程序包进行编译,所以本步可以省略。而如果你多次使用了这些源程序编译内核,那么最好要先运行一下这个命令。
确保/usr/include/目录下的asm、linux和scsi等链接是指向要升级的内核源代码的。它们分别链向源代码目录下的真正的、该计算机体系结构(对于PC机来说,使用的体系结构是i386)所需要的真正的include子目录。如:asm指向/usr/src/linux/include/asm-i386等。若没有这些链接,就需要手工创建,按照下面的步骤进行:
# cd /usr/include/
# rm -r asm linux scsi
# ln -s /usr/src/linux/include/asm-i386 asm
# ln -s /usr/src/linux/include/linux linux
# ln -s /usr/src/linux/include/scsi scsi
这是配置非常重要的一部分。删除掉/usr/include下的asm、linux和scsi链接后,再创建新的链接指向新内核源代码目录下的同名的目录。这些头文件目录包含着保证内核在系统上正确编译所需要的重要的头文件。现在你应该明白为什么我们上面又在/usr/src下"多余"地创建了个名为linux的链接了吧?
配置
接下来的内核配置过程比较烦琐,但是配置的适当与否与日后Linux的运行直接相关,有必要了解一下一些主要的且经常用到的选项的设置。
配置内核可以根据需要与爱好使用下面命令中的一个:
#make config(基于文本的最为传统的配置界面,不推荐使用)
#make menuconfig(基于文本选单的配置界面,字符终端下推荐使用)
#make xconfig(基于图形窗口模式的配置界面,Xwindow下推荐使用)
#make oldconfig(如果只想在原来内核配置的基础上修改一些小地方,会省去不少麻烦)
这三个命令中,make xconfig的界面最为友好,如果你可以使用Xwindow,那么就推荐你使用这个命令.如果你不能使用Xwindow,那么就使用make menuconfig好了。界面虽然比上面一个差点,总比make config的要好多了.
选择相应的配置时,有三种选择,它们分别代表的含义如下:
Y--将该功能编译进内核
N--不将该功能编译进内核
M--将该功能编译成可以在需要时动态插入到内核中的模块
如果使用的是make xconfig,使用鼠标就可以选择对应的选项。如果使用的是make menuconfig,则需要使用空格键进行选取。你会发现在每一个选项前都有个括号, 但有的是中括号有的是尖括号,还有一种圆括号。 用空格键选择时可以发现,中括号里要么是空,要么是"*",而尖括号里可以是空,"*"和"M"这表示前者对应的项要么不要,要么编译到内核里;后者则多一样选择,可以编译成模块。而圆括号的内容是要你在所提供的几个选项中选择一项。
在编译内核的过程中,最烦杂的事情就是这步配置工作了,很多新手都不清楚到底该如何选取这些选项。实际上在配置时,大部分选项可以使用其缺省值,只有小部分需要根据用户不同的需要选择。选择的原则是将与内核其它部分关系较远且不经常使用的部分功能代码编译成为可加载模块,有利于减小内核的长度,减小内核消耗的内存,简化该功能相应的环境改变时对内核的影响;不需要的功能就不要选;与内核关心紧密而且经常使用的部分功能代码直接编译到内核中。下面就让我们对常用的选项分别加以介绍。
1. Code maturity level options
代码成熟等级。此处只有一项:prompt for development and/or incomplete code/drivers,如果你要试验现在仍处于实验阶段的功能,比如khttpd、IPv6等,就必须把该项选择为Y了;否则可以把它选择为N。
2. Loadable module support
对模块的支持。这里面有三项:
Enable loadable module support:除非你准备把所有需要的内容都编译到内核里面,否则该项应该是必选的。
Set version information on all module symbols:可以不选它。
Kernel module loader:让内核在启动时有自己装入必需模块的能力,建议选上。
3. Processor type and features
CPU类型。内容蛮多的,不一一介绍了,有关的几个如下:
Processor family:根据你自己的情况选择CPU类型。
High Memory Support:大容量内存的支持。可以支持到4G、64G,一般可以不选。
Math emulation:协处理器仿真。协处理器是在386时代的宠儿,现在早已不用了。
MTTR support:MTTR支持。可不选。
Symmetric multi-processing support:对称多处理支持。除非你有多个CPU,否则就不用选了。
4. General setup
这里是对最普通的一些属性进行设置。这部分内容非常多,一般使用缺省设置就可以了。下面介绍一下经常使用的一些选项:
Networking support:网络支持。必须,没有网卡也建议你选上。
PCI support:PCI支持。如果使用了PCI的卡,当然必选。
PCI access mode:PCI存取模式。可供选择的有BIOS、Direct和Any,选Any吧。
Support for hot-pluggabel devices:热插拔设备支持。支持的不是太好,可不选。
PCMCIA/CardBus support:PCMCIA/CardBus支持。有PCMCIA就必选了。
System V IPC
BSD Process Accounting
Sysctl support:以上三项是有关进程处理/IPC调用的,主要就是System V和BSD两种风格。如果你不是使用BSD,就按照缺省吧。
Power Management support:电源管理支持。
Advanced Power Management BIOS support:高级电源管理BIOD支持。
5. Memory Technology Device(MTD)
MTD设备支持。可不选。
6. Parallel port support
串口支持。如果不打算使用串口,就别选了。
7. Plug and Play configuration
即插即用支持。虽然Linux对即插即用目前支持的不如Windows好,但是还是选上吧,这样你可以拔下鼠标之类的体验一下Linux下即插即用的感觉。
8. Block devices
块设备支持。这个就得针对自己的情况来选了,简单说明一下吧:
Normal PC floppy disk support:普通PC软盘支持。这个应该必选。
XT hard disk support:
Compaq SMART2 support:
Mulex DAC960/DAC1100 PCI RAID Controller support:RAID镜像用的。
Loopback device support:
Network block device support:网络块设备支持。如果想访问网上邻居的东西,就选上。
Logical volume manager(LVM)support:逻辑卷管理支持。
Multiple devices driver support:多设备驱动支持。
RAM disk support:RAM盘支持。
9. Networking options
网络选项。这里配置的是网络协议。内容太多了,不一一介绍了,自己看吧,如果你对网络协议有所了解的话,应该可以看懂的。如果懒得看,使用缺省选项(肯定要选中TCP/IP networking哦)就可以了。让我们看看,TCP/IP、ATM、IPX、DECnet、Appletalk……支持的协议好多哦,IPv6也支持了,Qos and/or fair queueing(服务质量公平调度)也支持了,还有kHTTPd,不过这些都还在实验阶段。
10. Telephony Support
电话支持。这个是什么东东?让我查查帮助,原来是Linux下可以支持电话卡,这样你就可以在IP上使用普通的电话提供语音服务了。记住,电话卡可和modem没有任何关系哦。
11. ATA/IDE/MFM/RLL support
这个是有关各种接口的硬盘/光驱/磁带/软盘支持的,内容太多了,使用缺省的选项吧,如果你使用了比较特殊的设备,比如PCMCIA等,就到里面自己找相应的选项吧。
12. SCSI support
SCSI设备的支持。我没有SCSI的设备,所以根本就不用选,如果你用了SCSI的硬盘/光驱/磁带等设备,自己找好了。
13. IEEE 1394(FireWire)support
这个是什么?低版本的没有见过,看看帮助再说。原来是要Fireware硬件来提高串行总线的性能,我没有,不选了。
14. I2O device support
这个也不清楚,帮助里说是这个需要I2O接口适配器才能支持的,在智能Input/Output(I2O)体系接口中使用,又是要硬件,不选了。
15. Network device support
网络设备支持。上面选好协议了,现在该选设备了,可想而知,内容肯定多得很。还好还好,里面大概分类了,有ARCnet设备、Ethernet(10 or 100 Mbit)、Ethernet(1000Mbit)、Wireless LAN(non-hamradio)、Token Ring device、Wan interfaces、PCMCIA network device support几大类。我用的是10/100M的以太网,看来只需要选则这个了。还是10/100M的以太网设备熟悉,内容虽然多,一眼就可以看到我所用的RealTeck RTL-8139 PCI Fast Ethernet Adapter support,为了免得麻烦,编译到内核里面好了,不选M了,选Y。耐心点,一般说来你都能找到自己用的网卡。如果没有,你只好自己到厂商那里去要驱动了。
16. Amateur Radio support
又一个不懂的,应该是配置业余无线广播的吧,没有,不要了。
17. IrDA(infrared)support
这个要红外支持,免了。
18. ISDN subsystem
如果你使用ISDN上网,这个就必不可少了。自己看着办好了。
19. Old CD-ROM drivers(not SCSI、not IDE)
做的可真周到,原来那些非SCSI/IDE口的光驱谁还在用啊,自己选吧,反正我是用的IDE的CD-ROM,不选这个。
20. Character devices
字符设备。这个内容又太多了,先使用缺省设置,需要的话自己就修改。把大类介绍一下吧:
I2C support:I2C是Philips极力推动的微控制应用中使用的低速串行总线协议。如果你要选择下面的Video For Linux,该项必选。
Mice:鼠标。现在可以支持总线、串口、PS/2、C&T 82C710 mouse port、PC110 digitizer pad,自己根据需要选择。
Joysticks:手柄。即使在Linux下把手柄驱动起来意义也不是太大,游戏太少了。
Watchdog Cards:虽然称为Cards,这个可以用纯软件来实现,当然也有硬件的。如果你把这个选中,那么就会在你的/dev下创建一个名为watchdog的文件,它可以记录你的系统的运行情况,一直到系统重新启动的1分钟左右。有了这个文件,你就可以恢复系统到重启前的状态了。
Video For Linux:支持有关的音频/视频卡。
Ftape, the floppy tape device driver:
PCMCIA character device support:
21. File systems
文件系统。内容又太多了,老法子,在缺省选项的基础上进行修改。介绍以下几项:
Quota support:Quota可以限制每个用户可以使用的硬盘空间的上限,在多用户共同使用一台主机的情况中十分有效。
DOS FAT fs support:DOS FAT文件格式的支持,可以支持FAT16、FAT32。
ISO 9660 CD-ROM file system support:光盘使用的就是ISO 9660的文件格式。
NTFS file system support:ntfs是NT使用的文件格式。
/proc file system support:/proc文件系统是Linux提供给用户和系统进行交互的通道,建议选上,否则有些功能没法正确执行。
还有另外三个大类都规到这儿了:Network File Systems(网络文件系统)、Partition Types(分区类型)、Native Language Support(本地语言支持)。值得一提的是Network File Systems里面的两种:NFS和SMB分别是Linux和Windows相互以网络邻居的形式访问对方所使用的文件系统,根据需要加以选择。
22. Console drivers
控制台驱动。一般使用VGA text console就可以了,标准的80*25的文本控制台。
23. Sound
声卡驱动。如果你能在列表中找到声卡驱动那自然最好,否则就试试OSS了。
24. USB supprot
USB支持。很多USB设备,比如鼠标、调制解调器、打印机、扫描仪等,在Linux都可以得到支持,根据需要自行选择。
25. Kernel hacking
配置了这个,即使在系统崩溃时,你也可以进行一定的工作了。普通用户是用不着这个功能的。
总算配置完了,现在存盘退出,当然你也可以把现在的配置文件保存起来,这样下次再配置的时候就省力气了。
编译
在繁杂的配置工作完成以后,下面你就可以自己到杯茶耐心等候了。与编译有关的命令有如下几个:
#make dep
#make clean
#make zImage
#make bzImage
#make modules
#make modules_install
#depmod -a
第一个命令make dep实际上读取配置过程生成的配置文件,来创建对应于配置的依赖关系树,从而决定哪些需要编译而那些不需要;第二命令make clean完成删除前面步骤留下的文件,以避免出现一些错误;第三个命令make zImage和第四个命令make bzImage实现完全编译内核,二者生成的内核都是使用gzip压缩的,只要使用一个就够了,它们的区别在于使用make bzImage可以生成大一点的内核,比如在编译2.4.0版本的内核时如果使用make zImage命令,那么就会出现system too big的错误提示。建议大家使用make bzImage命令。
后面三个命令只有在你进行配置的过程中,在回答Enable loadable module support (CONFIG_MODULES)时选了"Yes"才是必要的,make modules和make modules_install分别生成相应的模块和把模块拷贝到需要的目录中。
严格说来,第七个命令和编译过程并没有关系,它是生成模块间的依赖关系,这样你启动新内核之后,使用modprobe命令加载模块时就能正确地定位模块。
更新
经过以上的步骤,我们终于得到了新版本的内核。为了能够使用新版本的内核,我们还需要做一些改动:
#cp /usr/src/linux/System.map /boot/System.map-2.4.0test8
#cp /usr/src/linux/arch/i386/bzImage /boot/vmlinuz-2.4.0test8
以上这两个文件是我们刚才编译时新生成的。下面修改/boot下的两个链接System.map和vmlinuz,使其指向新内核的文件:
#cd /boot;rm -f System.map vmlinuz
#ln -s vmlinuz-2.4.0test8 vmlinuz
#ln -s System.map-2.4.0test8 System.map
然后修改/etc/lilo.conf:
#vi /etc/lilo.conf
增加如下一段:
image=/boot/vmlinuz-2.4.0test8
label=linux240
read-only
root=/dev/hda2
其中root=/dev/hda2一行要根据需要自行加以修改。
运行:
#/sbin/lilo -v
确认对/etc/lilo.conf的编辑无误,现在重新启动系统:
#shutdown -r now
在机器重启后出现LILO时按TAB键,输入linux240,新内核发挥作用了。
内核编译菜单中相关选项的意义
==============================
1. prompt for development and/or incomplete code/drivers
很多参考书上说这是那些开发人员认为还不是很稳定的功能,但是根据我的经验,这个是应该选的一个选项,因为现代的LINUX是建立在这些基础上的,所以应该可以回答Y,除非你只是想使用LINUX中已经完全定型的东西,但性能肯定不会好到哪,而且对系统特性的支持也不会好。
2.processor family (386,,486/cx486,586/k5/5x86/6x86,pentinum/k6/tsc,ppro/6x86)
这应该没有太多可说的吧,选择你的CPU的种类,BIOS可以自检得到,注意系统的启动信息。需要注意的是不能选择比你的CPU类型还高级的CPU,否则可能不能正常工作。
3. math emulation
模拟数学协处理器,如果你的机器没有数学协处理器,那就选上以提高性能,但486以后数学协处理器就集成到CPU内部了,应该是用不上的,所以一般的选择是N。当然选上也不会有什么问题,除了内核稍稍变大外。
4. mttr(memory type range register) support
这个选项是用来启动pentinum pro和pentinum II 的特殊功能,如果你用的不是这类CPU就选N,否则也仅仅是使内核变大而已。
5. symmetric multi-processing support
同步处理器支持,如果你有多个CPU就选上吧。
6. enable loadable module support
这会启动动态载入额外模块的功能,所以一定选上。
7. set version information on all symbols for modules
这个选项可以为某个版本的内核而编译的模块在另一个版本的内核下使用,但一般用不上。
8. kernel module loader
如果你启用这个选项,你可以通过kerneld程序的帮助在需要的时候自动载入或卸载那些可载入式的模块。建议选上。
9. networking support
如果你用到任何网络就应该选上
10. pci bios support
这个一般是应该选上的,除非你用没有任何PCI设备的机器。PCIBIOS是用来侦测并启用PCI设备的。
11. pci bridge optimization(v1.3)
当这个选项被启动时,操作系统会对从CPU和系统内存在PCI总线来往的数据做最佳化,这个功能已经完成实验阶段,使用起来应该很安全,而且还可增进系统的效率。
12. system v ipc
起用这个选项可以使内核支持System V 的进程间通信的功能(IPC),有些从System V转移过来的程序会需要这个功能,建议启用该功能。
13. sysctl support
除非你的内存少的可怜,否则你应该启动这个功能,启用该选项后内核会大8K,但能让你直接改变内核的参数而不必重新开机。
14. kernel support for elf binaries
该选项让你的系统得以执行用ELF格式存储的可执行文件,而ELF是现代LINUX的可执行文件、目标文件和系统函数库的标准格式。当操作系统要和编译器以及连接器合作时会需要这些标准,所以应该回答Y。
15. compile kernel as elf
这选项让你的内核本身以ELF的格式编译,如果你的系统上的过程gcc默认产生ELF格式的可执行文件,那么你就应该启动这个选项。先看看你的编译器的版本再决定。
16. parallel port support
如果你有任何并行口的设备并且想让LINUX使用,那么就可以启用这个选项。LINUX不仅可以使用并口的打印机,还可以支持PLIP(一种为并行口而设计的网络通讯协定),ZIP磁盘驱动器、扫描仪等。在大多情况下,你需要额外的驱动程序才能使用外接的并口设备。
17. plug and play support
支持PNP设备并非Microsoft的专利,如果你要让LINUX也支持PNP设备,只要启用该选项就可以,但有些情况下会和其他设备产生冲突(I/O,DMA,IRQ等)。这个选项对PCI设备没有影响,因为他们天生就是PNP设备。
18. normal floppy disk support
除非你不想在LINUX下使用软盘,否则就应该回答Y。但对于一些不需要支持软盘驱动器的系统而言,这个选项可以节省一些内存。
19. enhanced ide/mfm/dll disk support
除非你不需要MFM/DLL/IDE硬盘的的支持,否则就应该回答Y,但如果你只有SCSI的硬盘,关掉这个选项会比较安全。
20. enhanced ide/mfm/dll cdrom support
和上面的类似,只不过是对CDROM的支持而已。
21. enhanced ide/mfm/dll tape support
一般没有多少人在用磁带机吧,所以回答N是比较好的答案。
22. enhanced ide/mfm/dll floppy support
这个设备好象一般也没有人用,所以也可以回答N。
23. xt harddisk support
如果你有这种石器时代的XT硬盘,那么恭喜你你可以用上他了。
24. parallel port ide device support
LINUX是支持这种很新的并口的IDE设备的,如果你有的话就用上吧。
25. networking options
如果你在前面选了支持网络的话,在这里会回答很多问题。除非你有特别的需求,否则使用默认的选项应该就足够了。
26. scsi support
如果你有任何一种SCSI控制卡,这个选项就应该回答Y。事先搞清楚你的硬件的类型,因为这些问题都是针对特定的SCSI控制芯片和控制卡的,如果你不确定自己使用的是哪一种,查看你的硬件的说明文件或者LINUX的HOWTO文档。同样也会让你回答很多SCSI设备的支持(硬盘、CDROM、Tape、floppy等),依据你的情况选择。如果你没有SCSI设备的话,建议不要支持,因为这会节约很多内核空间。
27. network device support
这里面有很多关于网络控制卡的问题,如果你无法确定如何正确选择,查看硬件文档或LINUX HOWTO文档。
28. amateur radio support
这个选项可以用来启动无线网络的基本支持,目前的无线网络可以通过公众频率传输数据,如果你有此类设备就可以启用,具体请参考AX25和HAM HOWTO 文档。
29. isdn subsystem
如果你有ISDN硬件就应该启用该选项并安装合适的硬件驱动程序,你应该还会需要启用Support synchronous PPP选项(参考PPP overISDN)。
30. old cd-rom drivers
这是针对一些特殊光盘驱动器程序的问题,如果你有IDE或SCSI的CDROM控制卡,那么就不用启用该选项了。
31. character devices
LINUX支持很多特殊的字符设备,例如并口、串口控制卡、QIC02磁带驱动器以及特定界面的鼠标,此外对于游戏杆和影象摄取和麦克等也在这里面,依据你自己的情况选者吧。
32. filesystems
这是一系列内核所支持的各文件系统的问题,对ext2 /proc文件系统是一定应该支持的,有光驱还应该支持ISO9660(或模块支持),有WINDOWS或DOS分区并且想在LINUX下访问他们也可以进行支持。
33. console drivers
你至少应该支持VGA text console,否则你无法从控制台使用LINUX。
34. sound card support
在这里回答Y会出现很多关于声卡的问题,根据你自己的情况来配置。
35. kernel profiling support(v1.3)
这个选项可以开启内核做效率统计的功能,并且会提供其他在为系统侦错时有用的信息。这些功能会需要付出一些代价并造成系统执行得较为缓慢,除非你正在研究内核的某个问题,否则你应该回答N。
36. kernel hacking
如果你正打算深入研究自己系统上运行的LINUX如何运作,这里有很多选项,但一般没有必要的话可以全部关掉。
Linux 内核配置系统浅析
===========================================================================
随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 Linux 内核级的开发中。面对日益庞大的 Linux 内核源代码,开发者在完成自己的内核代码后,都将面临着同样的问题,即如何将源代码融入到 Linux 内核中,增加相应的 Linux 配置选项,并最终被编译进 Linux 内核。这就需要了解 Linux 的内核配置系统。
众所周知,Linux 内核是由分布在全球的 Linux 爱好者共同开发的,Linux 内核每天都面临着许多新的变化。但是,Linux 内核的组织并没有出现混乱的现象,反而显得非常的简洁,而且具有很好的扩展性,开发人员可以很方便的向 Linux 内核中增加新的内容。原因之一就是 Linux 采用了模块化的内核配置系统,从而保证了内核的扩展性。
本文首先分析了 Linux 内核中的配置系统结构,然后,解释了 Makefile 和配置文件的格式以及配置语句的含义,最后,通过一个简单的例子--TEST Driver,具体说明如何将自行开发的代码加入到 Linux 内核中。在下面的文章中,不可能解释所有的功能和命令,只对那些常用的进行解释,至于那些没有讨论到的,请读者参考后面的参考文献。
1.配置系统的基本结构
Linux内核的配置系统由三个部分组成,分别是:
Makefile:分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则;
配置文件(config.in):给用户提供配置选择的功能;
配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和 make xconfig)。
这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以。所以,在本文中,我们只对 Makefile 和配置文件进行讨论。另外,凡是涉及到与具体 CPU 体系结构相关的内容,我们都以 ARM 为例,这样不仅可以将讨论的问题明确化,而且对内容本身不产生影响。
2. Makefile
2.1 Makefile 概述
Makefile 的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成 Linux 内核二进制文件。
由于 Linux 内核源代码是按照树形结构组织的,所以 Makefile 也被分布在目录树中。Linux 内核中的 Makefile 以及与 Makefile 直接相关的文件有:
Makefile:顶层 Makefile,是整个内核配置、编译的总体控制文件。
.config:内核配置文件,包含由用户选择的配置选项,用来存放内核配置后的结果(如 make menuconfig)。
arch/*/Makefile:位于各种 CPU 体系目录下的 Makefile,如 arch/arm/Makefile,是针对特定平台的 Makefile。
各个子目录下的 Makefile:比如 drivers/Makefile,负责所在子目录下源代码的管理。
Rules.make:规则文件,被所有的 Makefile 使用。
用户通过 make menuconfig 配置后,产生了 .config。顶层 Makefile 读入 .config 中的配置选择。顶层 Makefile 有两个主要的任务:产生 vmlinux 文件和内核模块(module)。为了达到此目的,顶层 Makefile 递归的进入到内核的各个子目录中,分别调用位于这些子目录中的 Makefile。至于到底进入哪些子目录,取决于内核的配置。
在顶层 Makefile 中,有一句:include arch/$(ARCH)/Makefile,包含了特定 CPU 体系结构下的 Makefile,这个 Makefile 中包含了平台相关的信息。
位于各个子目录下的 Makefile 同样也根据 .config 给出的配置信息,构造出当前配置下需要的源文件列表,并在文件的最后有 include $(TOPDIR)/Rules.make。
Rules.make 文件起着非常重要的作用,它定义了所有 Makefile 共用的编译规则。比如,如果需要将本目录下所有的 c 程序编译成汇编代码,需要在 Makefile 中有以下的编译规则:
%.s: %.c
$(CC) $(CFLAGS) -S $< -o $@
有很多子目录下都有同样的要求,就需要在各自的 Makefile 中包含此编译规则,这会比较麻烦。而 Linux 内核中则把此类的编译规则统一放置到 Rules.make 中,并在各自的 Makefile 中包含进了 Rules.make(include Rules.make),这样就避免了在多个 Makefile 中重复同样的规则。对于上面的例子,在 Rules.make 中对应的规则为:
%.s: %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@
2.2 Makefile 中的变量
顶层 Makefile 定义并向环境中输出了许多变量,为各个子目录下的 Makefile 传递一些信息。有些变量,比如 SUBDIRS,不仅在顶层 Makefile 中定义并且赋初值,而且在 arch/*/Makefile 还作了扩充。
常用的变量有以下几类:
1)版本信息
版本信息有:VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSION,KERNELRELEASE。版本信息定义了当前内核的版本,比如 VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它们共同构成内核的发行版本KERNELRELEASE:2.4.18-rmk7
2) CPU 体系结构:ARCH
在顶层 Makefile 的开头,用 ARCH 定义目标 CPU 的体系结构,比如 ARCH:=arm 等。许多子目录的 Makefile 中,要根据 ARCH 的定义选择编译源文件的列表。
3)路径信息:TOPDIR, SUBDIRS
TOPDIR 定义了 Linux 内核源代码所在的根目录。例如,各个子目录下的 Makefile 通过 $(TOPDIR)/Rules.make 就可以找到 Rules.make 的位置。
SUBDIRS 定义了一个目录列表,在编译内核或模块时,顶层 Makefile 就是根据 SUBDIRS 来决定进入哪些子目录。SUBDIRS 的值取决于内核的配置,在顶层 Makefile 中 SUBDIRS 赋值为 kernel drivers mm fs net ipc lib;根据内核的配置情况,在 arch/*/Makefile 中扩充了 SUBDIRS 的值,参见4)中的例子。
4)内核组成信息:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
Linux 内核文件 vmlinux 是由以下规则产生的:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o /
--start-group /
$(CORE_FILES) /
$(DRIVERS) /
$(NETWORKS) /
$(LIBS) /
--end-group /
-o vmlinux
可以看出,vmlinux 是由 HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS 和 LIBS 组成的。这些变量(如 HEAD)都是用来定义连接生成 vmlinux 的目标文件和库文件列表。其中,HEAD在arch/*/Makefile 中定义,用来确定被最先链接进 vmlinux 的文件列表。比如,对于 ARM 系列的 CPU,HEAD 定义为:
HEAD := arch/arm/kernel/head-$(PROCESSOR).o /
arch/arm/kernel/init_task.o
表明 head-$(PROCESSOR).o 和 init_task.o 需要最先被链接到 vmlinux 中。PROCESSOR 为 armv 或 armo,取决于目标 CPU。 CORE_FILES,NETWORK,DRIVERS 和 LIBS 在顶层 Makefile 中定义,并且由 arch/*/Makefile 根据需要进行扩充。 CORE_FILES 对应着内核的核心文件,有 kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,这些是组成内核最为重要的文件。同时,arch/arm/Makefile 对 CORE_FILES 进行了扩充:
# arch/arm/Makefile
# If we have a machine-specific directory, then include it in the build.
MACHDIR := arch/arm/mach-$(MACHINE)
ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))
SUBDIRS += $(MACHDIR)
CORE_FILES := $(MACHDIR)/$(MACHINE).o $(CORE_FILES)
endif
HEAD := arch/arm/kernel/head-$(PROCESSOR).o /
arch/arm/kernel/init_task.o
SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe
CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
LIBS := arch/arm/lib/lib.a $(LIBS)
5)编译信息:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
在 Rules.make 中定义的是编译的通用规则,具体到特定的场合,需要明确给出编译环境,编译环境就是在以上的变量中定义的。针对交叉编译的要求,定义了 CROSS_COMPILE。比如:
CROSS_COMPILE = arm-linux-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
......
CROSS_COMPILE 定义了交叉编译器前缀 arm-linux-,表明所有的交叉编译工具都是以 arm-linux- 开头的,所以在各个交叉编译器工具之前,都加入了 $(CROSS_COMPILE),以组成一个完整的交叉编译工具文件名,比如 arm-linux-gcc。
CFLAGS 定义了传递给 C 编译器的参数。
LINKFLAGS 是链接生成 vmlinux 时,由链接器使用的参数。LINKFLAGS 在 arm/*/Makefile 中定义,比如:
# arch/arm/Makefile
LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds
6)配置变量CONFIG_*
.config 文件中有许多的配置变量等式,用来说明用户配置的结果。例如 CONFIG_MODULES=y 表明用户选择了 Linux 内核的模块功能。
.config 被顶层 Makefile 包含后,就形成许多的配置变量,每个配置变量具有确定的值:y 表示本编译选项对应的内核代码被静态编译进 Linux 内核;m 表示本编译选项对应的内核代码被编译成模块;n 表示不选择此编译选项;如果根本就没有选择,那么配置变量的值为空。
2.3 Rules.make 变量
前面讲过,Rules.make 是编译规则文件,所有的 Makefile 中都会包括 Rules.make。Rules.make 文件定义了许多变量,最为重要是那些编译、链接列表变量。
O_OBJS,L_OBJS,OX_OBJS,LX_OBJS:本目录下需要编译进 Linux 内核 vmlinux 的目标文件列表,其中 OX_OBJS 和 LX_OBJS 中的 "X" 表明目标文件使用了 EXPORT_SYMBOL 输出符号。
M_OBJS,MX_OBJS:本目录下需要被编译成可装载模块的目标文件列表。同样,MX_OBJS 中的 "X" 表明目标文件使用了 EXPORT_SYMBOL 输出符号。
O_TARGET,L_TARGET:每个子目录下都有一个 O_TARGET 或 L_TARGET,Rules.make 首先从源代码编译生成 O_OBJS 和 OX_OBJS 中所有的目标文件,然后使用 $(LD) -r 把它们链接成一个 O_TARGET 或 L_TARGET。O_TARGET 以 .o 结尾,而 L_TARGET 以 .a 结尾。
2.4 子目录 Makefile
子目录 Makefile 用来控制本级目录以下源代码的编译规则。我们通过一个例子来讲解子目录 Makefile 的组成:
#
# Makefile for the linux kernel.
#
# All of the (potential) objects that export symbols.
# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
export-objs := tc.o
# Object file lists.
obj-y :=
obj-m :=
obj-n :=
obj- :=
obj-$(CONFIG_TC) += tc.o
obj-$(CONFIG_ZS) += zs.o
obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.o
# Files that are both resident and modular: remove from modular.
obj-m := $(filter-out $(obj-y), $(obj-m))
# Translate to Rules.make lists.
L_TARGET := tc.a
L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
LX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
include $(TOPDIR)/Rules.make
a) 注释
对 Makefile 的说明和解释,由#开始。
编译目标定义
类似于 obj-$(CONFIG_TC) += tc.o 的语句是用来定义编译的目标,是子目录 Makefile 中最重要的部分。编译目标定义那些在本子目录下,需要编译到 Linux 内核中的目标文件列表。为了只在用户选择了此功能后才编译,所有的目标定义都融合了对配置变量的判断。
前面说过,每个配置变量取值范围是:y,n,m 和空,obj-$(CONFIG_TC) 分别对应着 obj-y,obj-n,obj-m,obj-。如果 CONFIG_TC 配置为 y,那么 tc.o 就进入了 obj-y 列表。obj-y 为包含到 Linux 内核 vmlinux 中的目标文件列表;obj-m 为编译成模块的目标文件列表;obj-n 和 obj- 中的文件列表被忽略。配置系统就根据这些列表的属性进行编译和链接。
export-objs 中的目标文件都使用了 EXPORT_SYMBOL() 定义了公共的符号,以便可装载模块使用。在 tc.c 文件的最后部分,有 "EXPORT_SYMBOL(search_tc_card);",表明 tc.o 有符号输出。
这里需要指出的是,对于编译目标的定义,存在着两种格式,分别是老式定义和新式定义。老式定义就是前面 Rules.make 使用的那些变量,新式定义就是 obj-y,obj-m,obj-n 和 obj-。Linux 内核推荐使用新式定义,不过由于 Rules.make 不理解新式定义,需要在 Makefile 中的适配段将其转换成老式定义。
c) 适配段
适配段的作用是将新式定义转换成老式定义。在上面的例子中,适配段就是将 obj-y 和 obj-m 转换成 Rules.make 能够理解的 L_TARGET,L_OBJS,LX_OBJS,M_OBJS,MX_OBJS。
L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) 定义了 L_OBJS 的生成方式:在 obj-y 的列表中过滤掉 export-objs(tc.o),然后排序并去除重复的文件名。这里使用到了 GNU Make 的一些特殊功能,具体的含义可参考 Make 的文档(info make)。
d) include $(TOPDIR)/Rules.make
3.配置文件
3.1 配置功能概述
除了 Makefile 的编写,另外一个重要的工作就是把新功能加入到 Linux 的配置选项中,提供此项功能的说明,让用户有机会选择此项功能。所有的这些都需要在 config.in 文件中用配置语言来编写配置脚本,
在 Linux 内核中,配置命令有多种方式:
配置命令解释脚本
Make config, make oldconfig scripts/Configure
Make menuconfig scripts/Menuconfig
Make xconfig scripts/tkparse
以字符界面配置(make config)为例,顶层 Makefile 调用 scripts/Configure,按照 arch/arm/config.in 来进行配置。命令执行完后产生文件 .config,其中保存着配置信息。下一次再做 make config 将产生新的 .config 文件,原 .config 被改名为 .config.old
3.2 配置语言
1)顶层菜单
mainmenu_name /prompt/ /prompt/ 是用'或"包围的字符串,'与"的区别是'…'中可使用$引用变量的值。mainmenu_name 设置最高层菜单的名字,它只在 make xconfig 时才会显示。
2)询问语句
bool /prompt/ /symbol/
hex /prompt/ /symbol/ /word/
int /prompt/ /symbol/ /word/
string /prompt/ /symbol/ /word/
tristate /prompt/ /symbol/
询问语句首先显示一串提示符 /prompt/,等待用户输入,并把输入的结果赋给 /symbol/ 所代表的配置变量。不同的询问语句的区别在于它们接受的输入数据类型不同,比如 bool 接受布尔类型( y 或 n ),hex 接受 16 进制数据。有些询问语句还有第三个参数 /word/,用来给出缺省值。
3)定义语句
define_bool /symbol/ /word/
define_hex /symbol/ /word/
define_int /symbol/ /word/
define_string /symbol/ /word/
define_tristate /symbol/ /word/
不同于询问语句等待用户输入,定义语句显式的给配置变量 /symbol/ 赋值 /word/。
4)依赖语句
dep_bool /prompt/ /symbol/ /dep/ ...
dep_mbool /prompt/ /symbol/ /dep/ ...
dep_hex /prompt/ /symbol/ /word/ /dep/ ...
dep_int /prompt/ /symbol/ /word/ /dep/ ...
dep_string /prompt/ /symbol/ /word/ /dep/ ...
dep_tristate /prompt/ /symbol/ /dep/ ...
与询问语句类似,依赖语句也是定义新的配置变量。不同的是,配置变量/symbol/的取值范围将依赖于配置变量列表/dep/ …。这就意味着:被定义的配置变量所对应功能的取舍取决于依赖列表所对应功能的选择。以dep_bool为例,如果/dep/ …列表的所有配置变量都取值y,则显示/prompt/,用户可输入任意的值给配置变量/symbol/,但是只要有一个配置变量的取值为n,则/symbol/被强制成n。
不同依赖语句的区别在于它们由依赖条件所产生的取值范围不同。
5)选择语句
choice /prompt/ /word/ /word/
choice 语句首先给出一串选择列表,供用户选择其中一种。比如 Linux for ARM 支持多种基于 ARM core 的 CPU,Linux 使用 choice 语句提供一个 CPU 列表,供用户选择:
choice 'ARM system type' /
"Anakin CONFIG_ARCH_ANAKIN /
Archimedes/A5000 CONFIG_ARCH_ARCA5K /
Cirrus-CL-PS7500FE CONFIG_ARCH_CLPS7500 /
……
SA1100-based CONFIG_ARCH_SA1100 /
Shark CONFIG_ARCH_SHARK" RiscPC
Choice 首先显示 /prompt/,然后将 /word/ 分解成前后两个部分,前部分为对应选择的提示符,后部分是对应选择的配置变量。用户选择的配置变量为 y,其余的都为 n。
6) if语句
if [ /expr/ ] ; then
/statement/
...
fi
if [ /expr/ ] ; then
/statement/
...
else
/statement/
...
fi
if 语句对配置变量(或配置变量的组合)进行判断,并作出不同的处理。判断条件 /expr/ 可以是单个配置变量或字符串,也可以是带操作符的表达式。操作符有:=,!=,-o,-a 等。
7)菜单块(menu block)语句
mainmenu_option next_comment
comment '…..'
…
endmenu
引入新的菜单。在向内核增加新的功能后,需要相应的增加新的菜单,并在新菜单下给出此项功能的配置选项。Comment 后带的注释就是新菜单的名称。所有归属于此菜单的配置选项语句都写在 comment 和 endmenu 之间。
8) Source 语句
source /word/
/word/ 是文件名,source 的作用是调入新的文件。
3.3 缺省配置
Linux 内核支持非常多的硬件平台,对于具体的硬件平台而言,有些配置就是必需的,有些配置就不是必需的。另外,新增加功能的正常运行往往也需要一定的先决条件,针对新功能,必须作相应的配置。因此,特定硬件平台能够正常运行对应着一个最小的基本配置,这就是缺省配置。
Linux 内核中针对每个 ARCH 都会有一个缺省配置。在向内核代码增加了新的功能后,如果新功能对于这个 ARCH 是必需的,就要修改此 ARCH 的缺省配置。修改方法如下(在 Linux 内核根目录下):
备份 .config 文件
cp arch/arm/deconfig .config
修改 .config
cp .config arch/arm/deconfig
恢复 .config
如果新增的功能适用于许多的 ARCH,只要针对具体的 ARCH,重复上面的步骤就可以了。
3.4 help file
大家都有这样的经验,在配置 Linux 内核时,遇到不懂含义的配置选项,可以查看它的帮助,从中可得到选择的建议。下面我们就看看如何给给一个配置选项增加帮助信息。
所有配置选项的帮助信息都在 Documentation/Configure.help 中,它的格式为:
<description>
<variable name>
<help file>
<description> 给出本配置选项的名称,<variable name> 对应配置变量,<help file> 对应配置帮助信息。在帮助信息中,首先简单描述此功能,其次说明选择了此功能后会有什么效果,不选择又有什么效果,最后,不要忘了写上"如果不清楚,选择 N(或者)Y",给不知所措的用户以提示。
4.实例
对于一个开发者来说,将自己开发的内核代码加入到 Linux 内核中,需要有三个步骤。首先确定把自己开发代码放入到内核的位置;其次,把自己开发的功能增加到 Linux 内核的配置选项中,使用户能够选择此功能;最后,构建子目录 Makefile,根据用户的选择,将相应的代码编译到最终生成的 Linux 内核中去。下面,我们就通过一个简单的例子--test driver,结合前面学到的知识,来说明如何向 Linux 内核中增加新的功能。
4.1 目录结构
test driver 放置在 drivers/test/ 目录下:
$cd drivers/test
$tree
.
|-- Config.in
|-- Makefile
|-- cpu
| |-- Makefile
| `-- cpu.c
|-- test.c
|-- test_client.c
|-- test_ioctl.c
|-- test_proc.c
|-- test_queue.c
`-- test
|-- Makefile
`-- test.c
4.2 配置文件
1) drivers/test/Config.in
#
# TEST driver configuration
#
mainmenu_option next_comment
comment 'TEST Driver'
bool 'TEST support' CONFIG_TEST
if [ "$CONFIG_TEST" = "y" ]; then
tristate 'TEST user-space interface' CONFIG_TEST_USER
bool 'TEST CPU ' CONFIG_TEST_CPU
fi
endmenu
由于 test driver 对于内核来说是新的功能,所以首先创建一个菜单 TEST Driver。然后,显示 "TEST support",等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y),则进一步显示子功能:用户接口与 CPU 功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了 tristate(因为 tristate 的取值范围包括 y、n 和 m,m 就是对应着模块)。
2) arch/arm/config.in
在文件的最后加入:source drivers/test/Config.in,将 TEST Driver 子功能的配置纳入到 Linux 内核的配置中。
4.3 Makefile
1)drivers/test/Makefile
# drivers/test/Makefile
#
# Makefile for the TEST.
#
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS) cpu
L_TARGET := test.a
export-objs := test.o test_client.o
obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o
obj-$(CONFIG_TEST_USER) += test_ioctl.o
obj-$(CONFIG_PROC_FS) += test_proc.o
subdir-$(CONFIG_TEST_CPU) += cpu
include $(TOPDIR)/Rules.make
clean:
for dir in $(ALL_SUB_DIRS); do make -C $$dir clean; done
rm -f *.[oa] .*.flags
drivers/test 目录下最终生成的目标文件是 test.a。在 test.c 和 test-client.c 中使用了 EXPORT_SYMBOL 输出符号,所以 test.o 和 test-client.o 位于 export-objs 列表中。然后,根据用户的选择(具体来说,就是配置变量的取值),构建各自对应的 obj-* 列表。由于 TEST Driver 中包一个子目录 cpu,当 CONFIG_TEST_CPU=y(即用户选择了此功能)时,需要将 cpu 目录加入到 subdir-y 列表中。
2)drivers/test/cpu/Makefile
# drivers/test/test/Makefile
#
# Makefile for the TEST CPU
#
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS)
L_TARGET := test_cpu.a
obj-$(CONFIG_test_CPU) += cpu.o
include $(TOPDIR)/Rules.make
clean:
rm -f *.[oa] .*.flags
3)drivers/Makefile
……
subdir-$(CONFIG_TEST) += test
……
include $(TOPDIR)/Rules.make
在 drivers/Makefile 中加入 subdir-$(CONFIG_TEST)+= test,使得在用户选择 TEST Driver 功能后,内核编译时能够进入 test 目录。
4)Makefile
……
DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o
DRIVERS-$(CONFIG_TEST) += drivers/test/test.a
DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a
DRIVERS := $(DRIVERS-y)
……
在顶层 Makefile 中加入 DRIVERS-$(CONFIG_TEST) += drivers/test/test.a 和 DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a。如何用户选择了 TEST Driver,那么 CONFIG_TEST 和 CONFIG_TEST_CPU 都是 y,test.a 和 test_cpu.a 就都位于 DRIVERS-y 列表中,然后又被放置在 DRIVERS 列表中。在前面曾经提到过,Linux 内核文件 vmlinux 的组成中包括 DRIVERS,所以 test.a 和 test_cpu.a 最终可被链接到 vmlinux 中。