从源代码打造一个最小化的Linux系统实作篇 Greg O'Keefe, gcokeefe@postoffice.utas.edu.au 2000年09月第0.8版 _________________________________________________________________ 以下就是从源代码中打造一个最小化的Linux系统的操作说明.它曾经是 [1]从加 电启动到Bash提示符(From PowerUp to Bash Prompt)的一部分.但是我将它们分 离开来,以便使得它们更简短而更为集中化.我们在此所要打造的系统是非常小 的,而且并不准备作为工作产品来使用.如果您想从头开始打造一个有实际用途 的系统,请参阅Gerard Beekmans所撰写的 [2]Linux空手道实作指南篇 (Linux From Scratch HOWTO). _________________________________________________________________ 1. 您所需要具备的条件 2. 文件系统 3. MAKEDEV(设备生成器) 4. 内核 5. Lilo系统引导器 6. Glibc库 7. SysVinit初始化脚本包 8. Ncurses库 9. Bash命令解释器 10. Util-linux (getty 和login) 11. Sh-utils 12. 可用性商榷 13. 更多信息 * 13.1 随机小技巧 * 13.2 资源链接 14. Administrivia * 14.1 版权声明(Copyright) * 14.2 主页 * 14.3 您的反馈意见 * 14.4 鸣谢录 * 14.5 修订历史记录 * 14.6 未来计划(TODO) _________________________________________________________________ 1. 您所需要具备的条件 我们首先要安装一个Linux发行套件比如小红帽(RedHat)到一个分区上,然后 使 用它来在另一个分区上打造一个新的Linux系统.我将我们所要打造的系统 称为 目标(target)而把我们所使用来打造新系统的系统称为源头(source). 可别把这 个源头(source)同我们同时使用的源码(source code)相 混淆了哦.:) 因此,您得需要一台具有两个独立分区的机器.如果可能,请尽量使用一台 没有 重要资料在里头的机器,以免数据受损.您可以使用一个已经存在的 Linux系统 作为源头系统,但是我并不推荐这种方式.如果您不慎遗漏了我们 打造的指令的 某些参数,您有可能会意外地在这个系统上安装了一些没有必要 的东西,有可能 会导致不兼容和冲突. 旧型的PC机硬件,大部分的486机器或者更早的机型,其BIOS都有一些极其 烦人 的限制.它们没有办法读取硬盘超过前512兆之后的空间.当然,这个 对 于Linux来说并不是什么大问题,因为只要Linux能够引导启动了,将使用 Linux 自己的磁盘IO,略过BIOS的调用.但是为了能够让这些旧型机器能够 引导Linux ,那么Linux内核必须存放在硬盘的前512兆之前的某个位置.如果您 正好有这么 个旧型机器,您得准备好一个独立的且完全在前512兆范围内的 硬盘分区,并将 其挂载为/boot.其它的分区就可以在任何位置, 可以任意处理而不必担心是在 硬盘的什么位置了. 上一次我打造这个系统时,所使用的源头系统是小红帽6.1(RedHat 6.1), 我安 装了基本系统,附加有以下软件包∶ * cpp (C++编译器) * egcs (增强型C编译器) * egcs-c++ (增强型C++语言编译器) * patch (打补丁程序) * make (编译批处理解释器) * dev86 (设备文件包) * ncurses-devel (ncurses库开发包) * glibc-devel (glibc库开发包) * kernel-headers(内核源码头文件包) 我还安装了X Window视窗系统和Mozilla网络浏览器以便更轻松地阅读文档, 而 实际上这两个东东并不是必要的.在我竣工之时,这个源头系统大概使用 了350 兆的磁盘空间(看起来是多了一些,可是我还在纳闷为什么呢). 竣工之时的目标系统占用了650兆磁盘空间,但是这个数值包含了所有的源码 以 及中途打造出来的文件.如果空间比较紧凑,您应该在每个软件包都打造 完毕之 后执行一下make clean来清除临时文件.当然了,我对这个 也是有点吃惊的. 最后,您的准备好我们所要用来打造系统的源码包.这些就是我在本文所讨论 的 软件包.这些软件包都可以从源码盘里面找到,或者从国际互联网上找到. 我会 给出美国的站点和位于澳大利亚的镜像站点的地址. * MAKEDEV (设备生成器包) [3]美国 另一个是 [4]美国 * Lilo (Linux引导器包) [5]美国, [6]澳大利亚. * Linux内核包(Kernel) 使用 [7]主页上所列举的镜像站点 而最好不要使用 [8]美国 站点下载,因为这些地方通常是超负荷运转的. [9]澳大利亚 * GNU libc库包 其本身,以及liuxthreads线程附加库可在以下地址下载到∶ [10]美国和 [11]澳大利亚 * GNU libc附加库包 您可能还会需要linuxthreads线程附加库和libcrypt加密 附加库. 如果libcrypt没在那个地方找到,那就是因为美国出口法律限制的 原因, 那么您就可以从这里弄到 [12]libcrypt加密附加库. 通 常linuxthreads线程附加库跟libc库是放在同一个地方的. * GNU ncurses [13]美国 [14]澳大利亚 * SysVinit (初始化脚本包) [15]美国 [16]澳大利亚 * GNU Bash (命令解释器包) [17]美国 [18]澳大利亚 * GNU sh-utils (命令解释器工具包) [19]美国 [20]澳大利亚 * util-linux (Linux常用工具包) [21]另外某个地方 [22]澳大利亚本软件包 包含有agetty和login. 总结一下,您所需要的就是∶ * 一台具有两个分别是400兆和700兆独立分区的机器,或许您可能会需要少一 些. * 一个Linux发行套件(譬如一个Red Hat光盘)和安装方式(譬如一个光驱) * 以上所列举的源码的tar包. 我假定您可以自己安装源头系统,而用不著我来帮忙.从这里开始,我假定源头 系统已经安装好了. 本小项目的第一个里程碑就是使得内核启动起来然后死翘翘,因为它没找到 init初始化程序.也就是说我们得安装一个内核和安装lilo.为了 顺利安 装lilo,我们要用上在目标系统上/dev目录下的设备文件. lilo需要它们来实现 底层必需的磁盘存取来写入引导扇区.MAKEDEV正是用来 创建这些设备文件的脚 本程序(您当然可以只需要从源头系统当中复制出来, 不过这可是作弊不劳而获 哦).但是最重要的事情就是,我们需要一个文件 系统来放置所有的这些东西. 2. 文件系统 我们的新系统是要安装在文件系统上的.因此首先我们得使用命令mke2fs来 创建 文件系统,然后将其挂载到某个地方.我建议是挂载到/mnt/target这个 目录上 .接下来的操作中,我假定就用这个目录了.为了节省您的宝贵时间,您可以 在/etc/fstab文件里面添加上这一项,以便每次源头系统启动的时候就能够 自动 将这个目录挂载上. 当我们启动了目标系统,放置在/mnt/target上的所有东西就会被当成了 放置 在/根目录上. 我们需要在目标系统上建立固定的目录结构.请参阅"文件层次结构标准(简 称FHS, File Heirarchy Standard)",见于 [23]文件系统一节来了解 详情,或 者只需要cd切换目录到目标系统所挂载的地方然后尽管执行以 下命令∶ mkdir bin boot dev etc home lib mnt root sbin tmp usr var cd var; mkdir lock log run spool cd ../usr; mkdir bin include lib local sbin share src cd share/; mkdir man; cd man mkdir man1 man2 man3 ... man9 因为FHS标准和大部分的软件包在手册页(man page)放置位置处理上并不一致, 因此我们需要做一个符号连接∶ cd ..; ln -s share/man man 3. MAKEDEV(设备生成器) 我们要把源代码放置到目标系统的/usr/src目录下面.因此,举个例 子吧,如果 您的目标系统是挂载在/mnt/target这个地方,且您的tar 包是放在/root里面, 那么您要做的就是∶ cd /mnt/target/usr/src tar -xzvf /root/MAKEDEV-2.5.tar.gz 然后就把这些tar包复制到您要解开它们的地方就行了.千万别迷糊了哦.;-> 当您安装软件的时候,通常情况下您会把它们安装在正在使用的系统上.但是我 们 并不想这么做,因为我们是要把/mnt/target当做根文件系统(root filesystem),就是要把这些软件安装到这个地方.不同的软件包有不同的处理 方式.比如说MAKEDEV设备生成器包,您要做的是∶ ROOT=/mnt/target make install 您得先在这个包当中的README说明文件和INSTALL安装说明文件当中查出这些选项 , 或者执行命令./configure --help查看帮助说明. 查看一下MAKEDEV包当中的Makefile文件,看看它是怎样处理我们在命令 行当中 设置的ROOT变量的.接著通过执行man ./MAKEDEV.man来 查看一下它的手册页, 看看它是怎么起到作用的.您会发现生成我们自己的设备的 方式就是执行cd /mnt/target/dev然后./MAKEDEV generic. 请使用ls命令来看看它都为我们生成 了哪些设备文件吧. 4. 内核 下一步就是生成内核了.我假设您以前是做过编译内核这种事的,所以我就长话 短说 了.如果要启动的内核已经准备好的话,那么要安装lilo就会更容易.请返 回到 目标系统的usr/src目录,然后在那儿解开Linux内核源码.进入Linux 源码 树(cd linux)然后使用您最喜欢的方式配置内核,比如make menuconfig.如果您 想让自己的轻松一些,那么您可以为自己配置一个没有 模块的内核.如果您已经 配置了模块,那么您就得编辑Makefile文件, 找出INSTALL_MOD_PATH并将其设置 为/mnt/target. 现在您可以执行make dep,make bzImage了.如果您设置了模块 项,可以再执 行make modules,make modules_install.把内核 映象文 件arch/i386/boot/bzImage和系统函数映象文件System.map 复制到目标系统 的boot启动目录/mnt/target/boot下面,然后准备安装 系统引导器lilo了. 5. Lilo系统引导器 Lilo包里面带有一个很小巧的脚本,名叫QuickInst.请把lilo源码包 解压到目 标系统的源代码目录/mnt/target/usr/src下面,然后执行该 脚本,方法是 ∶ROOT=/mnt/target ./QuickInst.它会询问您一些关于 您想怎样安装lilo的问 题. 切记∶因为我们已经设置ROOT根系统为目标系统分区了,所以您回答 提问时所给 出的文件名同它是密切相关的.比如当它询问您默认想启动哪个内核 的时候,您 的回答应该是/boot/bzImage,而并不是 /mnt/target/boot/bzimage哦.我发现 这个脚本里面有个小错误,它 会提示说∶ ./QuickInst: /boot/bzImage: no such file 但是您甭理这个提示就是了,不会有事的. 我们该让QuickInst把引导扇区(boot sector)放在何处为妥呢? 当我们重启时, 我们希望可以选择引导进入源头系统或者目标系统或者 其它共存于同一台机器的 其它系统.而且我们还希望我们要使用所编译 的lilo来引导我们新系统的内核. 我们怎么把这两件事情合而为一呢? 让我们先跑一小会儿题,看看lilo在一个双 重启动的Linux系统上是怎 样引导DOS的.在这样的一个系统上的lilo.conf文件 的内容看 起来可能会跟下面的差不多∶ prompt timeout = 50 default = linux image = /boot/bzImage label = linux root = /dev/hda1 read-only other = /dev/hda2 label = dos 如果机器是这么安装起来的,那么主引导记录(MBR,master boot record)就可以 被BIOS读取并加载,然后MBR加载lilo启动引导器,而后者则给出一个提示.如果 您在提示后面输入dos,lilo就会从hda2加载引导记录,就加载了DOS. 我们所要做的事情跟上头是一样的,除了在hda2的引导记录应该是另外一个lilo 引导记录之外,也就是在QuickInst所询问要安装的那个.因此来自Linux 发行套 件的lilo会加载我们所编译安装的lilo,然后我们所编译安装的lilo就会 加载我 们所编译安装的内核.当您重启后,您会看到两次lilo的提示. 长话短说,当QuickInst询问您该把引导扇区(boot sector)放到什么地方 时,您 就回答目标系统所在的分区,比如说是∶/dev/hda2. 现在来修改您的源头系统上的lilo.conf配置文件,那么看起来会有点像 这个样 子∶ other = /dev/hda2 label = target 修改完毕,接著执行lilo安装LILO.我们应该可以第一个引导进入目标系统了. 6. Glibc库 下一步我们要安装init,但是同运行在Linux上几乎全部的程序一样, init使用 了GNU C语言库glibc所提供库函数,因此我们先得把这个东东 安装上. Glibc库是一个很大而且很复杂的软件包.在我那个旧型的带8兆内存的386sx/16 机器 上,得花掉我90个小时来完成编译工作.但是在我那带有64兆内存的赛 杨(Celeron) 433上只花掉了33分钟.如果您只有8兆内存(或者少得让人打颤的容 量)的话,那就 做好苦熬的准备吧. glibc的安装文档建议在不同的独立分离目录里面编译.这样做就能够让您很轻松 地再次编译,因为您可以该目录下面接著编译.您可能也会想这么做,因为可以 为 您节省大约265兆的磁盘空间哦! 跟平常一样,把glibc-2.1.3.tar.gz(或者其它版本)这个tar包解压到 /mnt/target/usr/src这个目录下面.接下来,我们得把附加库也解压 到glibc库 目录下面.所以先cd glibc-2.1.3,然后接著在这个目录下面 把glibc-crypt-2.1.3.tar.gz和glibc-linuxthreads-2.1.3.tar.gz 这两个tar包 解开. 现在我们就可以生成编译目录,设置选项,执行make编译和安装glibc库了.这些 都是我所使用过的命令,但是最好您自己阅读一下文档,确认最适合您的状况的 做法.然而在您开始前,您可能需要执行df命令来查看一下还有多少 剩余空间. 您还可以在编译并安装完毕glibc库之后再执行一次看看这玩意儿到底 得占多大 地儿. cd .. mkdir glibc-build ../glibc-2.1.3/configure --enable-add-ons --prefix=/usr make make install_root=/mnt/target install 注意了,我们还有别的方法来告知一个软件包该装到什么地方. 7. SysVinit初始化脚本包 编译并安装SysVinit可执行代码是非常之简洁明了的.我偷懒一次,就给您 操作 命令吧.假定您已经解压并且进入SysVinit源码目录了∶ cd src make ROOT=/mnt/target make install 另外还有很多与init相关的脚本.在SysVinit包里面有一些工作 正常的范例脚本 ,但是您得自个儿手工安装了.它们在SysVinit源码树中 是有层次地布置 在debian/etc下面的.您只需要执行类似这样的 命令∶cd ../debian/etc; cp -r * /mnt/target/etc,直接把 它们复制到目标系统的etc目录下面就行了.当 然了,您最好是 在复制之前查看一下. 当重启之后,目标系统的内核就会加载init,一切都该各就其位了. 此时的问题 可能是脚本不能正常运行,因为没有命令解释器bash来 解释执行这些脚本.而 且init还会尝试执行getty,但是 根本就没有getty可供运行.请重新启动并确认 没有其它的错误. 8. Ncurses库 我们所需要的下一个东东是命令解释器Bash,而bash需要ncurses库,所以 我们 得先安装这玩意儿.ncurses库可以代替termcap处理文本屏幕的活计, 同时还通 过支持termcap调用提供了向后兼容性.为了拥有一个简洁新潮的 系统,我觉得 最好是禁止旧式的termcap方法.如果您后头要编译使用了 termcap的较老的应用 程序,您可能会不断地与麻烦为伴了.但是您至少会 知道什么东东使用了什么东 东.如果您必须要用,那么您可以重新编译 ncurses库,使其带有termcap支持. 我所使用的命令是∶ ./configure --prefix=/usr --with-install-prefix=/mnt/target --with-shar ed --disable-termcap make make install 9. Bash命令解释器 为了把bash安装到我原以为它该呆的地方,我花费了很多时间做了大量阅读和 思 考以及不断地尝试和出错,可谓是历尽千辛万苦啊.我说使用的配置选项是∶ ./configure --prefix=/mnt/target/usr/local --exec-prefix=/mnt/target --with-cu rses 一旦您已经编译并安装了bash之后,您需要生成一个符号连接,就象这样∶ cd /mnt/target/bin; ln -s bash sh.这是因为脚本通常头一句 是这么写著的∶ #!/bin/sh 如果您没有这么一个符号连接,那么您的脚本就不能运行,因为它们会去寻找 /bin/sh而非/bin/bash. 如果您愿意,您也可以到这里时重新启动一次.您会注意到脚本这一次确实运行 了.虽然您还是没能登录(login),这是因为还没有安装getty或者 login这些程 序. 10. Util-linux (getty 和login) 软件包util-linux包含有agetty和login.我们需要这两个 程序才能登录系 统(log in)和得到命令行提示符(bash prompt).在安装之后, 请在目标系统 的/sbin目录下为agetty建立一个符号连接到 getty.getty是所有Unix类系统当 中被认为应该呆在那个 地方的程序之一,所以生成连接的主意要强于改 动inittab来运行 agetty. 对于util-linux这个包,我剩下的一个问题就是该包的编译.这个包还包含 有more这个程序,而我没法让make进程给more 在目标系统上做一个指向ncurses 5库的连接,而不是在源头系统上指向 ncurses 4库的连接.我会努力克服这个困 难的. 您还得在目标系统上准备一个密码文件/etc/passwd.login 登录程序正是通过查 询该文件来确认您是否允许登录的.因为此次我们只是 打造一个玩具系统,所以 我们可以只设置根系统用户就够了,而且不需要任何 密码!! 只需要在目标系统 的密码文件/etc/passwd加上如下一行即可∶ root::0:0:root:/root:/bin/bash 所有的域是通过冒号(:)分隔开的,自左向右分别代表∶用户名称(user id), 密 码密串(password),用户号码(user number),用户群组号码(group number), 用户真实姓名(user's name),用户主目录(home directory)和缺省命令解释器 (default shell). 11. Sh-utils 我们所必须的最后一个软件包就是GNU sh-utils包.我们此时所需要从这个包里 面 得到的唯一的程序就是stty,它会在/etc/init.d/rc里面用到. 而后者是用 于改变运行级别和进入初始化运行级别的脚本.实际上我有一个而且 用过仅包含 有stty的软件包,但是却忘了是从何处得到的了.使用GNU的 软件包是个好主意 ,因为在里头还有其它您需要的东西,增加了这些东东会使得 系统可用性更好. 好了,打造完毕.您现在应该拥有一个可以启动并且能够提示您登录的系统了. 输入``root'',您就会进入命令解释器了.但是您做不了很多事情,甚至没有 一 个ls命令给您看看您的作品里面都有些什么东西.请连续按两次 TAB键,您就会 看到可用的命令了.这大概是这个系统中,我所发现的最令我 满意的事情. 12. 可用性商榷 看起来好像我们打造的是一个毫无用处的系统.说真的,要让它能够有实用 价值 也并不是什么难事.首先要做的事情之一就是您应该使得根文件系统(root filesystem)以可读写方式挂载起来.SysVinit软件包里面有干这活儿的脚本, 就在/etc/init.d/mountall.sh里面.还执行了一次mount -a 把所有 在/etc/fstab当中的条目以您所指定的方式挂载起来.请在 目标系统 的etc/rc2.d目录下生成一个类似S05mountall 的符号连接. 您可能会看到这个脚本会用到您尚未安装的命令.如果真是这样,找到包含该 命 令的软件包并安装之.请参看 [24]随机小技巧(Random Tips)这一小节,了解如 何 查找软件包. 看看在/etc/init.d里面的其它脚本.它们大部分都应该包含在任何 正经的系统 里面.一次添加一个,别忘了要确定添加下一个之前个个都运行 无误. 请对照文件层次结构标准(File Heirarchy Standard),请参看 [25]文件系 统(Filesystem)一节. 那里有一个命令列表,都是该在/bin和/sbin的命令.请 确定您已经把那里列举的所有命令都安装在系统上了.最好就是再找找相关 这类 问题的POSIX文档来看看. 从此,在这个系统里面添加更多必要的软件包就真是个事儿了.越是早些把编译 工具,比如说gcc和make这些添加进去就越好.一旦这些都 完工了,您就可以利 用目标系统来自我生息,就会越来越简单了. 13. 更多信息 13.1 随机小技巧 如果您的Linux系统上曾经使用RPM安装过有一个叫做thingy命令,而您 想获知这 个命令的源码来源,那么您就使用如下命令∶ rpm -qif `which thingy` 如果您有小红帽RedHat的源码光盘,那么您就可以使用下列命令安装源码包了∶ rpm -i /mnt/cdrom/SRPMS/what.it.just.said-1.2.srpm 这个命令会把tar包以及任何RedHat补丁包放到/usr/src/redhat/SOURCES 目录下 面. 13.2 资源链接 * 有一个关于从源代码编译软件的小型实作指南(mini-howto),就是 [26]软件 打造小小实作指南(Software Building mini-HOWTO). * 另外还有一个关于从一穷二白空手起家打造一个Linux系统的实作指南. 该 文更为集中于打造一个有实际应用价值的系统,而不仅仅是一个实习. 请看 ∶ [27]Linux系统空手道实作指南篇(The Linux From Scratch HOWTO). * [28]Unix文件系统标准(Unix File System Standard) 还有一个关于Unix文 件系统标准的 [29]链接.这个标准描述了在一个Unix系统中什么东东该呆在 什么位置 以及原因.它还描述了在/bin,/sbin等等目录中最小化 的要求. 如果您的目标是要打造一个小而全的系统,那么这个标准正是一个 好的参考 . 14. Administrivia 14.1 版权声明(Copyright) 本文版权所有,归属Greg O'Keefe.欢迎您在遵循 [30]GNU通用公共许可证(GNU General Public Licence)的各项条款的 前提下无需付费来使用,复制,散发或 者修改本文. 如果您在其它文档里面使用了本文的全文或者部分,请在鸣谢录提 提我就行了. This document is copyright (c) 1999, 2000 Greg O'Keefe. You are welcome to use, copy, distribute or modify it, without charge, under the terms of the [31]GNU通用公共许可证(GNU General Public Licence). Please acknowledge me if you use all or part of this in another document. 14.2 主页 本文最新的版本可在此找到∶ [32]From Powerup To Bash Prompt 本文的中译版可以找 [33]Linuxrat索取. 14.3 您的反馈意见 我很乐意从读者您那儿得知任何评论,改进意见和建议.请写信给我∶ [34]Greg O'Keefe 14.4 鸣谢录 本文所提及的产品名称是相应持有者的商标,在此我一并致谢. 我想对以下人员致谢,因为他们的帮助才有了这篇实作指南. Michael Emery 因其提醒我注意到Unios. Tim Little 因其提供了关于/etc/passwd的一些线索. sPaKr on #linux in efnet 因其发现syslogd需要/etc/services的支持以及介绍给我 使用短 语``rolling your own''来表述从源码打造系统. Alex Aitkin 因其引起了我对Vico以及他的``verum ipsum factum''(对编译进一步的 理解) 的注意. Dennis Scott 因其纠正了我的十六进制计算错误. jdd 因其指出一些拼写错误. 14.5 修订历史记录 0.8 * 最初版本.自"From PowerUp to Bash Prompt(从加电启动到Bash提示符)"实 作 篇分离独立出来. 14.6 未来计划(TODO) * 转换为docbook格式. References 1. http://www.linuxdoc.org/HOWTO/From-PowerUp-To-Bash-Prompt-HOWTO.html 2. http://www.linuxfromscratch.org/ 3. ftp://tsx-11.mit.edu/pub/linux/sources/sbin 4. ftp://sunsite.unc.edu/pub/Linux/system/admin 5. ftp://lrcftp.epfl.ch/pub/linux/local/lilo/ 6. ftp://mirror.aarnet.edu.au/pub/linux/metalab/system/boot/lilo/ 7. http://www.kernel.org/ 8. ftp://ftp.kernel.org/pub/linux/kernel 9. ftp://kernel.mirror.aarnet.edu.au/pub/linux/kernel/ 10. ftp://ftp.gnu.org/pub/gnu/glibc 11. ftp://mirror.aarnet.edu.au/pub/gnu/glibc 12. ftp://ftp.gwdg.de/pub/linux/glibc 13. ftp://ftp.gnu.org/gnu/ncurses 14. ftp://mirror.aarnet.edu.au/pub/gnu/ncurses 15. ftp://sunsite.unc.edu/pub/Linux/system/daemons/init 16. ftp://mirror.aarnet.edu.au/pub/linux/metalab/system/daemons/init 17. ftp://ftp.gnu.org/gnu/bash 18. ftp://mirror.aarnet.edu.au/pub/gnu/bash 19. ftp://ftp.gnu.org/gnu/sh-utils 20. ftp://mirror.aarnet.edu.au/pub/gnu/sh-utils 21. ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/ 22. ftp://mirror.aarnet.edu.au/pub/linux/metalab/system/misc 23. file://localhost/tmp/zh-sgmltools.415/BuildMin.txt.html#FHS 24. file://localhost/tmp/zh-sgmltools.415/BuildMin.txt.html#finding 25. file://localhost/tmp/zh-sgmltools.415/BuildMin.txt.html#FHS 26. http://www.linuxdoc.org/HOWTO/Software-Building.html 27. http://www.linuxfromscratch.org/ 28. ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/ 29. http://www.pathname.com/fhs/ 30. http://www.gnu.org/copyleft/gpl.html 31. http://www.gnu.org/copyleft/gpl.html 32. http://learning.taslug.org.au/power2bash 33. mailto:linuxrat@gnuchina.org 34. mailto:gcokeefe@postoffice.utas.edu.au