我们说的Linux其实指的就是内核而已。这个核心控制你主机的所有硬件并提供系统所有的功能,我们开机的时候其实就是利用开机管理程序加载这个核心文件来侦测硬件,在核心加载适当的驱动程序后,你的系统才能够顺利运行。
内核就是系统上面的一个文件而已,这个文件包含了驱动主机各项硬件的侦测程序与驱动模块。这个文件被读入主存储器的时机,当系统读完BIOS并加载MBR内的开机管理程序后,就能够加载核心到内存当中了。然后核心开始侦测硬件,挂载根目录并取得核心模块来驱动所有的硬件,之后呼叫init就能够依序启动所有系统所需的服务了。
这个核心文件通常被放置在/boot/vmlinuz,也不一定就是这个,因为一个主机上面可以拥有多个核心文件,只是开机的时候仅能选择一个加载而已。
内核模块在/lib/modules/$(uname-r)/kernel/下。
编译内核的目的:
1新功能的需求,需要新的功能,而这个功能只有在新的核心里面才有,那么能够用这个功能,只好重新编译我的内核了。如iptables防火墙机制只有在2.4.xx以后的版本里面才有,而新开发的主板芯片组,很多页需要新的核心推出之后,才能正常而且有效率地工作。
2原本核心模块太多,对于核心编译了很多莫名其妙的功能,那么可以重新编译内核来取消该功能。
3与硬件搭配的稳定性,原本linux内核大多是针对Intel的CPU开发的,如果你的CPU是AMD,那么有可能会让系统跑得不太稳。
4其他需求(如嵌入式系统):就是你需要特殊的环境需求时,就得自行设计你的核心(像是一些商业的软件包系统,由于需要较为小而美的操作系统,那么它们的内核就需要更简洁有力了。)
注:重新编译内核虽然可以针对你的硬件做优化的步骤,不过由于这些优化的步骤对于整体效能的影响是很小很小的,因此如果是为了增加效能来编译内核的话,如果是针对系统稳定性来考虑的话,那么有必要重新编译内核了。
内核的主要工作是控制硬件,所以编译内核之前,了解你的硬件配备,与你主机的未来功能,内核的功能是越简单越好,所以只要将这部主机的未来功能添加进去就可以了。
内核的版本:
主要的版本定义为:[主].[次].[释出]-[修改]的样式。只要知道2.6.x是稳定版本,2.5.x是测试用版本即可。我们使用最新的内核来重新编译内核时,大多是使用那种偶数的内核版本的。2.4.x与2.6.x是两个具有相当大差异的内核版本,两者之间使用到的函式库基本上不相同,在升级之前,如果你的内核原本是2.4.xx版,那么就升级到2.4.xx版本的最新版,不要由2.4.xx直接升级到2.6.xx版,否则会出问题。
目前新版的distributions,包括CentOS,FC,SuSE,Mandriva等等,都使用了2.6的内核,你可以直接由http://www.kernel.org下载最新的2.6.xx版本的内核来尝试编译。
内核源码的取得方式:
各主要distributions在推出他们的产品时,其实已经附上了内核原始码了。以CentOS.5x为例,可以在国家高速网络中心网站下载相关的内核SRPM的文件。由于CentOS5.x一直有在进行更新动作,因此可以在update目录底下找到内核源码。
原始内核源码:http://ftp.twaren.net/Linux/CentOS/5/os/SRPMS/
更新码:http://ftp.twaren.net/Linux/CentOS/5/updates/SRPMS/
使用原本distributions释出的源码的好处:
原本的distribution释出的源码中,含有它们设定好的默认设定值,我们可以轻易的就了解到当初它们是如何选择与内核及模块有关的各项设定项目的参数值,那么就可以利用这些可以配合我们linux系统的默认参数来加以修改。
linux内核目前是由其发明者LinuxTorvalds所属团队在负责维护的,而其网站在底下的网址上,在该网站上可以找到最新的kernel信息,不过目前的内核越来越大了。所以如果你的ISP连外网很慢的话,那么使用台湾的映射站台来下载。
核心官网:http://www.kernel.org/
交大资科:ftp://linux.cis.nctu.edu.tw/kernel/linux/kernel/
国高中心:ftp://ftp.twaren.net/pub/Unix/Kernel/linux/kernel/
保留原本设定:利用patch升级内核源码
如果你曾经自行编译过内核,那么你的系统当中应该已经存在前几个版本的内核源码,以及上次你自行编译的参数设定值才对;如果你只是想要在原本的内核底下加入某些特殊功能,而该功能已经针对内核源码推出patch补丁文件了。
举例说明:如果你想要由2.6.27升级到2.6.30的话,那么你就要下载patch-2.6.28,patch-2.6.29,patch-2.6.30等文件,然后依序一个一个去进行patch的动作,才能够升级到2.6.30。但是,如果你想要升级2.6.30的修改版本到2.6.30.3时,由于修改版本是针对2.6.30来制作的,因此你只要下载patch-2.6.30.3来直接将2.6.30升级至2.6.30.3即可。如果你想要从2.6.30.2升级到2.6.30.3呢?很抱歉,并没有2.6.30.2升级到2.6.30.3的补丁文件,所以你必须要将2.6.30.2还原到2.6.30,然后才能使用patch-2.6.30.3来升级2.6.30。
内核源码的解压缩/安装/观察
这里的内核源码是由底下的连接取得的
ftp://linux.cis.nctu.edu.tw/kernel/linux/kernel/v2.6/linux-2.6.30.3.tar.bz2
内核源码的解压缩与放置目录
2.6.x内核源码一般建议放置在/usr/src/kernels/目录底下
内核源码下的次目录
arch:与硬件平台有关的项目,大部分指的是CPU的类别,例如x86,x86_64,Xen虚拟支持等;
block:与成组设备较相关的设定数据,区块数据通常指的是大量储存媒体,还包括类似ext3等文件系统的支持是否允许等。
crypto:内核所支持的加密的技术,例如md5或者是des等等;
Documentation:与内核有关的一堆说明文件,若对内核有极大的兴趣,看看这里。
drivers:一些硬件的驱动程序,例如显示适配器、网络卡、PCI相关硬件等等;
firmware:一些旧式硬件的微脚本数据;
fs:内核所支持的filesystems,例如vfat,nfs等等;
include:一些可让其他过程调用的标头定义数据;
init:一些内核初始化的定义功能,包括挂载与init程序的呼叫等;
ipc:定义Linux操作系统内各程序的沟通;
kernel:定义内核的程序、内核状态、线程、程序的排程(schedule)、程序的信号(signal)等;
lib:一些函式库;
mm:与内存单元有关的各项数据,包括swap与虚拟内存等;
net:与网络有关的各项协议数据,还有防火墙(net/ipv4/netfilter/*)等等;
security:包括selinux等在内的安全性设定;
sound:与音效有关的各项模块;
virt:与虚拟化机器有关的信息,目前内核支持的是KVM(KernelbaseVirtualMachine);
这些文件大致有个印象就好,至少以后如果你想要使用patch的方法加入额外的新功能时,你要你的源码放在什么地方呢?最好跑到Documentation那个目录底下去看看正确的说明,对你的内核编译会有帮助的。
内核编译前的处理与内核功能选择
内核的目的是管理硬件与提供系统内核功能,因此你必须要先找到你的系统硬件,并且规划你的主机未来的任务,这样才能编译出适合你这部主机的内核。所以整个内核编译的重要工作是在挑选你想要的功能。
硬件环境检测与内核功能要求
通过/proc/cpuinfo及lspci查看
保持干净源码:makemrproper
如果我们是第一次编译,但是我们嫩不清楚到底下载下来的源码当中有没有保留目标文件(*.o)以及相关的配置文件存在,此时我们可以通过底下的方式来处理这些编译过程的目标文件以及配置文件:
[root@www linux-2.6.30.3]# make mrproper
这个动作会将你以前进行过的核心功能选择文件也删除掉,所以几乎只有第一次执行内核编译前才进行这个动作,其余时候,你想要删除前一次编译过程的残留数据,只要下达:
[root@www linux-2.6.30.3]# make clean
因为makeclean仅会删除类似目标文件之类的编译过程产生的中间文件,而不会删除配置文件,这个很重要,要记住!第一次进行编译,所以,请下达makemrproper
开始挑选内核功能:makeXXconfig
/boot底下存在一个名为config-xxx的文件,这个其实是内核功能列表文件!我们下面要进行的动作就是作出该文件!内核功能的挑选,最后会在/usr/src/kernels/linux-2.6.30.3/底下产生一个名为.config的隐藏文件,这个文件就是/boot/config-xxx的文件了。那么这个文件如何建立呢?常见的方法有:
makemenuconfig
最常使用的,是文本模式底下可以显示类似图形接口的方式,不需要启动XWindow就能够挑选内核功能列表。
makeoldconfig
通过使用已存在的./.config文件内容,使用该文件内的设定值为默认值,只将新版本内核内的新功能选项列出让用户选择,可以简化核心功能的挑选过程!对于作为升级内核源码后的功能挑选来说,是非常好用的一个项目!
makexconfig
通过以Qt为图像接口基础功能的图形化接口显示,需要具有Xwindow的支持。例如KDE是通过Qt来设计的XWindow,因此你如果在KDE画面中,可以使用这个。
makegconfig
通过以Gtk为图形接口基础功能的图形化接口显示,需要具有Xwindow的支持。例如GNOME就是透过Gtk来设计的XWindow,因此你如果在GNOME画面中,可以使用此一项目。
makeconfig
最旧式的功能挑选方法,每个项目都以条列式一条一条地列出让你选择,如果设定错误只能再次选择,很不人性化!
以makemenuconfig为例来说,
核心功能细项选择
General setup
与linux最相关的程序互动、核心版本说明、是否使用发展中程序代码等信息都在这里设定的。这里的项目主要都是针对核心与程序之间的相关性来设计的,基本上保留默认值就可以了!不要随便取消底下任何一个项目,因为可能会造成某些程序无法被同时执行的困境。不过底下有非常多新的功能,有不清楚的地方,可以按<Help>进入查阅。
loadable module + block layer
要让你的内核能够支持动态的核心模块,那么底下的第一个设定就得要启动才行!至于第二个block layer则预设是启动的,你也可以进入该项目的细项设定,选择其中你认为需要的功能即可!
CPU的类型与功能选择
进入[Processor type and features]后,挑选你主机的实际CPU形式。
电源管理功能
如果选择了[Power management and ACPI options]之后,就会进入系统的电源管理机制中。其实电源管理机制还需要搭配主板以及CPU的相关省电功能,才能够实际达到省电的效率啦!不论是Server还是Desktop的使用,在目前电力不足的情况下,能省电就加以省电吧!
一些总线(bus)的选项
这个项目则与总线有关,分为最常见的PCI与PCI-express的支持,还有笔记本电脑常见的PCMCIA插卡。那个PCI-Express的界面务必要选,不然你的新显示适配器可能会捕捉不到。
编译后执行文件的格式
选择[Executable file formats / Emulations],底下的选项必须要勾选才行!因为是给Linux核心运作执行文件用的数据。通常是与编译行为有关啦!
内核的网络功能
这个[Networking support]项目是相当重要的选项,因为它还包括了防火墙相关的项目,就是未来在服务器篇谈到的防火墙iptables这个数据。大部分的参数都与网络、防火墙有关。由于防火墙是在启动网络之后再设定即可,所以绝大部分的内容都可以被编译成为模块,而且也建议你编成模块!有用到再载入到内核即可!
各项装置的驱动程序
进入[Device Drivers]这个是所有硬件装置的驱动程序库!这里面的数据与你主机的硬件有绝对的关系。
文件系统的支持
文件系统的支持也是很重要的一项核心功能。因为如果不支持某个文件系统,那么我们的linux kernel就无法认识,当然也无法使用。如Quota NTFS等特殊的文件系统。
核心黑客、信息安全、密码应用
[Kernel hacking],那是与内核开发者比较有关的部分,这部分建议保留默认值即可。[Security Options]那是属于信息安全方面的设定,包括SElinux这个细部权限强化模块也在这里编入核心的。[Cryptographic API]这个密码应用程序编程接口工具选项,也是可以保留默认值。在密码应用程序编程接口方面,一般我们使用的帐号密码登录利用的就是MD5这个加密机制,要让核心有支持才行。几乎所有的项目都给它做成模块即可!不过MD5与SHA1必须要直接由内核支持比较好!
虚拟化与函式库
虚拟化是近年来非常热门的一个讧题,因为计算机的能力太强,所以时常闲置在那边, 此时,我们可以透过虚拟化技术在一部主机上面同时启劢多个操作系统来运作,这就是所谓的虚拟化。 Linux 核心已经主劢的纳入虚拟化功能喔!而 Linux 讣可的虚拟化使用的机制为 KVM
(Kernel base Virtual Machine)。 至亍常用的核心凼式库也可以全部编为模块��!
内核的编译与安装
编译内核与内核模块
内核与内核模块需要先编译起来,而编译的过程非常简单。
[root@www linux-2.6.30.3]# make vmlinux <==未经压缩的核心 [root@www linux-2.6.30.3]# make modules <==仅核心模块 [root@www linux-2.6.30.3]# make bzImage <==经压缩过的核心(预训) [root@www linux-2.6.30.3]# make all
我们常见的在 /boot/ 底下的核心档案,都是经过压缩过的核心档案,因此,上述的劢作中比较常用的是 modules 不 bzImage 这两个,
其中 bzImage 第三个字母是英文大写的 I 喔!bzImage 可以制作出压缩过后的核心, 也就是一般我们拿来进行系统开机的信息��!所以,基本上我们会进行的动作是:
[root@www linux-2.6.30.3]# make clean <==先清除暂存档 [root@www linux-2.6.30.3]# make bzImage <==先编译核心 [root@www linux-2.6.30.3]# make modules <==再编译模块
上述的动作会花费非常长的时间,编译的动作依据你选择的项目以及你主机硬件的效能而不同。最后制作出来的数据是被放置在 /usr/src/kernels/linux-2.6.30.3/ 这个目录下,还没有被放到系统的相关路径中。如果有发生任何错诨的话,很可能是由于核心项目选择得不好,可能你需要重以 make menuconfig 再次的检查一下你的相关设定喔! 如果还是无法成功的话,那么或许将原本的核心数据内原本的 .config 档案,复制到你的核心原始文件目��下, 然后据以修改,应该就可以顺利的编译出你的核心了。最后注意到,下达了 make bzImage 后,最终的结果应该会像这样
Root device is (8, 1) Setup is 12696 bytes (padded to 12800 bytes). System is 2207 kB CRC 7701ab0e Kernel: arch/x86/boot/bzImage is ready (#1) [root@www linux-2.6.30.3]# ll arch/x86/boot/bzImage -rw-r--r-- 1 root root 2272432 7 月 30 13:35 arch/x86/boot/bzImage
可以发现你的核心已经编译好而�D放置在 /usr/src/kernels/linux-2.6.30.3/arch/x86/boot/bzImage 里面��~那个就是我们的核心档案!
最重要就是他啦!我们等一下就会安装到这个档案哩! 然后就是编译模块的部分��~ make modules �行完毕后,就等着安装啦! ^_^
实际安装模块
安装模块前有个地方得要特别强调喔!我们知道模块是放置到 /lib/modules/$(uname -r) 目录下的,那如果同一个版本的模块被反复编译后来安装时,会不会产生冲突呢?�S例来说,鸟哥这个 2.6.30.3 的版本第一次编译完成�D安装妥当后,发现有个小细节想要重新处理,因此又重新编译过一次,那两个版本一模一样时, 模块放置的目��会一样,此时就会产生冲突了!如何是好?有两个解决方法啦:
1 先将旧的模块目��更名,然后才安装核心模块到目标目��去;
2 在 make menuconfig 时,那个 General setup 内的 Local version 修改成新的名称。
建议使用第二个方式,因为如此一来,你的模块放置的目��名称就不会相同,这样也就能略过上述的目��同名问题��! 好,那举如何安装模块到正确的目标目��呢?�辜虻�,同样使用 make 的功能即可:
[root@www linux-2.6.30.3]# make modules_install [root@www linux-2.6.30.3]# ll /lib/modules/ drwxr-xr-x 3 root root 4096 7 月 30 14:31 2.6.30.3vbird
开始安装新内核与多重内核选单(grub)
移动内核到 /boot 并保留旧内核文件
[root@www ~]# cp /usr/src/kernels/linux- 2.6.30.3/arch/x86/boot/bzImage \ > /boot/vmlinuz-2.6.30.3vbird <==实际核心 [root@www ~]# cp /usr/src/kernels/linux-2.6.30.3/.config \ > /boot/config-2.6.30.3vbird <==建讧配置文件也复制备份
建立相对应的 Initial Ram Disk (initrd)
使用如下的方法来建立 initrd 吧!让得搭配正确的核心版本喔!
[root@www ~]# mkinitrd -v /boot/initrd-2.6.30.3vbird.img 2.6.30.3vbird ....(前面省略).... Adding module ehci-hcd Adding module ohci-hcd Adding module uhci-hcd
编辑开机选单 (grub)
额外(单一)核心模块编译
我们现在知道核心所支持的功能当中,有直接编译到核心内部的,也有使用外挂模块的,外挂模块可以简单的想成就是驱动程序 啦!那么也知道这些核心模块依据丌同的版本,被分别放置到 /lib/modules/$(uname -r)/kernel/ 目录中,各个硬件的驱动程序则是放置到/lib/modules/$(uname -r)/kernel/drivers/ 当中!换个角度再来思考一下,如果刚刚我自己编译的数据中,有些驱动程序忘让编译成为模块了,那是否需要重新进行上述的所有动作? 又如果我想要使用硬件厂商释出的新驱动程序,那该如何是好?
编译前注意事项
核心原始码我们知道他是可能放置在 /usr/src/ 底下,早期的核心原始码被要求一定要放置到 /usr/src/linux/ 目��下,丌过,如果你有
多个核心在一个 Linux 系统当中,而�D使用的原始码�载⑾嗤�时, 呵呵~问题可就大了!所以,在 2.6 版以后,核心使用比较有趣的方法
来训计他的原始码放置目��, 那就是以 /lib/modules/$(uname -r)/build 及 /lib/modules/$(uname -r)/source 这两个连结档来��向正
确的核心原始码放置目��。如果以我们刚刚由 kernel 2.6.30.3 建立的核心模块来说, 那举他的核心模块目��底下有什举咚咚?
[root@www ~]# ll -h /lib/modules/2.6.30.3vbird/ lrwxrwxrwx 1 root root 31 7 月 30 14:29 build -> /usr/src/kernels/linux-2.6.30.3 drwxr-xr-x 10 root root 4.0K 7 月 30 14:30 kernel -rw-r--r-- 1 root root 337K 7 月 30 14:31 modules.alias -rw-r--r-- 1 root root 69 7 月 30 14:31 modules.ccwmap -rw-r--r-- 1 root root 224K 7 月 30 14:31 modules.dep ....(中间省略).... lrwxrwxrwx 1 root root 31 7 月 30 14:29 source -> /usr/src/kernels/linux-2.6.30.3
比较有趣的除了那两个连结档之外,还有那个 modules.dep 文件也挺有趣的, 那个档案是记录了核心模块的相依属性的地方,依据该档案,我们可以简单的使用 modprobe 这个指令来加载模块呢!至于核心原始码提供的头文件,在上面的案例当中, 则是放置到
/usr/src/kernels/linux-2.6.30.3/include/ 目录中,当然就是藉由 build/source 这两个链接档案来取得目��所在的啦!^_^
由于核心模块的编译其实不核心原本的原始码有点关系的,因此如果你需要重新编译模块时, 那除了 make, gcc 等主要的编译软件工具
外,你还需要的就是 kernel-devel 这个软件!让得一定要安装喔!而如果你想要在预训的核心底下新增模块的话,那举就得要找到 kernel
的 SRPM 档案了! 将该档案给他安装,并且取得 source code 后,才能够顺利的编译喔!
单一模块编译
想象两个情况:
如果我的默认核心忘记加入某个功能,而且该功能可以编译成为模块,不过, 预设核心却也没有将该项功能编译成为模块,害我不能使用时,该如何是好?
如果 Linux 核心原始码并没有某个硬件的驱动程序 (module) ,但是开发该硬件的厂商有提供给 Linux 使用的驱动程序原始码,那么我又该如何将该项功能编进核心模块呢?
很有趣对吧!不过,在这样的情况下其实没有什举好说的,反正就是 『去取得原始码后,重新编译成为系统可以加载的模块』啊!�辜虻�,对吧!^_^! 但是,上面那两种情况的模块编译行为是不太一样的,不过,都是需要 make, gcc 以及核心所提供的 include 头文件与函式库等等。
硬件开发商提供的额外模块
举个例子来说,为了省电,鸟哥在 2009 年�_买了整合型主板来架训家用的朋务器,没想到 CentOS 5.1 以前的版本对鸟哥新买
的主板内建网卡支持度丌足, 使用的网卡驱劢程序 r8169 有问题!搜寻了 google 才发现大家都有这个问题。解决方法就是到 Realtek 官网下载网卡驱劢程序来编译即可。
Realtek 的 r8168 网卡驱劢程序:http://www.realtek.com.tw/downloads/
你可以利用各种方法将他下载后,假训这个档案放置到 /root ,那举直接将他解压缩吧! �T后就可以读一读 INSTALL/README ,然后找
一下 Makefile ,就能够编译了。整体流程有点像这样:
# 1. 将档案解压缩: [root@www ~]# cd /usr/local/src [root@www src]# tar -jxvf /root/r8168-8.013.00.tar.bz2 [root@www src]# cd r8168-8.013.00/ # 2. 开始�行编译不安装: [root@www r8168-8.013.00]# vi readme <==注意查一下该档案内容 [root@www r8168-8.013.00]# make clean modules [root@www r8168-8.013.00]# ll src/*.ko <==建立底下的模块文件! -rw-r--r-- 1 root root 112216 7 月 31 01:11 src/r8168.ko [root@www r8168-8.013.00]# make install install -m 744 -c r8168.ko /lib/modules/2.6.30.3vbird/kernel/drivers/net/ # 重点在上面这行!会发现模块已经被移劢到核心模块目��! 4. 更新模块相依属性! [root@www r8168-8.013.00]# depmod -a
利用旧有的核心原始码进行编译
如果你后来发现忘让加入某个模块功能了,那该如何是好?其实如果仅是重新编译模块的话, 那么整个过程就会变的非常简单!我们先到
目前的核心原始码所在目��下达 make menuconfig , 然后将 NTFS 的选项训定成为模块,�T后直接下达:make fs/ntfs/那么 ntfs 的模块 (ntfs.ko) 就会自劢的被编译出来了! 然后将该模块复制到 /lib/modules/2.6.30.3vbird/kernel/fs/ntsf/ 目��下, 再执行 depmod -a ,呵呵~就可以在原来的核心底下新增某个想要加入的模块功能��~ ^_^
核心模块管理
核心不核心模块是分丌开的,至于驱动程序模块在编译的时候,更不核心的原始码功能分不开~ 因此,你必须要先了解到:核心、核心模块、驱动程序模块、核心原始码与头文件案的相关性, 然后才有办法了解到为何编译驱动程序的时候老是需要找到核心的原始码才能够顺
利编译! 然后也才会知道,为何当核心更新之后,自己之前所编译的核心模块会失效~
此外,与核心模块有相关的,还有那个�钩1皇褂玫� modprobe ��令, 以及开机的时候会读取到的模块定义数据文件/etc/modprobe.conf , 这些数据你也必须要了解才行~相关的��令说明我们已经在第二十章内谈过了, 你应该要自行前往了解喔! ^_^