Preface
当用户编译一个linux内核代码后,会产生几个文件:vmlinz、initrd.img, 以及System.map,如果配置过grub引导管理器程序,会在/boot目录下看到这几个文件。
vmlinuz
vmlinuz是可引导的、压缩的内核文件。
该文件包含了一个最小功能的内核,在PC上通常是先执行vmlinuz,之后加载initrd.img文件,最后加载根分区。
实际上initrd.img是可选的,从文件大小来看,initrd.img比vmlinuz文件大得多,initrd.img也包含了较多的功能,如果不需要额外的功能,例如在一些功能需求较小的嵌入式系统上,可以仅使用vmlinuz文件存放内核,而省去initrd.img文件。
“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核,它位于/boot/vmlinuz,它一般是一个软链接。
vmlinuz的建立有两种方式。
一是编译内核时通过“make zImage”创建。zImage适用于小内核的情况,它的存在是为了向后的兼容性。
二是编译内核时通过“make bzImage”创建,然后通过。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。
zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage 或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。
内核编译之后还有一个vmlinux文件,vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。
initrd文件
initrd是“initial ramdisk”的简写。就是由Bootloader初始化的内存盘。
在linux内核启动之前,Bootloader会把存储介质(例如闪存)中的initrd文件加载到内存,内核启动时会在访问到真正的根文件系统前访问内存中的initrd文件系统。
如果Bootloader配置了initrd,内核启动被分成两个阶段:
- 第一阶段先加载initrd文件系统中的驱动程序模块;
- 第二阶段才会执行真正的根文件系统中的/sbin/init进程。
第一阶段启动的目的是为第二阶段启动扫清障碍,linux根文件系统支持多种存储介质(如IDE、SCSI、USB等),如果把这些设备的驱动都编译进内核,内核会十分庞大,使用initrd存放设备驱动很好地解决了这一问题。
在启动顺序上,initrd会在vmlinuz代码执行完之后加载,使用initrd的机制可以很好地解决不同硬件环境的情况,是linux发行版,以USB设备启动的必备。在嵌入式系统上,在硬件相对固定的情况下,initrd作用不像PC上那么大,但是对于调试设备驱动起到了简化调试步骤的作用。
initrd一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。比如,使用的是scsi硬盘,而内核vmlinuz中并没有这个scsi硬件的驱动,那么在装入scsi模块之前,内核不能加载根文件系统,但scsi模块存储在根文件系统的/lib/modules下。为了解决这个问题,可以引导一个能够读实际内核的initrd内核并用initrd修正scsi引导问题。
initrd实现加载一些模块和安装文件系统等。
initrd映象文件是使用mkinitrd创建的。mkinitrd实用程序能够创建initrd映象文件。这个命令是RedHat专有的。其它Linux发行版或许有相应的命令。这是个很方便的实用程序。具体情况请看帮助:man mkinitrd
System.map
System.map是一个特定内核的内核符号表。它是你当前运行的内核的System.map的链接。
System.map是由“nm vmlinux”产生并且不相关的符号被滤出。
在进行程序设计时,会命名一些变量名或函数名之类的符号。Linux内核是一个很复杂的代码块,有许许多多的全局符号。
Linux内核不使用符号名,而是通过变量或函数的地址来识别变量或函数名。比如不是使用size_t BytesRead这样的符号,而是像c0343f20这样引用这个变量。
对于使用计算机的人来说,更喜欢使用那些像size_t BytesRead这样的名字,而不喜欢像c0343f20这样的名字。内核主要是用c写的,所以编译器/连接器允许我们编码时使用符号名,当内核运行时使用地址(使用符号表查询一个符号对应的地址,或者通过内存地址得到一个符号名称)。
然而,在有的情况下,我们需要知道符号的地址,或者需要知道地址对应的符号。这由符号表来完成,符号表是所有符号连同它们的地址的列表。
Linux 符号表使用到2个文件:
- /proc/ksyms
- System.map
/proc/ksyms是一个“proc file”,在内核引导时创建。实际上,它并不真正的是一个文件,它只不过是内核数据的表示,却给人们是一个磁盘文件的假象,这从它的文件大小是0可以看出来。
System.map是存在于你的文件系统上的实际文件。当你编译一个新内核时,各个符号名的地址要发生变化,你的老的System.map具有的是错误的符号信息。每次内核编译时产生一个新的System.map,你应当用新的System.map来取代老的System.map。
虽然内核本身并不真正使用System.map,但其它程序比如klogd, lsof和ps等软件需要一个正确的System.map。如果你使用错误的或没有System.map,klogd的输出将是不可靠的,这对于排除程序故障会带来困难。
没有System.map,你可能会面临一些令人烦恼的提示信息。
另外少数驱动需要System.map来解析符号,没有为你当前运行的特定内核创建的System.map它们就不能正常工作。
Linux的内核日志守护进程klogd为了执行名称-地址解析,需要使用System.map。System.map应当放在使用它的软件能够找到它的地方。
执行:man klogd可知,如果没有将System.map作为一个变量的位置给klogd,那么它将按照下面的顺序,在三个地方查找System.map:
- /boot/System.map
- /System.map
- /usr/src/linux/System.map
System.map也有版本信息,klogd能够智能地查找正确的映象(map)文件。