最小的Linux操作系统制作过程详解
一,什么是BabyLinux
BabyLinux不是一个完整的发行版,他是利用原有的一套完整的linux系统的内核原代码和编译工具,利用busybox内建的强大功能,在一张软盘上做的一个很小的linux系统。他具备一个linux系统的基本特征,支持linux系统最常用的一百多个命令,支持多种文件系统,支持网络等等,你可以把他当做一张linux起动盘和修复盘来用,你也可以把他当做一个静态路由的路由器软件,当然,你也可以把他当做一个linux玩具,向你的朋友炫耀 linux可以做的多么小。我把他叫做BabyLinux因为他很小巧,小的很可爱,像一个刚刚出生的小baby
二.为什么要作这样一个linux
先说说我一开始的想法,当我一开始接触linux的时候,看到书上说,linux通常安装只需要60M左右的空间,但是我发现装在我硬盘上的Redhat 6.0怎么制作空间确要占据好几百M的空间。为什么我的linux这么大呢? 后来我发现,装在我机器上的那么多东西只有不到30%是我平时常用的,还有30%是我极少用到的,另外的40%基本上是不用的。于是,我和大多数初学者一样,开始抱怨,
为什么linux不能做的精简一点呢?于是,我萌发了自己裁减系统的想法。可惜那个时候我还没有听说过有LFSDebain。等到我积累了足够的linux知识后,我开始制作这样一个小系统。
制作这样一个小系统最大的意义在于,你可以通过制作系统了解linux的启动过程,学会ramdisk的使用,让你在短时间内学到更多的linux知识。 当然,你会得到很大的乐趣。这个项目只是做一个具有基本特征的linux系统,如果你想自己做一个具有完整功能的linux,请阅读Linux From Scratch (LFS)文档。
三,什么人适合读这篇文档
如果你是一个linux爱好者,并且很想了解linux的启动过程和系统的基本结构,而且是一个喜欢动手研究小玩意的人,那么这个文档可以满足你的需求。 如果你仅仅是用linux来做一些普通的日常工作,而不在乎你的linux到底怎么工作,那么这份文档也许不太适合你。另外,如果你是linux爱好者, 但是目前还是一个刚刚入门的newbi,我建议你先把linux命令学好。不过我想我会尽可能的把这份文档写详细一些,如果你有足够的毅力,或许一个newbi也能成功做一个babylinux。或者,你遇到一件很不巧的事情,比如你的老婆来例假了,你的这个周
末就泡汤了,那么阅读这篇文档并做一个 linux小玩具可以打发你的时间。
四,应该具备的知识
在做一个babylinux之前,你应当已经会应用linux最常用的命令。并且至少有一次成功编译并安装系统内核的经历,会通过编译源代码来安装软件。 如果你具备了这些条件,那么做这样一个小系统会很顺利,如果你还没有掌握这些知识,你可能会遇到一些困难。但是只要有毅力,也可以成功。你不需要具备编程的知识,因为我的目标是:让具有中等以上linux水平的爱好者可以通过阅读文档轻松完成这个项目。关于一张软盘上的linux还有一个很著名的 linuxLOAP (Linux On AFloppy) 但是他是由比较专业的人员需要编写很多程序完成的。而且没有关于他制作过程的文档。
五,linux系统引导过程简介
首先,主板的BIOS会读取硬盘的主引导记录(MBR)MBR中存放的是一段很小的程序,他的功能是从硬盘读取操作系统核心文件并运行,因为这个小程序太小了,因此通常这个小程序不具备直接引导系统内核的能力,他先去引导另一个稍微大一点的小程序,再由这个大一
点的小程序去引导系统内核。在linux系统中这样的小程序有LILOGRUB。在这个项目中,我决定用LILO来做系统引导程序。在软盘上启动linux系统的过程和在硬盘上启动的过程相似。
Linux系统内核被引导程序装入内核并运行后,linux内核会检测系统中的各种硬件。并做好各种硬件的初始化工作,使他们在系统正式运行后能正常工作。之后内核做的最后一个工作是运行/sbin 下的init程序,init是英文单词initialization(初始化)的简称,init程序的工作是读取/etc/inittab文件中描述的指令,对系统的各种软硬件环境做最初化设定。最后运行mingetty等待用户输入用户名登录系统。所有的工作就这么简单,虽然linux启动的时候有很多内容,看上去十分高深,但是都不过是对这个过程的扩充。明白了这个道理,你可以写一些脚本程序让他在系统启动的特定时间运行完成任务。事实上系统内核并不关心/sbin下的init是不是真的init,只要是放在/sbin下名叫init的可执行程序他都可以执行。可以做以下实验:
编写一个非常简单的C程序:
main()
{
printf(“helloworld!\n”);
}
保存后以initc保存他,并用gcc编译。
#gcc –-static -o init initc
这里的--static 参数告诉gcc把这个程序静态联接,这样这个程序不倚赖任何库就能运行。把编译好的init程序拷贝到/sbin下,备份好原来的那个。重新启动系统最后系统的输出结果是: helloworld!
然后停在那里。做这个实验以前先确定你知道如何把系统恢复到原来的状态,有一个简单的方法,在内核启动前给他加上init=参数,比如你原先的init被你改成了initbak 只要在启动的时候给内核加上init=/sbin/initbak就可以用原来的init程序启动系统。
做完以上实验,就明白了内核和init程序之间的关系。此外,init程序不一定是一个二进制可执行程序,他可以是一个bash脚本,一个指向另一个程序的联接,他的位置也并不一定要在/
sbin下,只要在启动内核时,给内核加上init参数就能被运行,比如,开始时给内核加上init= /bin/bash参数,内核在最后一步就直接运行bash给出提示符,不用登录系统就可以输入命令了。其功能类似单用户模式启动系统。 /sbin/init 程序只是内核默认运行的第一个程序。
六,编译一个linux系统内核
1,编译前的规划和准备
在编译内核前,请先确定你的需求,把你的需求罗列成一张详细的表格。你需要让内核支持什么硬件,支持多少种分区类型和文件系统,支持哪些网卡,支持哪些网络协议。等等。请尽可能详细的罗列这些内容,但是你也不要太贪心,因为你所有能利用的空间只有1440K,如果你编译出一个大于1440K或很接近这个数字的内核,你的这个项目就不能完成了,你已经没有空间再放ramdisk映象文件,除非你原意再多出一张软盘,做一个两张软盘的小linux系统。对于声卡驱动之类,我劝你还是放弃吧,因为一个声卡驱动也许只让你的内核增大了十多K,但是你有了一个声卡驱动就务必要有一个播放器吧,否则声卡驱动就没有意义,可一个播放器的大小可不是一张软盘可以装得下的。在我先前制作的babylinux内核有900K,其中,文件系统部分站了大部分,因为我的目标是把他做成一个系统修复盘。因此我在
内核中编译7种文件系统的支持,每减少一个文件系统就可以减小几十甚至200K的内核大小。越是复杂,越是安全的文件系统,其支持模块也越大,比如在linuxFAT模块只有32KVFAT只有17K,但是ext3的模块就有86KJFS达到216K reiserfs模块是224K,可以想像,编译一个支持7个文件系统的900K的内核,文件系统部分就占了600K以上的空间,所以如果某一个文件系统是你根本不用的,那么还是不要编译进内核把,这样至少可以省下100K的空间。对于其他的驱动,比如网卡,通常大小只有89K,最大的也不过10 K,因此可以把常用的网卡芯片的驱动都编译进去。另外如果你想让你的babylinux支持U盘,那么scsi的驱动模块也是不可小看的,他通常要接近 150K,因为U盘是被当做scsi设备来驱动的。另外你还需要让你的内核支持即插即用,这些都是不小的空间开销,我的建议是你放弃一两个你不用的文件系统。总之,你最后编译出来的内核大小最好不要超过900K,否则你在busybox里只能编译进去很少的命令。
在我编译的busybox中,我编译进去120多个命令,基本上把busybox支持的命令都包括进去了。加上小系统所必需的文件系统目录,/dev下的设备文件,以及/etc下几个必需的配置文件,做成ramdisk压缩后的大小是440K 加上900K左右的内核刚好可以放入一张1440K软盘,请注意,你应该留下至少50K的空间,因为我们要在软盘上创建一个ext2文件系统,而
文件系统本生需要占据大概25K的磁盘空间。另外lilo的引导文件bootb的大小是57K,还有装上lilo后自动产生的map文件也要10K的空间, map文件的具体大小由内核安装的实际大小决定,通常不会超过30K
综上所述,请遵循下面的公式:
内核大小+文件系统压缩印象文件+50K <= 1440K
另外一点需要说明的是:以上所罗列的文件系统模块大小是察看我现在使用的Redhat 9 /lib/modules下的模块文件得到的,实际编译进内核大小会小一点,因为我们用make bzImage在内核源代码目录树下生成的内核是经过压缩过的。
如果你对以上说的内容不太明白也没有关系,我会在下面的内容中做详细的说明。
2,必需编译进内核的内容
首先,我们制作的这个小系统是基于一张软盘的,因此,你的内核必需支持软盘。另外对IDE硬盘和cdrom的支持也是不可少的,否则做出来的 babylinux就没有实用价值,因为他不
能访问硬盘和光盘上的内容这样的linux虽然可以做的更小,但是制造一个完全没有用的东西是浪费时间。其他的包括framebuffer等,如果你需要支持在字符界面下以高分辨率显示,以看到更多的屏幕内容,那么就必需把framebuffer支持编译进内核,此外在高分辨率下使用的8x8字体也必需编译进去。否则即使你给内核传递了vga= 参数,内核会因为没有可用的小字体而自动转跳到低分辨率模式下,这是以前困扰我好几天想不明白的事情,后来通过反复试验才明白原来是缺少字体的文体。这里我先大致提一下需要注意的事情。在下一小节具体编译时,我会继续就某些细节问题说明。
3,关于内核的版本
我是在Redhat 9 linux系统下打造的babylinux小系统。使用的是Redhat 9 自带的2420版的内核。
为什么我不用最新的26的内核?
一开始我也企图用最新的内核,但是通过试验我发现,在用最新的269内核的情况下,我编译一个all-no(即所有内容都选N,不支持任何硬件,只有一个最基本的内核)最小化内
核就要460K左右,如果我在这个基础上再加入几种文件系统和必要的驱动,那么内核的大小就不能装下一张1440K的软盘,而我用2420的内核编译一个最小化的内核只需要217K,的大小。如果优化了gcc参数他还能再小些。这样我就立即省下了200K的空间,在平时,200K的内容微不足道,但是在babylinux里,这个数目是整个空间的 1/7,相当于一个reiserfs文件系统模块的大小。当然,我也尝试了22以及更老的内核,但是他们缺少我需要的东西,因此最后权衡下来用24的内核是比较合理的。如果你用的是26内核的FC系统,那么最好还是去下载一个24版的内核,wwwkernelorg 有各个时期的内核可以下载。
4 内核的配置
如果你对linux内核的配置和编译已经很熟悉了,请跳过这一段,直接看busybox的编译。
root身份登录系统
进入/usr/src/linux目录
[root@gucuiwen root]# cd /usr/src/linux
如果你下载了一个24版本的内核,为了避免麻烦,请将他拷贝到/usr/src下,然后接压缩,再做一个指向他的名为linux的链接。虽然这并不是必需的,但是根据我以往的经验,如果我把linux源代码放在其他目录下解开并编译,偶然会有一些莫名其妙的小问题发生。
#cp linux-2420bz2 /usr/src/
#cd /usr/src
#tar xfvj linux-2420bz2