最小Linux系统制作例程

芝锐   2011-6-5 22:54 楼主

一,什么是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不能做的精简一点呢?于是,我萌发了自己裁减系统的想法.可惜那个时候我还没有听说过有LFS和Debain.等到我积累了足够的linux知识后,我开始制作这样一个小系统.

  制作这样一个小系统最大的意义在于,你可以通过制作系统了解linux的启动过程,学会ramdisk的使用,让你在短时间内学到更多的linux知识. 当然,你会得到很大的乐趣.这个项目只是做一个具有基本特征的linux系统,如果你想自己做一个具有完整功能的linux,请阅读Linux From Scratch (LFS)文档.

  三,什么人适合读这篇文档

  如果你是一个linux爱好者,并且很想了解linux的启动过程和系统的基本结构,而且是一个喜欢动手研究小玩意的人,那么这个文档可以满足你的需求. 如果你仅仅是用linux来做一些普通的日常工作,而不在乎你的linux到底怎么工作,那么这份文档也许不太适合你.另外,如果你是linux爱好者, 但是目前还是一个刚刚入门的newbi,我建议你先把linux命令学好.不过我想我会尽可能的把这份文档写详细一些,如果你有足够的毅力,或许一个 newbi也能成功做一个babylinux.或者,你遇到一件很不巧的事情,比如你的老婆来例假了,你的这个周末就泡汤了,那么阅读这篇文档并做一个 linux小玩具可以打发你的时间.

  四,应该具备的知识

  在做一个babylinux之前,你应当已经会应用linux最常用的命令.并且至少有一次成功编译并安装系统内核的经历,会通过编译源代码来安装软件. 如果你具备了这些条件,那么做这样一个小系统会很顺利,如果你还没有掌握这些知识,你可能会遇到一些困难.但是只要有毅力,也可以成功.你不需要具备编程的知识,因为我的目标是:让具有中等以上linux水平的爱好者可以通过阅读文档轻松完成这个项目.关于一张软盘上的linux还有一个很著名的 linux叫LOAP (Linux On A

  Floppy) 但是他是由比较专业的人员需要编写很多程序完成的.而且没有关于他制作过程的文档.

  五,linux系统引导过程简介

  首先,主板的BIOS会读取硬盘的主引导记录(MBR),MBR中存放的是一段很小的程序,他的功能是从硬盘读取操作系统核心文件并运行,因为这个小程序太小了,因此通常这个小程序不具备直接引导系统内核的能力,他先去引导另一个稍微大一点的小程序,再由这个大一点的小程序去引导系统内核.在linux系统中这样的小程序有LILO和GRUB.在这个项目中,我决定用LILO来做系统引导程序.在软盘上启动linux系统的过程和在硬盘上启动的过程相似.

  Linux系统内核被引导程序装入内核并运行后,linux内核会检测系统中的各种硬件.并做好各种硬件的初始化工作,使他们在系统正式运行后能正常工作.之后内核做的最后一个工作是运行

  /sbin 下的init程序,init是英文单词initialization(初始化)的简称,init程序的工作是读取/etc/inittab文件中描述的指令,对系统的各种软硬件环境做最初化设定.最后运行mingetty等待用户输入用户名登录系统.所有的工作就这么简单,虽然linux启动的时候有很多内容,看上去十分高深,但是都不过是对这个过程的扩充.明白了这个道理,你可以写一些脚本程序让他在系统启动的特定时间运行完成任务.事实上系统内核并不关心/sbin下的init是不是真的init,只要是放在/sbin下名叫init的可执行程序他都可以执行.可以做以下实验:

  编写一个非常简单的C程序:

  main()

  {

  printf(“hello,world!n”);

  }

  保存后以init.c保存他,并用gcc编译.

  #gcc –-static -o init init.c

  这里的--static 参数告诉gcc把这个程序静态联接,这样这个程序不倚赖任何库就能运行.把编译好的init程序拷贝到/sbin下,备份好原来的那个.重新启动系统最后系统的输出结果是: hello,world!

  然后停在那里.做这个实验以前先确定你知道如何把系统恢复到原来的状态,有一个简单的方法,在内核启动前给他加上init=参数,比如你原先的init被你改成了init.bak 只要在启动的时候给内核加上init=/sbin/init.bak就可以用原来的init程序启动系统.

  做完以上实验,就明白了内核和init程序之间的关系.此外,init程序不一定是一个二进制可执行程序,他可以是一个bash脚本,一个指向另一个程序的联接,他的位置也并不一定要在/sbin下,只要在启动内核时,给内核加上init参数就能被运行,比如,开始时给内核加上init= /bin/bash参数,内核在最后一步就直接运行bash给出提示符,不用登录系统就可以输入命令了.其功能类似单用户模式启动系统. /sbin/init 程序只是内核默认运行的第一个程序.

