编译Linux内核
编译Linux内核
以下内容由 xgfone
整理而成。
在编译Linux内核源码时,建议不要使用具有管理权限的账号(比如:root账号),用普通账号即可,因为编译内核不需要管理员权限,由于普通账号不具有管理权限,所以其操作不会损坏系统。
注:以下某些部分不适合低于 2.6
版本的Linux内核,比如:模块编译。另外,现在内核已经发展到 3.X
版本,但原理是一样的。
1、获得Linux内核源码,并进入其主目录
(1) 下载Linux内核源码
Linux内核的官方网址是 http://www.kernel.org/pub/linux/kernel/ 。
注:也可以从其他地方获得Linux内核源码,只要获得Linux内核源码即可。
(2) 解压Linux内核源码
$ gzip -cd linux-2.6.XX.tar.gz | tar xvf -
or
$ bzip2 -dc linux-2.6.XX.tar.bz2 | tar xvf -
以上是通用格式,如果是GNU tar
,还可以直接使用GNU tar
解压:
$ tar -xvzf linux-2.6.XX.tar.gz [-C SRC_DIR]
or
$ tar -xvjf linux-2.6.XX.tar.bz2 [-C SRC_DIR]
其中,SRC_DIR
是解压后的源码所放置的目录;如果没有-C
选项,则解压到当前目录。
注:有很多介绍“编译Linux内核
”的教材或文章都把解压后的Linux内核源码放到 /usr/src/linux
目录中,但是,在Linux内核源码的帮助文档中,Linux官方建议不要使用该目录。其实,在编译Linux内核源码时,在任何目录中都是可以的。
(3) 进入主目录
$ cd SRC_DIR/linux-2.6.XX
or
$ cd linux-2.6.XX
注:以上命令要根据自己的当前目录来确定怎么使用。
2、给Linux内核打补丁(可选)
如果linux-2.6.XX
版本正是我们所要的,这一步可以略过。
如果我们需要的是linux-2.6.XX.2
,则还需要给我们下载的linux-2.6.XX
版本打上 patch-2.6.XX.2.gz
补丁。
$ gzip -cd linux-2.6.XX.2.gz | patch -p1
or
$ gzip2 -dc linux-2.6.XX.2.bz2 | patch -p1
如果我们的Linux内核源码的版本是 linux-2.6.XX.1
,如果想用 linux-2.6.XX.2
,但又没有 linux-2.6.XX.2
的内核源码,只有 patch-2.6.XX.1
和 patch-2.6.XX.2
两个补丁,则我们不能在 linux-2.6.XX.1
上直接使用 patch-2.6.XX.2
这个补丁,而需要先用 patch-2.6.XX.1
补丁把我们的内核版本从 linux-2.6.XX.1
还原到 linux-2.6.XX
,再用 patch-2.6.XX.2
把 linux-2.6.XX
升级成 linux-2.6.XX.2
。这样做的原因是,2.6
版本的linux内核补丁都是基于 linux-2.6.XX
的,所以,只能应用到 linux-2.6.XX
上,不能应用到 linux-2.6.XX.YY
上。
3、移植Linux内核(可选)
如果我们下载的或已经打过补丁的Linux内核正是我们所想要的,即支持我们的硬件,那么可以跳过这一步。
一般来说,在我们的台式机(如常见的X86)上,我们不需要对Linux内核进行移植,可以略过这一步。
如果我们的Linux内核不支持我们的硬件,那么我们就需要对Linux内核进行移植。在大多数的Linux嵌入式中,经常须要移植Linux内核。
移植Linux内核需要修改Linux内核源码。
4、清除残留的信息(可选)
如果是刚刚下载的Linux内核源码并且没有执行过在编译Linux内核过程中所使用的任何一个make
命令,则可以跳过这一步。
如果在当前的Linux内核源码中,已经执行过在编译Linux内核过程中所使用的任何一个make
命令,并且想再次编译该Linux内核源码,则必须要执行这一步,不然,在编译的过程中,可能会遇到各种各样的小问题。
清除残留信息的过程很简单,只需要一个命令即可:
$ make mrproper
在某些讲述如何编译Linux内核的教程中,很多教程都使用Makefile
文件中的distclean
选项,即使用命令:
$ make distclean
其实,这两个命令使用哪个都行。而且,使用后者时,清除的内容将会更多的,因为distclean
选项所做的清除内容包含了mrproper
选项所做的清除内容;换句话说,就是当使用distclean
选项时,相当于也执行了mrproper
选项,同时也执行了其他的一些清除工作。但是,在Linux内核源码的README文档中,在清除残留信息时,提到的是mrproper
选项,而没有提到distclean
选项。
5、生成.config配置文件
在执行make命令进行编译Linux内核时,我们需要.config
配置文件,这个文件是指导Linux内核如何来编译Linux内核(这是Linux为了跨平台而设计的)。
.config
是一个很大、很复杂的文件,从头开始配置是很麻烦的,所以,在编译Linux时,我们一般都会从其它的地方拷贝一个现成的.config
到我们的Linux内核源码的主目录中。然后执行“make config
”命令进行配置我们自己的.config
文件。
“make config
”命令提供一个淳朴的命令行界面,我们一般不会使用它,可以使用其它能提供图形界面的配置命令:
make memuconfig 提供一个基于curses的命令行界面(需要curses库的支持)
make nconfig 提供一个基于ncurses(下一代curses)的命令行界面(需要ncurses库的支持)
make xconfig 基于QT的X Window图形化界面
make gconfig 基于GTK+的X Window图形化界面
make oldconfig 新配置的.config文件基于已经存在的.config,已经存在的.config中的值在新的.config文件中都成为默认值。
make silentoldconfig 同“make oldconfig”,但是该命令会避免被已经回答的问题弄乱屏幕,另外还会更新依赖。
make allyesconfig 在创建.config配置文件时,会尽可能选择“y”。
make allmodconfig 在创建.config配置文件时,会尽可能选择“m”。
make allnoconfig 在创建.config配置文件时,会尽可能选择“n”。
make randconfig 在创建.config配置文件时,会对各个选项进行随机设置(随机选择“y”、“m”、“n”)。
make defconfig 使用各架构下默认的.config文件(位于arch/<arch>/configs/*_defconfig)。
在执行这些命令中的任何一个时,对界面中的每个选项(即每个模块),可以选择“y
”、“m
”或“n
”三个中的任何一个。
“y”选项要求make把该模块编译进Linux内核中(这会增大内核的大小);
“m”选项要求make把该模块编译成一个“模块”,该模块可以被动态地加载进Linux内核或从Linux内核中裁掉;
“n”选项要求make跳过该模块而不用编译它。
注:在选择如何编译内核时,一般有三种类型:方括号
、尖括号
和圆括号
。
如果出现的是方括号,只能选择“Y”或“N”,而且“Y”用“*”来代替(显示在方括号中),“N”用“ ”(空)来代替(显示在方括号中)。
如果出现的是尖括号,则可以选择“Y”、“N”或“M”,而且“Y”用“*”来代替(显示在尖括号中),“N”用“ ”(空)来代替(显示在尖括号中),
“M”还是用“M”在尖括号中显示。
如果是出现的是圆括号,则允许输入选项内容。
6、解决各模块间的依赖关系(可选)
如果是编译2.6
内核(及其以后版本)的话,这一步可以跳过。
主要是早期的Linux内核(比如:2.4
版本)需要执行这一步,这一步能够解决各模块间的依赖关系,以及确保关键文件的正确位置。
执行的命令是:
$ make dep
7、编译Linux内核源码
make
命令会生成相应的Linux内核镜像。
$ make [all]
make
命令会根据.config
配置文件来编译Linux内核。此命令会编译默认的目标,但不同的平台、体系,其默认的目标不同,这需要查看该平台、体系下的Makefile
文件(位于arch/$(ARCH)/Makefile
)。其实,Linux官方已经帮我们默认地设计好了即要生成的目标文件,我们按照默认的即可。在Linux内核中,执行make
和make all
是一样的。如果想加快、节约编译时间,可以在make
命令中添加 -j N
选项,用来指定make
的并发性,其中的 N
为数字,可以设置处理器的数量,一般设置成处理器的个数的两倍。
当我们需要特定的目标时,我们可以在make
命令后明确地指定。比如:默认生成的是目标是zImage
,但我们想要生成bzImage
,则可以使用命令“make bzImage
”。但是,在明确地指定生成目标时,请先查看该平台、体系下Makefile
文件是否有该目标(在查看时,可以从Makefile
文件的规则和名为“archhelp
”的命令包中查看,其中“archhelp
”可以看成是帮助文档);如果没有的话,就算明确指明,也不能生成该目标。
不过,对于大部分平台、体系的计算机来说,一般都会先编译成vmlinux
目标,然后再把vmlinux
目标转换成其它形式的目标。
注:
(1) 一般能生成的目标有:vmlinux
、Image
、zImage
、bzImage
、uImage
等等,且后三者在ARM嵌入式芯片上用的最多。
(2) 在编译上述指定的目标时,make
只编译选项为“Y
”的模块,不会编译选项为“M
”或“N
”的模块。如果想编译选项为“M
”的模块,须要指定目标“modules
”。
只编译选项为“M
”的模块,但不编译选项为“Y
”的模块:
1> 编译Linux内核源码中所有选项为“M”的模块:
首先切换当前目录到Linux内核源码树的主目录,然后执行命令即可: $ make modules
注:此命令可以把Linux内核源码树中所有选项为“M”的模块编译成可动态加载、卸载的模块;
另外,用此命令编译的模块必须都是在Linux内核源码树内的。
2> 只编译Linux内核源码树中的某一个模块:
如果不想编译Linux内核源码树中所有的选项为“M”的模块,则也可以只单独编译其中的一个模块。
此时,首先须要先切换当前目录到Linux内核源码树的主目录中,然后以下命令即可:$ make SUBDIR=MODULE_DIR modules
其中,MODULE_DIR为所要编译的模块的主目录。
3> 编译在Linux内核源码树外的模块:
首先切换当前目录到要编译的模块的主目录内,然后执行以下命令即可:$ make -C $(KERNEL_DIR) M=$(pwd) modules
其中,KERNEL_DIR是Linux内核源码树的主目录, 而$(pwd)则是当前即要编译的模块的主目录(即当前目录),
pwd 为Shell下的命令(打印当前路径的全称),$(pwd)表示替换pwd命令的输出。
注:此时编译的模块虽然是在Linux内核源码树的外部,但也要遵循Linux内核源码的大框架,即在其主目录下,
要有一个Makefile文件来指导make编译模块,但不一定要有Kconfig文件(既然不在Linux内核源码树中,
所以一般也不需要)。
说明:由此可见,编译内核时分为两种编译:只编译选项为“Y
”的模块(即把模块编译进内核中)和只编译选项为“M
”的模块(即把模块编译成可动态加载、卸载的模块形式)。
8、安装编译后所生成的Linux内核(可选)
对于不同的平台,这一步的执行不同,甚至可以省略。比如:如果编译的是嵌入式Linux内核源码,这一步就可以省略。如果是X86平台、体系,并且想要安装刚刚编译的内核的话,则这一步是需要的。在X86下,安装编译后的Linux镜像的命令是:
# make install
相对于Linux内核的编译,Linux内核的安装也有多种。其实,对于Linux内核的安装分为三种:内核镜像
、内核模块
、内核头文件
(头文件不需要编译)。
(1) 安装内核镜像
$ make install
此命令会完成以下动作:
1> 把内核镜像拷贝到/boot目录下,并建立相应的system.map符号链接;
2> 修改bootloader的配置文件(一般是Grub);
3> 调用mkinitrd程序创建内核的initrd映象。
注:该命令会将内核映像文件安装到/boot
目录下。如果使用的是Fedora
系统的发布版,就会同时生成boot初始化文件系统映像,并同样安装到/boot
下。而Ubuntu
等Debian
系统的发布版则需要执行下列命令,另行生成和安装boot初始化文件系统映像。在 <内核版本>
的部分请输入表示当前生成的内核版本的文字:
update-initramfs -c -k <内核版本>
通过安装内核而生成的文件和目录
/lib/modules/<内核版本> | 安装模块的目录 |
/boot/vmlinuz-<内核版本> | 内核映像文件 |
/boot/initramfs-<内核版本> 或 /boot/initrd.img-<内核版本> | boot初始化文件系统映像 |
/boot/Systemmap-<内核版本> | 地址信息文件 |
(2) 安装可动态加载、卸载的模块
$ make modules_install
此命令会把编译后的动态模块安装到标准的系统模块所在的位置(默认地,一般为/lib/moudules/$(KERNEL_RELEASE)
目录,其中KERNEL_RELEASE
为该内核的发行版本)。
(3) 安装Linux内核的头文件
$ make headers_install
此命令会把Linux内核中相应的头文件安装到标准的系统头文件所在的位置。
我们一般常安装的是内核镜像。
注:在编译Linux内核源码时(即,使用make
),如果没有指定O
选项,则在编译过程中,所产生的所有中间文件都将位于各自相应的源码所在的目录中;如果指定了O
选项,则在编译过程中,所产生的所有中间文件将位于由O
选项所指定的目录中。其使用方法是:
$ make O=BUILD_DIR
其中,BUILD_DIR
是可以系统中的任何一个目录。但是,
(1)要使用 O 选项,就要在第5步配置 .config 文件时使用;
(2)如果使用了 O 选项,每次使用 make 命令时,都必须使用 O 选项,而且在每次使用 O 选项时所指定的目录必须都相同。
附:make的一些对象或变量
对象 | 说明
-----------------|-------------------------------------------------------------------------------- clean | 将源码树恢复到编译前的状态。obj 文件等删除,.config或编译过程中自动生成的部分文件不会被删除。 mrproper | 将源码树完全恢复到发布时的状态。发布时源码树中不存在的文件全部被删除,包括.config文件。 help | 显示可以使用的make对象。 tags | 生成标签文件。有了标签文件,就能使用Emacs等编辑器的 tag jump 功能跳到函数定义处,可以高效进行源码浏览。 cscope | 生成用于cscope的索引文件。cscope是基于控制台(文字界面)的源码浏览器。 <dir>/<file>.o | 仅进行生成指定的目标文件所必需的编译。当仅指定
时,由.config文件生成该目录内所有目标文件。 <dir>/<file>.ko | 仅生成指定的模块。注:其它的一些对象或变量请参见“5、生成.config配置文件
”。