六,编译一个linux系统内核

 

  1,编译前的规划和准备

  在编译内核前,请先确定你的需求,把你的需求罗列成一张详细的表格.你需要让内核支持什么硬件,支持多少种分区类型和文件系统,支持哪些网卡,支持哪些网络协议.等等.请尽可能详细的罗列这些内容,但是你也不要太贪心,因为你所有能利用的空间只有1440K,如果你编译出一个大于1440K或很接近这个数字的内核,你的这个项目就不能完成了,你已经没有空间再放ramdisk映象文件,除非你原意再多出一张软盘,做一个两张软盘的小linux系统.对于声卡驱动之类,我劝你还是放弃吧,因为一个声卡驱动也许只让你的内核增大了十多K,但是你有了一个声卡驱动就务必要有一个播放器吧,否则声卡驱动就没有意义,可一个播放器的大小可不是一张软盘可以装得下的.在我先前制作的babylinux内核有900多K,其中,文件系统部分站了大部分,因为我的目标是把他做成一个系统修复盘.因此我在内核中编译7种文件系统的支持,每减少一个文件系统就可以减小几十甚至200多K的内核大小.越是复杂,越是安全的文件系统,其支持模块也越大,比如在linux下FAT模块只有32K,VFAT只有17K,但是ext3的模块就有86K,JFS达到216K, reiserfs模块是224K,可以想像,编译一个支持7个文件系统的900多K的内核,文件系统部分就占了600K以上的空间,所以如果某一个文件系统是你根本不用的,那么还是不要编译进内核把,这样至少可以省下100多K的空间.对于其他的驱动,比如网卡,通常大小只有8,9K,最大的也不过10多 K,因此可以把常用的网卡芯片的驱动都编译进去.另外如果你想让你的babylinux支持U盘,那么scsi的驱动模块也是不可小看的,他通常要接近 150K,因为U盘是被当做scsi设备来驱动的.另外你还需要让你的内核支持即插即用,这些都是不小的空间开销,我的建议是你放弃一两个你不用的文件系统.总之,你最后编译出来的内核大小最好不要超过900K,否则你在busybox里只能编译进去很少的命令.

  在我编译的busybox中,我编译进去120多个命令,基本上把busybox支持的命令都包括进去了.加上小系统所必需的文件系统目录,/dev下的设备文件,以及/etc下几个必需的配置文件,做成ramdisk压缩后的大小是440多K, 加上900K左右的内核刚好可以放入一张1440K软盘,请注意,你应该留下至少50K的空间,因为我们要在软盘上创建一个ext2文件系统,而文件系统本生需要占据大概25K的磁盘空间.另外lilo的引导文件boot.b的大小是5.7K,还有装上lilo后自动产生的map文件也要10多K的空间, 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 自带的2.4.20版的内核.

  为什么我不用最新的2.6的内核?

  一开始我也企图用最新的内核,但是通过试验我发现,在用最新的2.6.9内核的情况下,我编译一个all-no的(即所有内容都选N,不支持任何硬件,只有一个最基本的内核)最小化内核就要460K左右,如果我在这个基础上再加入几种文件系统和必要的驱动,那么内核的大小就不能装下一张1440K

  的软盘,而我用2.4.20的内核编译一个最小化的内核只需要217K,的大小.如果优化了gcc参数他还能再小些.这样我就立即省下了200多K的空间,在平时,200多K的内容微不足道,但是在babylinux里,这个数目是整个空间的 1/7,相当于一个reiserfs文件系统模块的大小.当然,我也尝试了2.2以及更老的内核,但是他们缺少我需要的东西,因此最后权衡下来用2.4的内核是比较合理的.如果你用的是2.6内核的FC系统,那么最好还是去下载一个2.4版的内核,www.kernel.org 有各个时期的内核可以下载.

  4, 内核的配置

  如果你对linux内核的配置和编译已经很熟悉了,请跳过这一段,直接看busybox的编译.

  以root身份登录系统

  进入/usr/src/linux目录

  [root@gucuiwen root]# cd /usr/src/linux

  如果你下载了一个2.4版本的内核,为了避免麻烦,请将他拷贝到/usr/src下,然后接压缩,再做一个指向他的名为linux的链接.虽然这并不是必需的,但是根据我以往的经验,如果我把linux源代码放在其他目录下解开并编译,偶然会有一些莫名其妙的小问题发生.

  #cp linux-2.4.20.bz2 /usr/src/

  #cd /usr/src

  #tar xfvj linux-2.4.20.bz2

  如果是tar.gz格式,可以这样解开

  #tar xfvz linux-2.4.20.tar.gz

  为了方便,做一个到目录linux-2.4.20的连接:

  #ln -s linux-2.4.20 linux

  进入linux源代码目录:

  #cd linux

  清理源代码树:

  #make mrproper

  运行配置程序:

  #make xconfig

  code maturity level options

  先选择N,当我们配置好常规的东西,要加入framebuffer支持时再将这一项选择Y,因为在2.4.20中,framebuffer支持尚属于实验性代码.如果不在code maturity level options选择为Y,将不能配置framebuffer.

  Loadable module support

  选择N,为了简化系统的制作,我在这个项目中不选择可加载内核模块的支持.

  processor type and features

  processor family 中选择你需要的CPU类型,如果你想让老至386,新到P4的CPU都能运行babaylinux那么请选择386CPU,否则请按自己的实际情况选择.

  其他选项都选择N.这些在babylinux中都是不需要的.

  General setup

  networking support 选择Y

  PCI support 选择Y 除非你不用PCI设备,不过一般人都是需要的,因为现在网卡大部分是PCI的.

  System V ipc 选择Y

  systrl support选择Y

  kernel support for ELF 选择Y

  其余内容都可以选择N,如果有特殊需求,比如的网卡是ISA的,那么请将相应的内容选上.但是不能贪心,时刻牢记,我们能利用的空间只有 1440K ,内核的大小绝不能超过 900K,任何不必要的东西都应该从内核中去除.

  memory technology devices (MTD)

  Parallel port support

  Plug and Play configuration

  以上三个大项中的所有内容选择N

  block devices

  Normal floppy disk support

  Loopback device support

  RAM disk support

  initial RAM disk (initrd) support

  Per partition statics in /proc/partitions

  以上几项选择Y,其余全部选择N.

  这里的选项比较重要,我想重点说明一下.对于软盘的支持,那是不必说的,那是必备的.

  loopback device 即回环设备,我们平时用命令

  #mount -o loop somecd.ISO /mnt/cdrom

  挂装光盘映象文件,或者其他文件系统映象文件时就用到了内核中的loopback 模块,如果没有编译进这个模块,你将不能用上面的命令挂装光盘映象和文件系统映象.

  个人认为这个功能是非常重要的,所以编译了进去.

  RAM disk support 即内存磁盘(比较贴切的说法是虚拟磁盘,即拨出一部分内存当做磁盘用).这是制作babylinux项目中的核心内容,由于一张软盘的空间有限, babylinux的根文件系统是用gzip压缩法高度压缩的,在运行时,将解压缩后的文件拷贝到一个RAM disk运行,所以在运行时,你在根文件系统上的所有操作实际上是在内存上进行的.但是在形式上和在真正的磁盘上运行一样.只不过放在RAM disk上的所有内容会在系统关机后全部消失.

  不仅在运行babylinux时用到ramdisk,我们在制作压缩的根文件系统时也要用到ramdisk,学习ramdisk的使用是做一个babylinux的重要目的之一. 在linux中,还支持另外一种虚拟磁盘,叫做shm,

  (shared memory),这种虚拟磁盘机制比ramdisk更加先进,ramdisk的大小是固定的,由编译内核时候的default ram disk size 决定.默认为4096K(4M),也可以在内核装载前加上ramdisk_size=参数来决定他的大小,但是系统一旦启动,ramdisk的大小是不能改变的,而shm的大小却动态的改变.默认情况下为物理内存的一半,当系统需要更多内存的时,他就自动缩小.系统内存富余时,他自动增大,这样可以充分灵活的利用内存空间,shm通常用来作为系统的磁盘高速缓存,存放系统运行中的临时文件等.redaht 的linux在默认情况下都有shm的支持,可以用mount和df察看他的挂装点和大小,如下命令:

  [root@gucuiwen linux]# mount

  /dev/hda1 on / type ext3 (rw)

  none on /proc type proc (rw)

  usbdevfs on /proc/bus/usb type usbdevfs (rw)

  none on /dev/pts type devpts (rw,gid=5,mode=620)

  /dev/hda6 on /home type ext3 (rw)

  /dev/hda5 on /oracle type ext3 (rw)

  none on /dev/shm type tmpfs (rw)

  /dev/hda7 on /var type ext3 (rw)

  [root@gucuiwen linux]# df -h

  文件系统 容量 已用 可用 已用% 挂载点

  /dev/hda1 2.9G 2.7G 26M 100% /

  /dev/hda6 3.8G 1.8G 1.8G 50% /home

  /dev/hda5 5.7G 677M 4.8G 13% /oracle

  none 125M 0 125M 0% /dev/shm

  /dev/hda7 711M 91M 584M 14% /var

  虽然shm有这么多的优点,我还是选择了ramdisk,因为ramdisk可以很方便地在系统启动的时候加载,而shm却没那么容易,下面就来讲一下关于内核启动时加载ramdisk映象的相关内容.

  initial RAM disk (initrd) support

  即初始化ramdisk支持,这个选项让内核有能力在内核加载阶段就能装入RAMDISK,并运行其中的内容,否则只能在系统运行阶段用ramdisk ,我们平时在编译了一个新内核后,如果你的根文件系统用的是ext3,而你没有把ext3编译进内核,而只作为一个模块编译了,那么就需要用 mkinitrd命令做一个initrd (initializtion ramdisk),这个ramdisk里放了ext3的模块,这样内核在加载根文件系统前就能正确识别ext3文件系统.否则,内核加载的最后一步就会出现kernel panic cant not find init .... 的错误.

  在babylinux项目中,这个选项是必需的,这里的作用是把解压的根文件系统映象装入ramdisk.

  Per partition statics in /proc/partitions

  这个选项不是必需的,但是我发现如果我不把这个功能编译进内核,那么当我在挂装文件系统的时候会有些小问题,比如我不能以简写的挂装命令来挂装文件系统. 我不确定到底是不是这个选项的关系,但是把这个选项编译进内核只增大一点点内核空间,所以为了避免麻烦,我把他编译了进去.

  Multi-device support (RAID and LVM)

  Cryptography support (CryptoAPI)

  这两个大项全部选择N,因为在个人用PC上,及少牵涉到这两项,如果你真的有RAID设备或者LVM,那么就自己摸索着配置一下吧.

  Networking options

  这一大项中,只需要把下列项目编译进内核:

  Packet socket :mmapped IO

  TCP/IP networking

  对于IP:advanced router这项,如果你想重点把babylinux用做静态路由软件,那么把这项编译进去,而对于network packet filtering (replaces ipchains)这一项,没有必要编译进去了,因为busybox没有提供iptables工具来设置包过滤防火墙.同样,unix domain sockets这项也不必选择,只有运行X的情况下才需要选这项.

  Telephony Support 选择N

  ATA/IDE/MFM/RLL support

  选择Y,然后下面的'IDE,ATA and ATAPI Block Devices'按钮就被激活

  下面几项请选择Y,其余都可以是N.

  Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support

  Include IDE/ATA-2 DISK support

  Auto-Geometry Resizing support

  Include IDE/ATA CDROM support

  如果你的内核要运行在一台很老的pentium或486上,请把CMD640 chipset bugfix/support编译进去,因为那时候主板的CMD640 IDE控制芯片大多有莫名其妙的BUG,把这项编译进去会修复这个bug.

回复评论 (5)

顶一下芝锐!~

LZ的名字起得真NB!~
QQ:1625345502
点赞  2011-6-5 23:46
一看就晕哦
点赞  2011-6-6 07:51

比较高难,顶一下

http://shop34182318.taobao.com/ https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
点赞  2011-6-7 17:44

回复 4楼 ddllxxrr 的帖子

的确很难 反正俺看不懂哦
点赞  2011-6-7 18:10
顶楼主,放心,不顶肺
点赞  2011-6-7 18:38
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复