很难。
制作一个Linux发布版需要很高深的技术吗?
你能行。
好多人很想参与Magic Linux的开发,但多数不知从何入手。
其实很简单,你只需要一台供你支配的386以上配置且能够上网的电脑,
以及一个能够工作在其上的gcc环境。
对,这就够了,其他的软件都是在帮助你更快的、更简便的完成这个工
作。因为Linux源自于网络,发展于网络,能够上网才能得到Linux;Linux
自身及在其之上运行的大多数软件都是以源代码方式提供,且非gcc不能完成
内核的编译。拥有了这些,你就可以开始将这些源代码转化为可运行的内核、
Shell和一些应用软件,一点一点的就构成了一个完整的可用的Linux系统。
本文就是讲述如何开始这些工作的。我会假定你对Linux有一定的了解,
使用过如:cp、mv、rm等常用命令,曾经成功的在某个Linux系统中安装过某个
以源代码形式发布的软件,有一些C/C++或其他语言的经验。
另外,我所提到的gcc环境并不是单指gcc本身,而是一些与gcc相交织的
软件集合,它们共同工作,完成从源代码到可执行程序的转换过程,这个过程
一般被称之为创建。
第一章 工具链的制作
在正式开始前,我还要让你了解一个重要的知识,那就是编译器、连接器
和程序库他们三者之间的关系,如果你已经非常了解了,可以略过这些内容。
编译器只是将源代码翻译成对应的机器代码,但是这个时候这些代码是不能执
行的,因为既没有排列好执行顺序也没有指定从哪里开始;程序库实现了一些
程序必要的行为,比如你要输出一些内容到屏幕或从键盘获得输入等;连接器
将编译器产生的机器代码进行排序并告诉计算机(严格来讲是操作系统)从什么
地方开始执行,当程序需要输出一些内容到屏幕或从键盘获得输入时,请求程
序库完成这些事情。虽然这些说法显得很不专业,但是足以帮助你理解它们三
者之间的关系了。如果要获得更为专业、详细的知识请阅读相关专业书籍。
一个能够正常工作的gcc环境,需要三个源代码包的支持:binutils、
gcc和glibc,它们分别提供了连接器、编译器和程序库。其实他们并不仅仅提
供了这些内容:
binutils是一个二进制工具集合,包含了汇编器、反汇编器、连接器、
elf可执行文件分析器等重要工具,这些工具在进行Linux各种应用的开发时,
发挥着巨大的作用。你可以在/usr/bin目录中找到他们。
gcc是一个编译器集合,包含了C、C++、Objective-C、Fortran、Java和
Ada语言,同时为这些语言提供了一些标准库,如:libstdc++、libgcj,……。
gcc的C是Linux的标准编译器,linux内核完全依赖于gcc,其他编译器不能编译
linux内核。由于ABI(Application Binary Interface)的不兼容,其他编译器
也不适合Linux上的大多数软件的编译,尤其那些使用C++作为开发语言的软件系
统。目前Intel C/C++编译器开始兼容gcc的ABI,因此现在可以采用Intel C/C++
编译器创建Linux上使用C/C++开发的软件系统,但内核不行。gcc被安装在
/usr/bin、/usr/lib和/usr/include目录中。
glibc是提供系统调用和基本函数的C库,比如open,malloc,printf等等,
所有动态连接的程序都要用到它。它是用户程序与内核交流的标准接口。
这三个包在一起相互作用,来完成Linux的所有创建任务。除了glibc是自包
含(自己依赖于自己)的外,其他两个都要依赖于glibc,同时glibc会与具体的内
核版本有一定的依赖性,但这并不是绝对的。注意,他们都是用gcc编译,并用
binutils的连接器进行连接的。这里就比较有趣了,是先有鸡还是先有蛋呢?计
算机的发展史咱们就不必讨论了,在本章会详细介绍如何来让他们的代码执行起
来。但他们不是我们要的全部。
首先,你应该保证你有可用的gcc环境,能够用它让你的源代码变成可执行程
序。往往这需要你的计算机运行了某个版本的Linux发布。当然Windows也行,不
过会比较麻烦,我不建议你这么做,我也不会在这里讨论该怎么做。你也不要指
望你现在的这个gcc环境可以直接创建Magic Linux的所有,最后你得到的还是属
于你现在使用的这个发布。因为你现在的gcc创建产生后的程序,还是依赖于你现
有Linux发布版的glibc的。如果你说你现在就创建一个glibc不就行了吗?不信你
可以试试,当你安装完后,看看你的系统还能工作不?言归正传,你现在急需的
是能够帮助你产生MagicLinux的gcc。
1.准备环境
最好能在你的硬盘上划分出一个独立的分区,1G大小就够了。不过这个你都嫌
麻烦的话,你有1G以上的自由空间也行。现在我假设你划分了一个独立的分区。
来吧,创建/mnt/ml目录,并将你的新分区挂接到这上面(如果不了解挂接的话,
先找找资料,google一下mount)。
#mkdir /mnt/ml
#mount /dev/hda6 /mnt/ml (我假设你的新分区设备名为hda6)
cd到/mnt/ml目录并创建toolchain目录。
#cd /mnt/ml
#mkdir toolchain
在你的根目录创建一个连接到toolchain上(如果不了解,google一下ln)。
#ln -sv /mnt/ml/toolchain /toolchain
创建sources目录。
#mkdir sources
将binutils、gcc和glibc等的源代码复制到sources目录中。你可以在网络上找到它们,
google一下就行了。binutils的最新本版是2.18,源代码包可能是这样的文件名:
binutils-2.18.tar.bz2;gcc的最新本版是4.2.2,源代码包可能是这样的文件名:
gcc-4.2.2.tar.bz2;glibc的最新版本是2.7,源代码包可能是这样的文件名:
glibc-2.7.tar.bz2。
修改PATH环境变量。
#export PATH=/toolchain/bin:$PATH
PATH环境变量指定了shell命令的默认搜索路径。我们修改PATH环境变量使得在你的gcc
可用时,默认执行的是它,而不是系统自带的。
环境已经准备好了,我们开始吧。
2.创建新的gcc环境
为了创建完全独立于你现有系统的gcc环境,我们要创建两次binutils和gcc。他们创
建的次序是很重要的。我们现在进行第一次创建。进入sources目录。
#cd sources
2.1创建binutils
首先创建binutils,是因为在创建gcc和glibc时,它们会检测连接器和汇编器,以便决定
它们能够开启那些特性。整个过程大概需要5分钟(根据你机器的处理能力会有很大的不
同),占用200MB左右的磁盘空间。
解压缩源代码包,并进入解压后的目录:
#tar -jvxf binutils-2.18.tar.gz
#cd binutils-2.18
由于binutils在某些时候会有一点小问题,要修复它,需要下在一个补丁,对应2.18版本的
补丁是:binutils-2.18-configure-1.patch。
#patch -Np1 -i ../binutils-2.18-configure-1.patch
binutils的文档说应该在一个独立的目录中创建binutils。那就创建一个build目录。
#mkdir build
#cd build
现在为创建binutils做准备工作:
#CC=”gcc -B/usr/bin/”../configure \
--prefix=/toolchain --disable-nls --disable-werror
各选项的含义是:
CC=”gcc -B/usr/bin/”
告诉gcc使用你当前操作系统/usr/bin目录下的连接器。这个对于某些你使用的Linux
发布是很重要的,因为新创建的连接器ld可能与它所提供的gcc不兼容。
--prefix=/toolchain
这个参数告诉configure脚本,把binutils软件包中的程序安装到/toolchina目录中,也就是
/mnt/ml/toolchain。
--disable-nls
这个参数禁止了国际化(通常简称i18n)。此时根本不需要国际化支持。
--disable-werror
这个参数可以防止由于你的系统提供的gcc在产生警告事件时停止创建过程。
准备好了,我们可以创建了:
#make
创建完成后,安装:
#make install
别着急,还没完事。还得调整一下:
#make -C ld clean
#make -C ld LIB_PATH=/toolchain/lib
make参数的含义是:
-C ld clean
告诉make,删除所有子目录ld中编译生成的文件。
-C ld LIB_PATH=/toolchain/lib
这个选项重新编译ld子目录中的所有文件。LIB_PATH是Makefile的一个变量,
在命令行里制定他,可以覆盖默认值,并让它指向/toolchain/lib目录。这个变量
的值指定了连接器的缺省库搜索路径。
手工将新的程序复制到/toolchain/bin目录:
#cp -v ld/ld-new /toolchain/bin
好了,binutils创建完成了,你现在可以删除build目录了。但是不要删除binutils-2.18目
录,因为还要编译一次呢。退回到sources目录,准备gcc的创建工作。
#cd ../
#rm -rf build
#cd ../
2.2 创建gcc
连接器准备好了,但我们这次先不使用它,没有这个必要,因为我们还需要再次
创建gcc的,那时再用也不迟。这个过程大概会花费你50分钟的时间和650MB的磁盘空
间。
解压缩gcc的源代码包并进入源代码目录。
#tar -jvxf gcc-4.2.2.tar.bz2
#cd gcc-4.2.2
gcc与binutils一样,建议你在一个独立的目录中创建它。创建build目录。
#mkdir build
#cd build
为创建gcc做准备。
#../configure -prefix=/toolchain -with-local-prefix=/toolchain \
--disable-nls -enable-shared -enable-languages=c
各选项的含义是:
CC=”gcc -B/usr/bin/”
告诉gcc使用你当前操作系统/usr/bin目录下的连接器。也就是不使用你刚创建完
的那个连接器ld。这也是为了避免一些麻烦,可以参考创建binutils时对该选项的
说明。
--with-local-prefix=/toolchain
默认情况下,gcc会搜索两个include目录,一个是其安装目录下的include,这里是
/toolchian/include,另外一个是/usr/local/include。这个选项就是将/usr/local/include这个默
认搜索目录删除,而还是指向/toolchain/include。
--enable-shared
这个选项是为了创建出libgcc_s.so.1和libgcc_eh.a文件,因为下一个要创建的glibc需要
libgcc_eh.a,要是没有它就不会产生正确的结果。
--enable-languages=c
因为gcc是一个编译器集合,这个选项指定要创建何种语言的编译器。此时只需要C,
那就没什么可说的了。
准备好了,我们可以开始创建了:
#make
创建完成,安装:
#make install
最后你还应该创建一个符号连接cc到这个新的gcc,因为好多时候会用cc而不是gcc,这是为
了与Unix平台保持一致,便于程序的移植。
#ln -vs gcc /toolchain/bin/cc
好了,gcc创建完成了,接下来的创建过程会自动使用这个gcc的,因为你修改了PATH环境变
量。做些首尾工作,要不然你的磁盘可能吃紧了。
#cd ../
#rm -rf build
#cd ../
2.3创建glibc
前面我说过,glibc是自包含的,不会依赖于那个软件包。但是我也说过,它是用户程序与
内核交流的标准借口,因此对内核还是有一定依赖的。但是这并不相矛盾,因为只要是一个系
列的内核,基本上是没多少关系的,比如现今的2.6系列,它们会有提供统一的系统调用。
glibc需要了解内核的系统调用和这些调用相关的一些数据结构。Linux内核做得很好,不需
要有内核的二进制文件,一切都包含在了内核的公用头文件中了。这里说些题外话,如果你够
牛的话,其实完全可以不用glibc,只利用这些头文件就可以写出合适的用户程序的,甚至比利
用glibc的更为优秀。不过如果你的确那么牛,这篇文章也就不适合你了。
不多说了,下载一个内核源代码到你的source目录吧。最新的是2.6.23.12。
解压缩内核源代码包并进入源代码目录:
#tar -jvxf linux-2.6.23.12.tar.bz2
#cd linux-2.6.23.12
安装相关的头文件:
#make mrproper
#make headers_check
#make INSTALL_HDR_PATH=dest header_install
#cp -rv dest/include/* /toolchain/include
#cd ../
关于内核操作有什么不了解的,可以使用make help命令获得帮助。
解压缩glibc的源代码包并进入源代码目录:
#tar -jvxf glibc-2.7.tar.bz2
#cd glibc-2.7
与gcc一样,还需要单独的目录来创建,建立build目录:
#mkdir build
#cd build
由于glibc不再支持i386体系了,因此它的开发者们建议在创建glibc是,最好使用-march=i486编译选
项。的确如此,如果你不指定这个选项,会创建失败。但是毕竟现在i486用的已经很少了,我们
也可以不考虑兼顾,为了进一步提高性能,我建议使用-march=i686编译选项。你可以通过下面的
命令完成这些:
#echo “CFLAGS += -march=i686” > configparms
接着,为创建glibc做准备工作:
#configure -prefix=/toolchain -disable-profile -enable-add-ons \
--enable-kernel=2.6.0 -with-binutils=/toolchain/bin -without-gd \
--with=headers=/toolchain/include -without-selinux
各选项的含义如下:
--disable-profile
忽略掉profiling信息相关的库文件创建,这个时候一般不需要。
--enable-add-ons
这个指示glibc使用附加的NPTL(本地POSIX线程库)包作为线程库。
--enable-kernel=2.6.0
告诉glibc要支持2.6.x内核。
--with-binutils=/toolchain/bin
这个参数并不是必要的,只是为了防止在创建glibc时用错了binutils程序。这里保证使用刚才创建
好的binutils。
--without-gd
保证不生成memusagestat程序,这个程序会产生对你现有系统的依赖。
--with-headers=/toolchain/include
这个选项就是告诉glibc,使用刚刚安装的内核都文件创建自己。
--without-selinux
不提供SELinux特性的支持,因为toolchain是不需要的。
在这个阶段你要是看到下面的警告你就不用管它,跟你没有任何关系,也不会有什么影响的。
configure: WARNING:
*** These auxiliary programs are missing or
*** incompatible versions: msgfmt
*** some features will be disabled.
*** Check the INSTALL file for required versions.
创建软件包:
#make
在安装glibc的过程中,它会警告缺少/toolchain/etc/ld.so.conf文件。其实没什事,不会有什么影响,只要提
供一个空文件它就闭嘴了:
#mkdir -v /toolchain/etc
#touch /toolchain/etc/ld.so.conf
一切顺利就可以安装了:
#make install
恭喜你,又干了一件了不起的事情,不过别忘了收尾阿,空间有限。
#cd ../
#rm -rf build
#cd ../
Magic Linux开发入门指南(二)2.4 让这个新的gcc环境能够真正的工作起来
编译器、连接器、程序库都创建好了,可以开始创建MagicLinux了吧?呵呵,不行!这个
新的gcc环境还没有真正工作起来呢。不信,我们做一个实验试试。编写一个最简单的C代码:
#echo 'main(){}' > ttt.c
#gcc ttt.c
#readelf -l a.out
看看结果,是不是有一行类似下面的内容:
[Requesting program interpreter:/lib/ld-linux.so.2]
这是不对的。readelf是分析elf可执行文件(Linux下可执行文件的格式)格式的工具,-l选项是
用来显示可执行文件各段头内容的,通过它可以了解一个可自行文件的依赖关系。上面的结果
表明新的gcc产生的可执行文件还是依赖于你现有系统的ld-linux.os.2,这是glibc的一部分。
这是为什么?该怎么办?
问题在创建binutils是就已经作了一些解决,但是还没有完全解决。回想一下,在安装完
binutils后,还做了如下操作:
#make -C ld clean
#make -C ld LIB_PATH=/toolchain/lib
#cp -v ld/ld-new /toolchain/bin
创建了一个ld-new,而且还复制到了/toolchain/bin下,这个ld-new就是关键,执行下面操作:
#mv -v /toolchain/bin/{ld,ld-old}
#mv -v /toolchain/$(gcc -dumpmachine)/bin/{ld,ld-old}
#mv -v /toolchain/bin/{ld-new,ld}
#ln -sv /toolchain/bin/ld /toolchain/$(gcc -dumpmachine)/bin/ld
这就使得接下来创建的程序都使用/toolchain/lib中的程序库了。可是为什么要现在才作上述
操作呢?因为在这之前,glibc还没有被创建,/toolchain/lib还不存在。
连接器搞定了,编译器还不行呢。这里我要多说几句。程序库分为两种,即静态库和动态
库。静态库就是在程序在被创建时由连接器确认它们的关系,并将它们组合在了一起成为一个
整体;动态库则不同,是在程序运行时,由动态连接器确认它们之间关系,它们是完全独立的
个体。这里不要将连接器和动态连接器弄混了,连接器是binutils提供的ld程序,而动态连接
器则是glibc提供的。连接器的名称通常是 ld-linux.so.2,在不怎么流行的平台上则可能是
ld.so.1,而在新的64位平台上更可能是别的完全不同的名称。为什么这时候编译器还不行呢?
因为编译器为了创建不同的程序,即要使用连接器也要使用动态连接器,所以要还要告诉编译
器动态连接器在哪里。现在的这个gcc还只是知道你所用系统的动态连接器的位置,你现在应
该告诉他新的在哪里。这是通过gcc的specs文件完成的。
通过下面的指令来产生gcc的specs文件:
#gcc -dumpspecs > `dirname $(gcc -print-libgcc-file-name)`/specs
直接将specs文件放入了它所起作用的位置。可以执行下面的命令获得这个路径:
#dirname $(gcc -printf-libgcc-file-name)
打开spces文件,修改所有类似“/lib/ld-linux.so.2”内容为
“/toolchain/lib/ld-linux.so.2”。你可以用任何你熟悉的方式作,我有一个简单的方法如
下:
#gcc -dumpspecs | sed 's@/lib/ld-linux.so.2@/tools&@g' \
> `dirname $(gcc -print-libgcc-file-name)`/specs
这几个程序简单的组合,利用管道和输出从定向机制,就完成了所有的操作。这就是类Unix系
统的精髓与魅力所在,各位读者体会一下,如果想了解更多Unix精髓,建议看一下《Unix编程
艺术》一书。
动态连接器问题解决了,还有一些麻烦问题需要处理,要不然,这个新的gcc环境还是对你
现在运行的系统有关系,因为gcc的fixincludes脚本在创建gcc的过程中,由于某些原因把你系
统里的头文件复制给这个新的gcc环境中去了。这就是好心办坏事,不过不用担心,是有办法挽
救的。执行下面的命令会将fixincludes产生的影响处理掉,当然,如果fixincludes没有帮倒
忙,下面的命令也不会作坏事,破坏这个新生的gcc环境:
#GCC_INCLUDEDIR=`dirname $(gcc -print-libgcc-file-name)`/include
#find ${GCC_INCLUDEDIR}/* -maxdepth 0 -xtype d -exec rm -rvf '{}' \;
#rm -vf `grep -l "DO NOT EDIT THIS FILE" ${GCC_INCLUDEDIR}/*`
#unset GCC_INCLUDEDIR
这下好了,马上检测一下:
#gcc ttt.c
#readelf -l a.out
如果你能看到如下结果就证明成功了:
[Requesting program interpreter:/lib/ld-linux.so.2]
如果没看到这样的结果,没办法了,从新创建gcc吧。注意查看一下你现在的PATH环境变量,是
否与“准备环境”一节相符。
最后,应该收一下尾了。
#rm -v ttt.c a.out
2.5 继续工作
这个时候你的全新的gcc环境可以工作了,但是我们前面说过,还需要再创建一次binutils
和gcc,这是因为你的binutils和c编译器还没有脱离你现有的系统,同时gcc也只支持c编译,
只是临时的过度产物。现在就要开始真正的工作了,这次创建的binutils和gcc真正的与你的系
统脱离了,而且你要用它们来完成接下来很多重要程序的创建工作。
这次的创建如此之重要,你需要保证创建完成的binutils和gcc可以很好的工作。不要以为
你可以凭借经验作到这些,或者手工编写一些简单的程序测试一下,这是一个浩大且复杂的工
程。你真正需要的是一些现有的工具,来检测你创建完成的程序是否能用。你也不用担心这些,
几乎每个重要的开源软件都包含了测试套件,但是这些套件需要一些工具的支持,所以,你在
第二次创建binutils和gcc之前,要准备好这样的工具。它们是Tcl、Expect和DejaGNU,它们
为大多数软件的测试套件提供了支持环境,所以不管怎么样,我都要建议你创建它们。
Tcl,呵呵,和你家的电视机没什么关系。Tcl是Tool Command Language的简称,即工具
命令语言。Tcl是一个种很通用的脚本语言,它几乎在所有平台上都可以解释运行,功能强大。
首先,Tcl是一种简单的脚本语言,主要用于发布命令给一些交互程序,如文本编辑器、调试器、
Shell等;其次,Tcl是一个库包,可以被嵌入应用程序,Tcl的库包含了一个分析器,不但用于
执行内建命令的例程,还可以使用你扩充(定义新的过程)的库函数。
Expect是Tcl的一个扩展,为某些交互程序提供了自动交互功能。现代的Shell对程序提
供了最小限度的控制(开始、停止等),而把交互的特性留给了用户。这意味着有些程序你不
能非交互的运行,比如说passwd。有一些程序可以非交互的运行,但在很大程序上丧失了灵活
性,比如fsck。这表明Unix的工具构造逻辑开始出现问题。Expect恰恰填补了其中的一些裂痕,
解决了在类Unix环境中长期存在着的一些问题。不仅如此,不管程序是交互的还是非交互的,
Expect都能运用。这是一个小语言和类Unix系统的其他工具配合起来产生强大功能的经典例子。
DejaGNU是一个测试程序框架,如果这么解释你不明白的话,可以说成是Framework,如
果还不能明白,可以说DejaGNU有点类似MFC,但是它们实现的语言和目的不同。DejaGNU就是
为前面所说的测试套件提供了一个统一的框架,它也是Tcl的一个扩展。
另外,补充一句,大部分软件包,都会包含测试工具的,建议你执行以下,这样可以保证
你发布的系统有很好的可用性。
没什么可说的,创建它们吧。
2.6 创建Tcl
目前最新版本的Tcl是8.5.0,但是Expect还没有跟上脚步,所以建议使用8.4.17。源代码
包可能是这样的名字:tcl8.4.17-src.tar.gz。创建Tcl大概需要5分钟左右的时间,占据24MB
左右的磁盘空间。
解压缩源代码包,并进入源代码目录:
#tar -zvxf tcl8.4.17-src.tar.gz
#cd tcl8.4.17
现在为创建Tcl作准备工作:
#cd unix
#./configure --prefix=/toolchain
开始创建:
#make
安装:
#make install
为了让Tcl的一些扩展程序能够被创建,还需要安装Tcl的头文件,执行下面的操作:
#make install-private-headers
为了兼容性,还需要创建一个符号连接,如下:
#ln -sv tclsh8.4 /tools/bin/tclsh
Tcl创建完毕了,可以进行下一步的操作了。别忘了收尾工作:
#cd ../../
#rm -rf tcl8.4.17
另外,如果你对这个刚刚创建的Tcl有些不放心的话,在执行安装步骤之前可以测试一下:
#TZ=UTC make test
这个过程不是必需的,因为Tcl的测试程序在某些环境下会失败,而且这个测试也并不是很关
键。TZ=UTC 参数将时区设置为协调世界时(UTC),也就是格林尼治时间(GMT),但只是在运行
测试程序的时候才这样设置,这将确保时钟测试正确。
2.7 创建Expect
目前最新版本的Expect是5.44,但是官方说5.44在重入上还有一些问题,建议使用5.43。
源代码包可能是这样的文件名:expect-5.43.0.tar.bz2。不过5.43也有bug,需要修复一下,
补丁文件可能是这样的文件名:expect-5.43.0-spawn-1.patch。
整个过程比较快,不到一分钟就能搞定,也只需要4~5MB的磁盘空间。
首先解压缩源代码,进入源代码目录:
#tar -jvxf expect-5.43.0.tar.bz2
#cd expect-5.43
打补丁,修Bug:
#pathc -Np1 -i ../expect-5.43.0-spawn-1.patch
为了让Expect也能够完全独立,还需要对它的configure文件做些修改:
#cp configure{,.bak}
#sed 's:/usr/local/bin:/bin:' configure.bak > configure
作准备工作:
#./configure --prefix=/toolchain --with-tcl=/toolchina/lib \
--with-tclinclude=/toolchain/include --with-x=no
各选项的含义是:
--with-tcl=/toolchain/lib
告诉configure脚本,Tcl解释器在哪里。这里要用刚刚创建的Tcl,否则就会使用你系
统的Tcl了。
--with-tclinclude=/toolchain/include
告诉configure脚本,Tcl的头文件位置。
--with-x=no
不使用X图形系统支持,因为没有Tk(Tcl的图形用户界面组件)。
开始创建:
#make
安装:
#make SCRIPTS="" install
安装选项的含义:
SCRIPTS=""
这个选项防止安装 Expect 所补充的一些并不需要的脚本。
最后作些收尾工作,回到sources目录下。
另外,与Tcl一样,在安装之前,可以测试一下:
#make test
但是这也是不必要的,同样在某些环境下会失败。
2.8 创建DejaGNU
最新版本是1.4.4。源代码包的文件名可能是:dejagnu-1.4.4.tar.gz整个过程不到一分
钟,需要6~7MB的磁盘空间。
解压缩源代码包,进入源代码目录:
#tar -zvxf dejagnu-1.4.4.tar.gz
#cd dejagnu-1.4.4
过程很简单,执行下列操作:
#./configure --prefix=/toolchain
#make install
你要测试的话,执行下面的操作:
#make check
最后别忘了收尾工作。
2.9 第二次创建gcc
测试工具都已经创建完毕并安装好了,现在就要开始第二次创建gcc和binutils了。这次的创
建将使它们连接到新的glibc,这样就与你的系统彻底脱离了。
另外,这些测试工具会受到伪终端的影响,如果设置不正确的话,它们是拒绝工作的。为
了保证这一点,你可以执行下列命令:
#expect -c “spawn ls”
如果你得到下面的结果:
The system has no more ptys.
Ask your system administrator to create more.
这就说明你系统的虚拟终端没有配制好,就不要尝试运行测试工具了,因为那没有什么意义。
至于如何设置虚拟终端,已经超过本文的范围,请查看相关专业文档。
闲话不多说,首先应该创建gcc,毕竟这是一个编译器集,让它先工作起来似乎比较让人感
到宽心。但这时候要注意的是,这次创建的gcc是独立于任何系统的,那么首先就要让它规矩的
一点,不要与任何系统有什么丝毫牵连。这里有一个惹祸的根苗----fixincludes脚本。在gcc的编
译过程会运行fixincludes脚本来扫描系统头文件目录,并找出需要修正的头文件,然后把修正后
的头文件放到gcc专属头文件目录里。另外,由于gcc专属头文件目录会被优先搜索,结果就是
gcc使用的头文件是你系统的头文件,而不是你新创建的那个。这个好心办坏事的东西,还是把
它除掉算了。通过修改gcc/Makefile.in文件来完成这个操作。将“./fixinc.sh”用“-c true”替换。简
单的命令行操作如下:
#cd gcc-4.2.2
#cp -v gcc/Makefile.in{,.orig}
#sed ‘s@\./fixinc\.sh@-c true@’ gcc/Makefile.in.orig \
> gcc/Makefile.in
同时,第一次创建gcc时采用的是bootstrap方式,这种方式不仅仅是创建gcc,而是重复创建它几
次。它用第一次创建生成的程序来第二次创建自己,然后又用第二次创建生成的程序来第三次
创建自己,最后比较第二次和第三次创建的结果,以确保编译器可以毫无差错的编译自身。在
这种模式下,会默认带有一个-fomit-frame-pointer编译选项,而在关闭bootstrap模式后,这个选项
也会被取消,为了确保在关闭bootstrap模式时同样开启-fomit-frame-pointer编译选项,我们需要手
工修改gcc/Makefile.in文件,在“XCFLAGS=…”的内容后面添加“-fomit-frame-pointer”,可以通
过下面简单的指令完成操作:
#cp -v gcc/Makefile.in{,.orig}
#sed ‘s/^XCFLAGS=$/& -fomit-frame-pointer/’ gcc/Makefile.in.tmp \
> gcc/Makefile.in
还有需要注意的是,这次应该让gcc缺省使用toolchain的glibc,而且默认搜索目录也不要再有
/usr/include了,为了保证这一点,需要执行下列操作:
#for file in $(find gcc/config -name linux64.h -o -name linux.h)
do
cp -uv $file{,.orig}
sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
-e 's@/usr@/tools@g' $file.orig > $file
echo "
#undef STANDARD_INCLUDE_DIR
#define STANDARD_INCLUDE_DIR 0" >> $file
touch $file.orig
done
这比在创建完gcc再调整specs文件要好,这样可以保证新的动态连接器在这次创建gcc的时候就
用上。也就是说,随后的所有临时程序都会连接到新的glibc上。上述这些操作是非常重要的,
为了成功完成gcc的创建,一定要执行它们。
一切准备好好,进入正题吧。与第一次相同,还是要创建一个build目录:
#mkdir build
#cd build
做准备工作,执行configure脚本:
#../configure -prefix=/toolchain \
--with-local-prefix=/toolchain --enable-clocale=gnu \
--enable-shared --enable-threads=posix \
--enable-__cxa_atexit --enable-languages=c,c++ \
--disable-libstdcxx-pch --disable-bootstrap
各选项的含义是:
--enable-clocale=gnu
这个参数确保C++库在任何情况下都能使用正确的locale模块。
--enable-threads=posix
该选项使得C++的异常处理为线程安全的。
--enable-__cxa_atexit
用__cxa_atexit代替atexit来登记C++对象的本地静态和全局析构函数,这是为了完
全符合对析构函数的处理规定。它还会影响到C++ABI,并且这使得生成的C++共享库在
其他的Linux发行版上也能使用。
--enable-languages=c,c++
同第一次一样,此时又加入了C++编译器的支持。
--disable-libstdcxx-pch
该选项使得不创建libstdc++预编译头(PCH),它占用了很大的空间,而且还用不到它。
--disable-bootstrap
该选项关闭bootstrap模式。目前的gcc是默认开启bootstrap模式的。
开始创建gcc:
#make
此时可以执行gcc的测试套件了,不过肯定会有错误的,因为它太过全面了:
#make -k check
这里的-k参数使得测试套件即使遇到错误也要继续运行,直到完成。接下来执行安装程序:
#make install
好了,这项重要的工作完成了。可以执行2.4章所介绍的测试方法测试一下,如果执行下列命
令后:
#readelf -l a.out
如果可以看到类似下面的内容:
[Requesting program interpreter:/lib/ld-linux.so.2]
这说明你的操作成功了,如果没有,你的麻烦就大了。
好了,你已经完成了gcc的第二次创建工作,可以继续下面的工作了,最后别忘了收尾工
作,毕竟磁盘空间很重要啊。此次你可以将gcc的源代码都删掉了。
2.10 第二次创建binutils
这次创建binutils的步骤与第一次基本一样:
#cd binutils-2.18
#mkdir build
#cd build
#../configure -prefix=/toolchain \
--disable-nls -with-lib-path=/toolchain/lib
#make
不太相同的是在执行configure脚本时,带有一个--with-lib-path选项。这个选项指示configure脚本
在binutils创建过程中将传递给连接器的库搜索路径设置为/toolchain/lib,也就是使用新创建的程
序库。
接下来测试套件可以发挥一下了:
#make check
现在就可以安装了:
#make install
最后我们还要再创建一次连接器ld,以待后面使用:
#make -c ld clean
#make -c ld LIB_PATH=/usr/lib:/lib
#cp -v ld/ld-new /toolchain/bin
至于这些操作的含义在第一次创建binutils时已经说明了,不再复述。不要忘了收尾工作,这次
你可以将binutils的源代码目录一同删除了。
2.11 锦上添花
到了这个阶段,你的toolchain制作算是告一个段落了。由于前面说过,glibc是自包含
的,在这里也不需要再次创建它了。所有的工具都与你的系统脱离了关系,编译器、连接器
和程序库它们之间也交织起来可以很好的完成本职工作了,而且你还有了功能强大的测试套
件支持工具。
虽然这样看起来不错,但是你的toolchain还不能脱离你现有的系统独立工作,除非你非
常厉害,不过我想不出这个世界上是否真的有这样厉害的人。为什么呢?首先,一个用C写成
的完整程序,很少只有一个源代码文件。它提供的所有文件都要编译一遍,还要将它们产生的
目标文件再用连接器一一将它们与程序库组合成最终的程序。我描述这些步骤就已经很复杂了,
你要是手工这么操作将会是什么结果?其次,好多源代码包提供不止一个工具,这些工具大多
数情况下会互相依赖,有时还要依赖已安装的软件包,这些依赖关系都需要你来维护,如果只
有一两个还好说,如果上千个呢?往往这个数量比你想象的还要多。另外,Linux的大多数软
件的源代码都提供了很好的可移植性,它们们可以运行于所有类Unix系统之上。虽然都属于类
Unix系统,但不同的产品有不同的特性,好多软件需要依赖这些特性。这往往需要利用预编译
器帮忙决定采用那些特性适合何种系统。如果这些内容通过手工解决的话,工作量很可能是一
个天文数字。由此可见,你现在的这个toolchain创建一个完整的软件系统没有任何问题,但是
需要付出根本无法估量的冗繁的操作步骤才能完成某个简单的软件包的创建过程。
懒惰是人类文明进步与发展的原动力。Unix世界的程序员经过几十年的不懈努力,艰苦卓
绝,前仆后继,发明并创造了一个又一个神奇而强大的工具,让他们自己、你、我和其他所有
人都因此而受益非浅。这其中就包括gcc、binutils和glibc,还有后面我将介绍给你的所有用
于这个toolchain的工具。
我首先要介绍给你的一个非常重要的工具----Make,它对于toolchain无比重要,毕竟你不
想一个文件一个文件的编译各种软件包。无论是在何种环境下make都是一个非常重要的软件创
建工具。不管是自己进行项目开发还是安装应用软件,你都经常要用到make或make install。
利用make工具,你可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百
个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复
杂的相互关系。而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来
说简直就是一场灾难。而make工具则可以根据Makefile描述的规则自动完成编译工作,并且可
以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用make和makefile工具可
以大大提高项目开发的效率。同时掌握make和makefile之后,你也不会再面对着Linux下的应用
软件手足无措了。至于它们的详细内容已经超过本文的范围,感兴趣的读者可以参考有关专业
文档。
另外一个与创建软件包有关的工具是configure脚本。Linux上的软件大多是可以运行在其
他类Unix系统上的,而且它们之间或多或少的存在一些差异,这些差异对程序员是不透明的,
比如有的平台编译器文件名为cc,而有的可能是gcc,还有可能是其他别的什么。而且,即便
是在Linux上,不同的发行版本也可能存在一些差异,比如有些支持framebuffer,而有些可能
不支持。应用软件的作者为了使他们的软件尽可能的在大多数系统下都可以很好的工作,他们
要针对不同的环境作不同的处理,这些对于作者是没有问题的,可是将这些代码交付给用户,
让用户决定该如何取舍却是不可行的事情。因此,configure这个脚本工具就诞生了,它来检
测各系统间的差异性,然后修改相应的Makefile文件,决定做如何取舍。configure脚本是由
软件作者自己维护的,它清楚的知道在什么样的环境下,采用什么样的编译器、编译选项等内
容,而用户只需要执行一下这个脚本,就一切OK了。configure是完全使用shell脚本编写的,
几乎可以在所有类Unix系统中运行,这就保证了它的通用性。随着configure的发展,逐渐的扩
展了原有的功能,开始承担软件包的配置工作了,可以决定软件包的那些组件被创建,那些可
以不用创建等。而且人们还发明了用于产生configure的工具,使得原来很复杂的工作变得越
来越简单了。
前面介绍了Make工具和confiugre脚本。这两个重要的工具是你在创建Linux所有软件包必
不可少的工具,你永远也离不开它们,就像你离不开gcc、binutils和glibc一样,否则你就无
法进行MagicLinux的开发。使用Make需要configure改写Makefile文件,使用configure需要
shell来执行,shell来执行configure还需要更多的软件包来支持,它们是Ncurses、Bash、
Bzip2、Coreutils、Diffutils、Findutils、Gawk、Gettext、Grep、Gzip、Make、Patch、
Perl、Sed、Tar、Util-linux。除了Ncurses之外,其他的都是按照文件名排列的,因为它们
之间不存在太多必要的依赖关系。
Ncurses是最早的System V Release 4.0 (SVr4)中 CURSES的一个克隆。这是一个可自由
配置的库,完全兼容旧版本的curses。简而言之,它是一个管理应用程序在字符终端显示的函
数库。ncurses不仅仅封装了底层终端功能,而且提供了一个相当稳固的工作框架
(Framework),可以在字符模式下产生美观的界面。它提供了一些创建窗口的函数。而它的姊
妹库 Menu、Panel和Form则对curses基础库及进行了扩展。这些扩展库通常都随同curses一起
发行。可以建立一个同时包含多个窗口(multiple windows)、菜单(menus)、面板
(panels)和表单(forms)的应用程序。窗口可以被独立管理,例如让它滚动或者隐藏。
Linux系统下被广泛应用的shell----Bash就要依赖它完成字符界面的显示功能。
Bash是Bourne-Again Shell的缩写,这个 Shell 是 Bourne Shell 的增强版本,也是基
准于 GNU 的架构下发展出来的。第一个流行的 shell 是由 Steven Bourne 发展出来的,为了
纪念他所以就称为 Bourne shell,或直接简称为 sh。而后来另一个广为流传的 shell 是由柏
克莱大学的 Bill Joy设计依附于BSD版的Unix系统中的shell,这个shell的语法有点类似C语言,
所以才得名为C shell,简称为csh。由于在学术界 Sun 主机势力相当的庞大,而 Sun 主要是
BSD的分支之一,所以C shell也是另一个很重要而且流传很广的 shell 之一。Bash是GNU计划
中重要的工具软体之一,目前也是 GNU操作系统中标准的shell,他主要相容于sh。所以,可
想而知的,目前几乎所有的Linux发布都是使用bash作为管理核心的主要shell。如果没有
shell,configure脚本是无法执行的,也就无法创建各种软件包了。
Bzip2是块排序文件压缩器,使用Burrows-Wheeler块排列文本压缩算法和霍夫曼编码来
压缩文件。压缩比要大于gzip工具使用的基于LZ77/LZ78的压缩算法(如gzip格式),接近PPM统
计压缩算法族的压缩比。Linux的大部分软件的源代码包的文件名有.bz2,这就表明它使用了
bzip2进行的打包压缩,需要bzip2来解压缩。
Coreutils如其名称一样,是一个核心工具包。包含如:cp、ls、mkdir、cat等常用的
shell命令。为了使你的toolchain在各种环境下都可以独立工作,你需要它拥有这些常用命令。
Diffutils这个软件包里的程序向你显示两个文件或目录的差异,常用来生成软件的补丁。
它包括cmp、diff、diff3和sdiff,这些工具在很多时候很有用处,你的toolchain应该拥有它们。
Findutils软件包包含查找文件的工具,既能即时查找(递归的搜索目录,并可以显示、
创建和维护文件),也能在数据库里查找(通常比递归查找快但是在数据库没有及时更新的情况
下,结果并不可靠)。configure脚本在做一些系统检测时会用到,所以你的toolchain一定要
包含这个软件包。
Gawk是GNU所做的awk,Gawk 最初在1986年完成,之后不断地被改进、更新。Gawk包含
awk的所有功能。awk是一个程序设计语言,有很强的数据处理能力。对于文本文件里的内容做
修改、比对、抽取等处理时,awk能够以很短的程序轻易完成。如果使用C或Pascal等语言写程
序来完成上述的动作,不但不方便还需要花费大量时间,所写的程序也会很大。awk能够依照
使用者定义的格式来分解输入的数据,也可依照使用者定义的格式来输出数据。至于awk的语
法等内容的说明已经超出本文的范围,请读者阅读专业著作。
Gettext是实现程序国际化和本地化的一种工具。最初,在美国,程序通常都采用英语
编写,界面和文档都用英文表述。这是很自然的事情,因为他们日常使用的就是英语。如果
这些程序只被习惯使用英语的人使用,这本身也是很正常的事,不需要有什么改变。然而,
慢慢地,事情有了一些变化,许多的程序,不仅需要被习惯说英语的人使用,也需要被其他语
种的人使用,比如中文,日文,德文等。这些不使用英语作为母语的人,他们更希望程序的界
面或文档,能够以他们自己更加熟悉的语言表示。那么,有没有一种方法呢?这种方法可以让
程序支持多种语言,而且可以很方便地切换语言环境,由一种语言转换到另一种语言。正是基
于这种目的,翻译项目试图研究出可行的工作方案,解决这个问题,让人们有机会开发出真正
的多语言程序。gettext是翻译项目的重要一步,它提供了一个工作框架,由一些集成的工具
和文档组成,帮助程序员、翻译人员和最终用户实现程序的国际化和本地化。需要说明的是,
gettext只是实现此目标的一种方法,还有其他方法的存在。如果你的toolchain要支持多语言,
gettext是非常明智的选择。
Grep(global search regular expression(RE) and print out the line,全面搜索正
则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把
匹配的行打印出来。Unix的grep家族包括grep、egrep和fgrep。egrep和fgrep的命令只跟grep
有很小不同。egrep是grep的扩展,支持更多的re元字符, fgrep就是fixed grep或fast grep,
它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,
不再特殊。linux使用GNU版本的grep。它功能更强,可以通过-G、-E、-F命令行选项来使用
egrep和fgrep的功能。Shell脚本在缺少grep支持的时候,几乎寸步难行。
Gzip是GNU zip的缩写,它是一个 GNU 自由软件的文件压缩程序。gzip 的基础是
DEFLATE,DEFLATE 是 LZ77 与哈夫曼编码的一个组合体。文件名以.gz结尾的源代码包就是采
用Gzip压缩和解压的。
Make就不用多说了,前面已经有很详细的介绍了,没有它你将寸步难行。
Patch是前面介绍的Diffutils软件包中diff程序的一个接口。diff有很多选项,但是该
命令最常用的用途是用来生成一个文件,该文件中列出了内容发生改变的行,显示两个原始文
件修改过的行以及由于内容没有变化而忽略掉的行。patch典型地用于把一个目录下的源代码
文件更新到新的版本,从而就避免了下载整个新的源代码档案的必要。下载一个有效的patch
仅仅需要下载发生变化的那些代码行就可以了,这往往是由diff产生的。patch最初源自十几
年前,那时网络带宽的限制促进了patch的发展,然而和当时的很多Unix工具一样,直到现在,
patch还在广泛应用。我想你的toolchain没有理由不包含这个工具吧。
Perl当初只是 Unix 系统管理员的一个工具,被用在无数的小任务中。从那以后,它逐
步发展成为一种全功能的程序设计语言,特别是在各种计算平台上,它被用作 Web 编程、数
据库处理、XML处理以及系统管理 ---- 它能够完成所有这些工作,同时仍然是处理小的日常
工作的完美工具,这是它的设计初衷。Perl 快速、有趣,而且特别有用。很多人因为需要
Perl而使用它,又因为热爱它而继续使用它。这样的东西为什么不放在你的toolchain里面呢?
有关Perl的语法等内容超过了本文的范围,请读者参考其他专业著作。
Sed (Stream EDitor)是类UNIX系统上提供将编辑工作自动化的编辑器,使用者无需直接
编辑数据。使用者可利用sed所提供的20多种不同的参数,组合它们完成不同的编辑动作。此
外,由于sed都以行为单位编辑数据,它也是一种行编辑器。一般sed最常用在编辑那些需要不
断重复某些编辑动作的文件上,列入将文件中的某个字符串替换成另外一个字符串等。这些
相对于其他的编辑器(如:vi、emacs等)用手动的方式修改文件,sed用起来较为省力。前
面的内容,有关修改文件内容的操作,我使用的都是sed。
Tar可以为文件和目录创建档案。利用tar,用户可以为某一特定文件创建档案
(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件。tar最初被用来在磁
带上创建档案,现在,用户可以在任何设备上创建档案,如磁盘。利用tar命令,可以把一大
堆的文件和目录全部打包成一个文件,这对于备份文件或将几个文件组合成为一个文件以便于
网络传输是非常有用的。利用tar命令,可以把一大堆的文件和目录全部打包成一个文件,这
对于备份文件或将几个文件组合成为一个文件以便于网络传输是非常有用的。Tar还可以与
bzip2和gzip相结合,创建压缩包,Linux上的大部分软件源代码是用Tar也bzip2或gzip相结合
产生的压缩包形式发布的。Tar产生的文件包文件名以.tar结尾,与bzip2结合产生的包以
.tar.bz2结尾,与gzip结合产生的文件包以.tar.gz结尾。
Util-linux软件包包含许多工具。其中比较重要的是加载、卸载、格式化、分区和管理
硬盘驱动器。你的toolchain需要磁盘挂接和卸载工具,因为在创建基本Linux系统时需要挂接
proc等文件系统。
好了,你需要的大部分软件都介绍完了,最新版本可以在网络上获得,我就不一一列举了。
如果你觉得你的toolchain还需要更多更强的功能,没问题的,下载相应的软件包,创建安装
就行了。当你的toolchain被众多好用强大的软件工具充实之后,可以做的事情不仅仅是为了
创建基本的MagicLinux系统,至于你能将它应用在何种领域或目的,就由读者你细心体会去吧。
2.12 结束语
感谢你能够为了MagicLinux而来阅读这么生涩的文章。本章前部分详细讲述了如何创建一个
gcc环境,因为这非常重要,我不希望你在这个环节有任何失误。
Toolchain技术不仅仅在制作Linux发布时起到很大作用。而且在诸如嵌入式开发等那些需要
交叉环境下应用非常广泛。本章所讨论的内容也不仅限于用于MagicLinux开发的toolchain的制作,
同样适用于其他方面应用的toolchain的制作,不过是给定的一些编译选项有些变化。如果读者
对这方面感兴趣的话,可以据此而举一反三,独立思考一下,并通过互联网寻找你要了解的问
题的答案。如果你想很快的拥有一个toolchain,而完全自己做感觉吃力的话,你可以参考LFS,
它的官方网站是http://www.linuxfromscratch.org/,那里介绍的内容比文本要全面,而且更为
专业,本文的大部分内容也是参考它的。
下一章我将让你了解Linux的结构,让你对Linux有一个彻底的了解。
Magic Linux开发入门指南(三) 一旦你拥有了一个toolchain,就拥有了制作MagicLinux的初级武器了。这个时候你可能已经开始辂
胳膊挽袖子,准备大干一场了,可你却突然发现,还是什么也做不了,原因就在于你还不了解如何使
用toolchain来制作MagicLinux。下一章我将讲述Linux的组织结构,让你对Linux有一个彻底的了解,为正
式开始制作MagicLinux作准备。
第二章 Linux的文件组织结构
像Linux这些类Unix操作系统与你所熟知的Windows有很大的不同,其中一个显著的不同就是类Unix
操作系统的文件管理方式,它只有一个文件树,以一个树根“/”为起点,所有的文件和外部设备都以
文件的形式挂接在这个文件树上,包括硬盘、软驱、光驱、调制解调器等,而且有着非常严格的组织
结构。不同的Unix分支在文件组织细节方面会有一些不同,但基本层次与本文下面将介绍的内容是相
一致的。
在Linux操作系统下,众多软件都被安装在既定的位置,它们的文件名、文件内容、目录结构等都
是固定的,相互合作的软件系统通过这些既定的规则就可以恰当的找到它们的合作伙伴,互相配合完
成一些更为复杂的任务。作为用户也可以在通过查看既定的位置的内容来判断是否安装了自己需要的
软件,或者修改软件的配置等,且同等的操作在任何同类系统中都能得到相同的结果。为了实现这些
目的,Linux必须严格的组织文件层次关系,且形成一种规范行为,才不至于因为混乱而使得用户以及
软件系统对操作系统产生恐慌。这充分体现了Unix的传统----简单。
Magic Linux作为一个成熟的Linux发布,必定要因袭传统标准,作为Magic Linux的开发者,熟识
Linux的文件组织结构也很自然的成为你必修的基本功课,因为这决定了你制作出来的系统能否被其他
人所接受,以及能否正确运行那些成熟稳定且应用广泛的软件产品。
2.1 基本组织原则
类Unix系统的文件组织结构是按照一个基本组织原则而设计的。当你能够深入理解这些原则之后,
你会发现目前这种组织结构是必然的选择,你也不必牢记那些结构性的问题,而不由自主的做出与现
今沿袭下来的基本一致的布局来。
按照各文件在系统中所起的作用,可以划分为是否共享与是否可变两类属性。一般来说,不一定
需要本地系统提供的文件都可以看作为共享的,而那些必须由本地系统提供的文件则可以看作为非共
享的;能够被保存在只读存储设备上的文件都可以被认为是不可变的,反之那些必须要存在可读写存
储设备上的文件就是可变的。
从是否可共享这一属性上可以充分体现类Unix操作系统极强的灵活性与可扩展性。由于采用单一
文件树形式,任何设备都能够以文件方式挂接到树的某一枝上,这使得非本地存储设备通过某种网络
协议挂接到本地文件树上成为可能。这样,很有可能你正在使用的某些程序根本就不在你的计算机里,
而是在离你非常遥远的大洋彼岸。给文件赋予是否可共享这一属性,使得这种操作变得轻松且合理,
只要按照这个原则来组织文件布局,就可以很容易的提供这种让你难以置信的强大功能。其实这也是
历史原因造成的,毕竟在很早以前,大量的本地存储是非常奢侈的事情,让远端的一个设备来存储大
家共用的一些程序是很棒的解决方案。那些程序自然的就是为大家所共享的。然而这种共享不能解决
所有问题,毕竟本机的设备不能完全与别人的设备相同,那么它们对应的文件也不能提供给别人使用,
自然的也就是非共享文件了。
可变属性在某种意义上为类Unix系统提供了很大的伸缩性,可以容易的将它们一直到很多非通用
设备上,比如电视机顶盒。它允许你合理的将那些固定不变的二进制代码存储在只读设备上,而那些
随着系统运行而时刻变化的内容,如系统日志等可以存储在随机读写设备上,这样便于你最大限度的
节省硬件设备上的成本开销。于此,系统的安全性也得到了保障,毕竟很少有人希望他们那些常用的
程序可以很容易的被一些恶意程序所修改,这点Windows上的用户们是感同身受的。
以前,类Unix系统的文件组织结构不是非常清晰,/usr和/etc目录混合着大量的可变与不可变的文
件。由此,引入了/var目录,将/usr目录下的可变文件全部转移至/var目录下,这就保证了/usr目录可以
单独放在其他设备上且以只读方式被挂接在树根上。目前/etc目录下的可变文件还没有完全转移至/var
目录下,但是从技术角度看,这也只是一个时间问题。
下表列出了依照这一基本原则组织文件的典型例子:
可共享
不可共享
不可变
/usr /etc
/opt /boot
可变
/var/mail /var/run
/var/spool/news /var/lock
表2-1
2.2 根文件系统
根文件系统保存在根分区,它担负着系统启动、修复、更新、恢复等重要任务。当你启动系统成
功后,可以执行下列命令查看树根的内容:
#cd /
#ls
结果可能是下面的内容:
bin dev lib media opt root sys tmp usr
boot etc home mnt proc sbin srv var
虽然他们井然有序的排列在一起,但是他们并不一定完全存放在同一个设备上,比如/usr、/var等,而
且/sys和/proc根本不在任何外部存储设备上,而是在内存中。
这里,将直接挂接在树根“/”上的设备叫做根设备,可能是内存,也可能是磁盘,如果是磁盘的
某一分区,就称之为根分区,它是操作系统启动时默认挂接的设备,是所有文件系统的根源。可谓有
了根文件系统就有了一切。如果当系统崩溃到连根文件系统都无法挂接时,你只有唯一的选择----重
装。
为了启动系统,根分区的内容必须能够支持到系统挂接了其他设备和其它磁盘分区之后。这就要
求它必须包含必要的工具(如init、mount等),配置信息(如inittab、fstab等)以及其他重要的启动数
据。还应该包含引导器的配置信息,但与具体引导器的不同而不同,有的需要有的可以放在其他地方。
为了更新或修复系统,根文件系统还包含那些能够提供这些功能的工具。保证有经验的系统管理
员能够重建被毁坏的部分。
为了恢复系统,根文件系统要提供备份和还原工具,在必要的时候可以通过以前的备份恢复系统
的运行。这些备份往往保存在光盘、磁带等大容量外存上面。
根文件系统上的内容需要平衡考虑,要仔细斟酌将哪些内容放入根分区,尽量保证根分区的精巧。
有很多因素要求跟分区要尽量的精巧:第一,根文件系统包含了众多的系统特有的配置文件,如:内
核、hostname文件等,这些都是系统特有的,这就意味着根文件系统不能通过网络系统共享。保证网
络系统根分区在服务器的最小化,可以最小化非共享文件区域 的磁盘空间浪费。这也使得工作站可
以使用更小容量的硬盘。第二,虽然你划分了较大的根分区给根文件系统,且对它付诸了你的心血,
可是有人却使用了更小的根分区,导致你的发布无法安装。当系统有了更多的安装文件,你可能会发
现这与其它使用更小根分区的系统不兼容了。作为开发者,你就把这个问题转移给你的广大用户了。
第三,由于磁盘错误毁坏了根分区数据是致命的,更小的根分区可以尽量减小这种致命问题的发生。
应用程序不得创建或依赖根文件系统上的特定文件或子目录,后面将要讲述的其他位置可以为任何软
件包提供更多的灵活性。这有很多原因,最主要的,从性能或安全等多方面考量,系统管理员需要保
证根文件系统精小,而且也规避了因交叉挂接而引发的各种混乱问题。
根文件系统必须包含下列目录,某些可以是符号连接:
名称
功能
bin
重要的可执行文件,shell命令
boot 引导器配置文件,内核等。
dev 设备文件
etc 系统专有配置文件
lib 重要的共享库和内核模块
media 可移动设备挂接点
mnt 其他文件系统的临时挂接点
opt 附加应用软件包
sbin 重要的系统可执行文件
srv 本系统提供的用于服务器的数据
tmp 临时文件
usr 文件系统的第二层
var 可变数据
表2-2-1
这些内容可能与你理解的不太一样,但这就是全部必须的内容,各目录的详细内容下面会逐一介绍。
而下表所列的可能就是你疑问的了,但这些是可选的,只要在有相应子系统的时候才需要。不过对于
大多数通用系统它们都是需要的。
名称
功能
home
用户自己的目录
lib
其他各式的重要共享库,比较少见
root
root用户的专有目录
表2-2-2
它们的具体内容也会在下面内容进行详细介绍。
2.3 子目录
本节将讲述根文件系统下各子目的详细内容,以及为了使系统能够运行它们所必须包含的内容。
/usr和/var由于本身的复杂性,我会以单独的章节介绍它们。
2.3.1 /bin
/bin目录下包含了用户命令文件,也就是shell命令,供所有用户使用。不仅仅是用户,脚本程序也
要使用它们。即使其他文件系统还没有被挂接时,它们也要能够工作。
/bin目录不得包含任何子目录,下表列出了它必有的内容,有些文件可能是一个符号连接:
名称 功能
cat 连接多个文件,并将内容输出至标准输出
chgrp 改变文件的所属组
chmod 改变文件的访问权限
chown 改变文件的所属用户和组
cp 复制文件和目录
date 打印或设置系统日期与时间
dd 转换并复制文件
df 查看文件系统磁盘空间使用情况
dmesg 打印或控制内核的消息缓冲
echo 显示一行文本
false 不做任何事情,表示不成功
hostname 显示或设置系统的主机名
kill 向进程发送信号
ln 创建文件连接
login 开始系统上的一个会话
ls 列出目录内容
mkdir 创建目录
mknod 创建块或字符设备的专用文件
more 分页显示文本
mount 挂接一个文件系统
mv 移动文件或更改文件名
ps 查看系统进程状态
pwd 打印当前工作目录名
rm 删除文件或目录
rmdir 删除空目录
sed 一个流式文本编辑器
sh Bourne命令shell
stty 改变或打印终端行设置
su 改变用户标识
sync 刷新文件系统缓存
true 什么也不做,表示成功
umount 卸载文件系统
uname 打印系统信息
表2-3-1
如果/bin/sh不是真正的Bourne shell,它必须连接到一个真正的shell命令上,既可以是硬连接也可以是软
连接。例如,调用sh或bash时,bash会有不同的行为。使用符号连接也使得用户容易地了解到/bin/sh不
是一个真正的Bourne shell。“[”和“test”命令必须被放在一起,既可以在/bin目录下也可以在/usr/bin
目录下。这是为了保持与POSIX.2标准一致,即使它们已经由shell程序内部实现了。
下表的内容是可选的。如果有相应的子系统被安装,则是必须的。目前大多数发布都会部分的提
供它们。
名称 功能
csh 著名的C shell
ed “ed”文本编辑器
tar tar打包工具
cpio cpio打包工具
gzip GNU压缩工具
gunzip GNU解压缩工具
zcat GNU解压缩工具
netstat 网络统计工具
ping ICMP网络测试工具
表2-3-2
如果存在gunzip和zcat程序,它们一定是gzip的软连接或硬连接。/bin/csh可能是/bin/tcsh或/usr/bin/tcsh的符
号连接。tar、gzip和cpio为系统提供了恢复能力,反之,如果不考虑从根分区恢复系统的话,这些程序
可被忽略(例如一个只读片内根系统,通过NFS挂接/usr目录)。若要通过网络恢复系统,还需要ftp或
tftp存在于根分区。
2.3.2 /boot
该目录包含了除在启动时刻和映像安装器不需要的配置文件外,启动进程所需要的所有内容。换
句话说,/boot目录下存储的数据用于内核开始执行用户级程序之前的操作。这里也可能包含备份的主
引导扇区信息和扇区映像文件。另外,操作系统的内核必须放在“/”或“/boot”目录下。
2.3.3 /dev
/dev目录是设备文件或专有文件的存放位置。要是/dev中的设备需要手工创建,/dev必须含有一个
名为MAKEDEV的命令,它能够创建需要的设备文件。同时也应该为任何本地设备包含一个
MAKEDEV.local文件。如果需要,MAKEDEV必须具备为任何发现与系统的设备创建文件的权限,而
不限于那些特定的安装实现。
2.3.4 /etc
/etc包含了系统特有的配置文件。所谓配置文件,就是用于控制程序运行的本地文件,它一定是
不可变文件,而且是可编辑的,那些二进制可执行文件是不能作为配置文件的。/etc下基本没有二进
制文件,下表中列出的目录需要包含在/etc下,有些可能是符号连接。
名称 功能
opt /opt目录下各软件的配置文件
X11 X Window系统的配置文件,可选。
sgml SGML的配置文件,可选。
xml XML的配置文件,可选。
表2-3-3
当相关子系统安装后,下列文件必须放在/etc中,有些也可以是符号连接:
名称 功能
csh.login C shell登陆的全局初始化文件,有C shell时有效。
exports NFS文件系统的访问控制列表,有NFS支持时有效。
fstab 文件系统的静态信息,系统初始化时需要挂接多个文件系统时需要。
ftpusers FTP守护进程的用户访问控制列表。
geteways routed的网关列表。
gettydefs getty程序的配置文件。
group 用户组列表文件。
host.conf 决定如何解析域名。
hosts 主机名静态信息。
hosts.allow 允许访问本机的IP地址列表。
hosts.deny 禁止访问本机的IP地址列表。冲突时,优先。
hosts.equiv 信任主机列表,作用于rlogin,rsh,rcp。
hosts.lpd 信任主机列表,作用于lpd。
inetd.conf inetd守护进程的配置文件。
inittab init程序的配置文件。
issue 登陆提示符上面的消息和标识文件。
ld.so.conf 搜索共享库的目录列表文件。
motd 本机的标识信息,远程登陆时确认你当前控制的是本机还是远程计算机。
mtab 文件系统的动态信息,记录当前挂载的文件系统。
mtools.conf mtools程序的配置文件。
networks 网络名称的静态信息,与hosts类似。
passwd 用户密码文件。
printcap ldp打印机的性能数据库。
profile sh shell登陆的全局初始化文件。
protocols 网络协议定义文件。
resolv.conf DNS域名解析的配置文件。
rpc RPC协议定义文件。
securetty Root登陆的TTY访问控制文件。
services 记录网络服务名和它们对应使用的端口号及协议。
shells 可使用的登陆shell路径名。
syslog.conf syslogd守护进程的配置文件。
表2-3-4
这里,显然mtab就不适合/etc内单纯为不可变文件的原则,这在今后会逐步得到解决。
(未完...待续...)
Magic Linux开发入门指南(四) /etc/opt目录下的内容是为/opt而准备的。附加应用软件包的配置文件必须安装在
/etc/opt/
/etc/X11目录下的内容是X Windows系统的配置文件,X Windows系统的所有主机
相关配置文件全都在这里。这使得在/usr以只读方式挂接时,可以局部控制
X Windows系统。只要安装了X Windows系统,下表中的文件必须安装在/etc/X11目录
下:
名称 功能
Xconfig XFree86早期版本的配置文件。
XF86Config XFree86第3、4版的配置文件。
Xmodmap 定义xdm和xinit所要使用的键盘映射。
xorg.conf Xorg的配置文件。
表2-3-5
/etc/X11下的那些子目录是为xdm以及别的程序(如窗口管理器)准备的。那些只有
一个配置文件的窗口管理使用的配置文件名是system.*wmrc(除非有约定俗成的名
字),一般不占用一个子目录。任何窗口管理器的子目录一定与它实际的可执行文
件名相一致。
/etc/sgml和/etc/xml里的内容是SGML和XML的配置文件,这是可选的。定义它们
高级参数的基本配置文件就安装在这里。
2.3.5 /home
/home为用户专有目录,每个用户都会在/home目录下有其对应的子目录,而且这
个子目录一般与他的用户名相同。没有任何程序会依赖于这个目录。
同一个程序,不用的用户有不同的配置方式,这些用户独有的配置文件就保存
在他的home目录中,一般以“.”开头(即隐藏文件)。如果某个程序需要创建多个
配置文件,它就会在用户的home目录下创建一个以“.”开头的目录(即隐藏目
录),将这些配置文件放入目录中。这种情况下,配置文件不再需要以“.”开头
了。
2.3.6 /lib
/lib目录里存放着必备的共享库和内核模块。在系统启动和运行根文件系统上的
各种命令时,都要用到这些共享库。必须至少存在一组下列文件名组合方式的一组
文件(可以是文件,也可以是符号连接):
文件名组合 功能
libc.so.* C动态链接库。
ld* 运行时连接器/加载器。
表2-3-6
如果安装了某个C预处理器,由于历史原因,/lib/cpp必须是它的连接。modules子目录
必须存在于/lib目录下,它是内核可装载模块的存放位置。
2.3.7 /lib
有些时候,系统为了支持不只一种可执行文件格式,会需要有多个不同的/lib目
录来单独保存必须的共享库。一般的,在处理这个问题时会在lib后面追加一些修
饰,如AMD64系统的/lib64等。这种情况下就不再需要/lib
象PPC64、s390x、sparc65和AMD64这些即支持64位程序也支持32位程序的平台
下,/lib
中,而32位库则依然放置在/lib中,这保证了对原有32位程序的平滑兼容。完全64位
架构的IA64不支持32位程序,则只有/lib,也就是说所有的64位库都要放置在/lib中。
2.3.7 /media
该目录用于挂接如软盘、光盘、U盘等可移动设备,其各子目录挂接各种类型
设备,从其目录名可了解具体设备类型。曾经出现过很多其他名称的挂接点,如
/cdrom,/mnt或/mnt/cdrom等,现在统一规范到/media中。虽然很多人习惯于将这些
可移动设备挂接在/mnt子目录中,但这与传统不符,因为/mnt实际上只是临时挂接
点。下表的内容一定要出现在/media目录中,具体内容与系统实际情况有关。
名称 功能
floppy 软盘驱动器
cdrom CD-ROM
cdrecorder 光盘刻录机
zip Zip磁盘驱动器,现在比较少见
表2-3-7
如果系统中有不止一个同类的驱动器,则在其挂接类型目录名后面带有一个0起始的
数字,比如“cdrom0、cdrom1、…..”。
2.3.8 /mnt
提供这个目录是让系统管理员临时挂接某个文件系统的。此目录的内容属于局
部问题,不应该影响任何程序的运行。千万不要在这里安装什么程序,毕竟一个临
时目录在系统不需要它的时候会被其他东西占用。
2.3.9 /opt
这里是系统附加软件包的栖息地。每个软件包会在/opt下拥有一个自己的目录,
那里面存放着他所有的可执行程序和静态数据。这个专有目录名应该与它本身的名
字相一致,这样用户就很容易管理它们了。也可以使用厂商名称,不过这个厂商名
称必须是在Linux名称与号码分配管理局注册过的,不过厂商名下面还应该是软件
名,毕竟同一个厂商可以有多个软件。
/opt/bin、/opt/doc、/opt/include、/opt/info、/opt/lib和/opt/man是保留给本地系统管理
员使用的。有些软件包可能会提供一个前端文件(连接或副本),系统管理员可以
将这个文件放在这些保留目录中,但是在缺少这些保留目录时也要保证可以正常工
作。
用户调用的程序必须放在软件包目录下的bin子目录中。如果包含Unix指南页,
则要将它们放在对应软件包的share/man子目录下,而且也必须拥有与/usr/share/man目
录中相同的组织结构。
软件包有可变文件,必须将这些可变文件安装在/var/opt目录下。见/var/opt一
节,会有更详细的介绍。
本机专有的配置文件必须安装在/etc/opt目录下,见/etc一节的介绍。
没有哪些附加软件包会将它们的文件放在/opt、/var/opt和/etc/opt之外,除非那个
软件包的某些文件必须放在特定位置,否则不能正常工作。比如,设备锁文件必须
放在/var/lock中,设备文件必须放在/dev中。
一些Linux发行版可能在/opt目录下安装了软件,在没有征得本地管理员同意时,
千万不要擅自更改或删除这里的软件。
为附加软件提供/opt目录,是类Unix世界非常优良的传统习惯。发布版对/opt目
录的使用应该做些少许的限制,因为发布的软件与本地安装软件可能会发生冲突,
尤其那些使用固定路径的软件。
2.3.10 /root
是否为root帐户提供专有home目录取决于开发者或者用户的使用习惯。不过还是
建议提供这个/root目录。
2.3.11 /sbin
必备的系统软件存放处。用于系统维护的软件和某些只限root用户使用的命令存
储在/sbin、/usr/sbin以及/usr/local/sbin目录下。/sbin目录存储的则是那些在系统启动、
恢复、还原以及修复过程中必备系统工具,也是对/bin下内容的补充。那些已知
在/usr被挂接之后才需要的程序一般可以放在/usr/sbin目录下。用户自行安装的系统管
理程序应该放在/usr/local/sbin目录下。下表所列的各种命令应该出现在/sbin目录下。
名称 功能
shutdown 关机命令
fastboot 快速从新启动系统,不做磁盘检测。可选。
fasthalt 快速停止系统运行,不做磁盘检测。可选。
fdisk 磁盘分区表维护工具。
fsck 文件系统检测与修复工具。
fsck.* 特定文件系统检测与修复工具。
getty getty程序,设置终端机模式、连线速率和管制线路。
halt 停止系统运行。
ifconfig 网口配置工具。
init 系统初始化进程。内核调用的第一个用户进程。
mkfs 创建文件系统,也叫格式化。
mkfs.* 创建特有的文件系统。
mkswap 格式化交换分区。
reboot 重新启动系统。
route IP路由表维护工具。
swapon 开启页交换。
swapoff 关闭页交换。
update 周期性的刷新文件系统缓冲的守护进程。
表2-3-8
2.3.12 /srv
某些服务进程启动之后,它们需要读取的数据会放在/srv目录中。提供/srv目录
一方面为了方便用户找到特定服务的数据文件,另一方面也为某些服务放置它们的
执行脚本(如CGI脚本)、只读或可写数据提供一个合适的地方。提供给特定用户
的数据应该放在那个用户的home目录下。
目前还没有规范的/srv下子目录的命名方法。有一种方式是按照协议构造/srv下
的数据,如ftp、rsync、www、cvs等等。对于大型系统,可能利用功能管辖范围构
造/srv下的内容,如/srv/physics/www、/srv/compsci/cvs等。它的具体内容在不同主机间
会有很大的差异,因此,没有程序会依赖于/srv下特定的子目录结构,也没有那个程
序必须在/srv下保存数据。不过,/srv目录始终会作为服务进程数据存放地而一直存
在着。
2.3.13 /tmp
/tmp必须能够被任何程序、任何用户访问,它存放临时文件。任何程序都不用
对/tmp目下任何文件或目录负责,尽管如此,还是建议系统重起后,应该清除/tmp目
录下的内容。
2.4 /usr目录组织结构
/usr是整个文件系统的第二个主要区段。它既是可共享的也是只读的,这意味
着/usr可以共享于多种主机之间,但不能写入。任何主机专有的或随时间变化的信息
只能存放在其他地方。
即使是大型软件包,也不得占用/usr下的一个独立子目录。下表所列的目录或目
录的符号连接要求出现在/usr下:
名称 功能
bin 大部分用户命令。
include C/C++程序头文件目录。
lib 程序库。
local 用户自行安装的程序存放于此。
sbin 非重要的系统程序。
share 体系结构独立数据。
表2-4-1
下表所列目录是可选的,根据具体软件安装情况而定:
名称 功能
X11R6 X Window 11 第六版
games 育教娱乐程序
lib
src 源代码。
sbin 非重要的系统程序。
表2-4-2
这里有一个例外,就是X Windows系统,它占有了一个独立子目录,这是历史原因导
致的。
为了与老系统保持兼容,可以在/usr目录下创建下列符号连接:
/usr/spool --〉/var/spool
/usr/tmp --〉/var/tmp
/usr/spool/locaks --〉/var/lock
表2-4-3
这是因为这些目录早先是在/usr下的,后来转移到了/var目录下。一旦系统不再需要
那些符号连接了,可以直接删除。
2.4.1 /usr/X11R6
该目录保留给X Window 11第六版和与其有关的文件的。为了使XFree86与其它
系统的X Window相兼容,只要有/usr/X11R6目录就一定要有下表中的符号连接:
/usr/bin/X11 --〉/usr/X11R6/bin
/usr/lib/X11 --〉/usr/X11R6/lib/X11
/usr/include/X11 --〉/usr/X11R6/include/X11
表2-4-4
通常,不得利用这些符号连接安装或管理软件,它们仅供用户使用。另外,在
X Window发布版本更替期,没什么好方法可以了解当前使用的是X11的那个版本。
保存在/usr/X11R6/lib/X11的主机专属数据应解释为示范文件。应用程序需要了解
本机信息时,必须参考/etc/X11下的某个配置文件,虽然这个配置文件可能是
/usr/X11R6/lib下某个文件的连接。
2.4.2 /usr/bin
系统中绝大多数的可执行文件都放置在这里。下表所列的目录必须放在/usr/bin
下,除非没有安装与它们相关的系统:
名称 功能
mh MH邮件处理系统的命名所在目录。
/usr/bin/X11 如果存在/usr/X11R6/bin目录,一定是它的符号连接。
表2-4-5
下表所列的文件或符号连接也必须放在/usr/bin目录下,除非你没有安装这些软件:
名称 功能
perl 经典的perl语言解释器。
python 最近非常流行的python语言解释器
tclsh 古老而特别的tcl解释器。
wish 图形界面的tcl解释器。
expect Tcl语言非常经典的扩展部分,实现程序的自动交互。
表2-4-6
读者会发现一个特别的地方,我所列出来的这些程序都是某种脚本语言的解释器或
扩展部分。这是因为shell脚本解释器(shell脚本的第一行往往带有一个!#
的注释)不能依赖于某个具体目录,这就要求必须标准化它们的位置。Bourn shell和
C shell解释器已经被锁定在/bin目录中了,可是Perl、Python和Tcl却经常被放置在不同
的地方,于是/usr/bin下的内容往往是到其实际位置的符号连接。
2.4.3 /usr/include
标准头文件存放目录,系统内所有通用的C语言头文件都要放置在这里。如果
有/usr/X11R6/include/X11存在,则一定要建立/usr/include/X11这个符号连接。
2.4.4 /usr/lib
/usr/lib内的内容是库和目标文件,它们都是二进制文件,但不被用户和shell脚本
直接执行。应用程序可以在这里创建独立的子目录。如果应用程序在这里创建了独
立的子目录,它所有的与体系结构有关的执行数据都必须放在这个子目录中。
由于历史原因,如果有/usr/sbin/sendmail存在,则必须在/usr/lib下创建sendmail符号
连接;如果有/lib/X11存在,也必须在/usr/lib下创建X11的符号连接,即使/lib/X11也是
一个符号连接也要这样做。
2.4.5 /usr/local
/usr/local是供系统管理员安装局域性软件使用的,这保证了当系统更新时不会把
它们覆盖掉。/usr/local也可被用于在一组主机之间共享程序和数据,不过不能在/usr
中也有同样的东西。下表所列的目录或符号连接必须出现在/usr/local中。
名称 功能
bin 局域性可执行文件。
etc 局域性软件的配置文件目录。
games 局域性育教娱乐可执行文件。
include 局域性C头文件。
lib 局域性程序库。
man 局域性在线指南。
sbin 局域性系统管理可执行文件。
share 局域性体系结构无关数据。
src 局域性源代码。
表2-4-7
默认情况下不会再有其他的子目录了。/usr/local/etc也可能符号连接到/etc/local,这更
适用于将所有配置文件放置在/etc下这一规则。
2.4.6 /usr/sbin
那些非必要的系统管理软件可执行文件就存放在这里。那些用于还原、更新、
修复系统,以及挂接/usr目录的系统管理程序都必须放置在/sbin目录下。
2.4.7 /usr/share
这里存放着所有体系结构无关的数据文件,它的内容可以共享给运行在任何体
系结构下的操作系统。例如,某个站点可能由i386、Aplpah和PPC三种体系结构平台
构成,但是它们可能共同维护唯一的一个挂接自某处的/usr/share目录。需要注意的
是,虽然/usr/share可以共享于不同体系结构之间,但必须保证它们运行的是不同发布
平台的相同操作系统。
任何程序或软件包包含或需要的不需要修改的体系结构无关的数据都应该保存
在/usr/share(如果安装的是局域性软件,应该是/usr/local/share)目录中。同时也建
议/usr/share下的任何子目录也应用于此目的。保存在/usr/share/games下的游戏数据必
须是纯粹的静态文件,类似于分值、日志等文件,必须放置在/var/games中。
下表所列目录或符号连接必须出现在/usr/share中:
名称 功能
man 在线指南。
misc 体系结构无关的其他数据。
表2-4-8
下表所列目录或符号连接,与你所安装的软件有关,如果有则必须出现
在/usr/share中:
名称 功能
dict 单词列表。
doc 其他文档。
games /usr/games的静态数据文件。
info GNU Info系统的主目录。
locale 本地信息。
nls 本地语言支持的消息目录。
sgml SGML数据。
terminfo terminfo数据库目录。
tmac 没有与groff一同发布的troff宏。
xml XML数据。
zoneinfo 时区配置信息。
表2-4-9
建议将那些应用程序专有的,且体系结构无关的目录放置在这里,例如:groff、
perl、ghostscript、texmf等等,为了向后兼容,某些发布版也会将它们放置在/usr/lib
下,出于同样的原因,/usr/share/games也可能会被这样处理。
2.4.8 /usr/share/man
本节会详细的介绍贯穿于整个系统的在线指南的文件组织结构,当然,也会包
括/usr/share/man目录下的组织结构。
/usr/share/man是各种指南页面存储的主要目录,它包含了“/”和“/usr”下大部
分程序的帮助信息。各指南页面被存储在
路径中。下面将逐步解释
当你打开/usr/share/man目录,你会看到如man1、man2、…、man8这样的子目录,
它们的具体分工如下:
lman1:公共可用的命令或软件的指南页面保存在这里。
lman2:所有系统调用(可以理解为类似Windows的API)的说明保存在这里。
lman3:库函数的说明信息保存在这里。
lman4:特种文件的说明保存在这里,如/dev目录下的设备文件和提供网络协
议支持的内核接口等。
lman5:大部分数据文件的格式说明保存在这里。这包括各种包含文件、程序
输出文件和系统文件。
lman6:寓教于乐程序、演示程序和一些不太重要的程序的说明文档保存在这
里。
lman7:其他类型的在线帮助信息保存在这里。如troff等文本处理宏的信息。
lman8:系统维护程序文档保存在这里。当然,这些程序有时也适合普通用户
使用。
上述目录也必须出现在/usr/share/man/
这是为了提供多语言版本支持而提供的,其中
常遗憾的是,没有中文语言的支持。
料还请参考其他文献,这里就不一一列举了。如果系统只使用一种语言的话,
同理,如果某些内容与体系结构有关的话,如设备驱动器或底层系统管理命
令,则需要将这些内容放置在对应的
保存为/usr/share/man/
/usr/local下的软件的在线帮助信息保存在/usr/local/man中。X11R6的保存
在/usr/X11R6/man中。它们的组织结构与/usr/share/man是相同的。
数字1至8即为前面讲述的
这个文件要带有一个“.
的帮助文档可能不遵循这一规则,反倒会使用它们自己定义的后缀。如X Window就
会使用.x作为后缀。
其他位置的帮助文档,提供多语言版本支持时,同样遵循/usr/share/man的多语言
版本支持规则,包括后面将要讲述的/var/cache/man目录。
2.4.9 /usr/share/misc
那些不需要在/usr/share目录下拥有自己目录的应用程序,会将它们的体系结构无
关的数据放置在这里。
下表所列内容就是会出现在/usr/share/misc目录下的内容:
名称 功能
ascii ASCII码表。
magic file命令使用的特征符号表。
termcap 终端性能数据库。
termacp.db 终端性能数据库。
表2-4-10
2.5 /var目录组织结构
/var目录包含着系统中绝大多数的可变数据,如系统日志、spool目录与文件等。
/var的某些内容不能共享给其他系统,最典型的就是/var/logs、/var/lock和/var/run。
当然,某些内容是可以共享给其他系统的,特别是/var/mail、/var/cache/man和
/var/spool/news。
/var的创建就是为了使得/usr可以只读方式被挂接成为可能。/usr里面的东西一旦
有被写入的要求,则必须转移到/var下。
若是无法为/var分配一个单独的分区,通常更好的选择是将/var转移至/usr目录下
(这是为了节省根分区的空间,让它尽量精小化)。不过即使这样,也千万不要直
接将/var连接到/usr,因为这就很难区分/var和/usr了。正确做法是将/var连接至/usr/var。
通常情况下,应用程序不得在/var顶层添加任何目录。这个位置只留给那些影响
到整个系统的软件,而且还需要经过Linux社团商议之后决定是否添加。
下表所列目录或符号连接是/var下必须具备的内容。
名称 功能
cache 应用程序的缓存数据。
lib 可变状态信息。
local /usr/local的可变数据。
lock 锁文件。
log 日志文件与目录。
opt /opt的可变数据。
run 与当前运行进程有关的数据。
spool spool应用程序数据。
tmp 系统重新启动期间起到保护作用的临时文件。
表2-5-1
下面列出的目录都是保留的,某些新的应用程序不得擅自使用它们,否则可能造成
与历史或局部性规则产生冲突:
/var/backups
/var/cron
/var/msgs
/var/preserve
当你的系统安装了某些程序,则下表所列的目录或符号连接必须放置在/var中。
名称 功能
account 记录进程的运行情况。
crash 系统崩溃产生的dumps。
games 寓教于乐程序的可变数据。
mail 用户邮箱文件。
yp 网络信息服务(NIS)数据库文件。
表2-5-2
2.5.1 /var/account
该目录包含了当前活动进程的运行情况日志和复合进程的利用率数据。这些内
容用在类Unix系统的lastcomm和sa程序。
2.5.2 /var/cache
该目录的内容是来自应用程序的缓存数据,这些内容可以加快那些费时的运算
或I/O处理任务。应用程序必须能够再生或恢复这些数据。与/var/spool不同,删除缓
存文件不会造成数据丢失错误。
/var/chace下的数据有一定的时效性,这取决于使用它的程序或系统管理员,有
时这两者会同时影响这些数据的时效性。应用程序必须有能力恢复被手工删除的缓
存数据(通常是因为磁盘空间有限,才会手工删除缓存文件)。另外,缓存数据没
有格式要求。
为缓存数据单独划分一个目录,就可以让系统管理员将它们设置在其他磁盘上,
而且还可以从/var的其他目录备份策略。这是/var/cache存在的意义所在。
2.5.3 /var/crash
虽然这个目录不能被Linux系统所使用(Linux不支持系统崩溃dumps),但是它可
以为其他支持这一功能类Unix系统所使用。
2.5.4 /var/games
/usr中那些寓教于乐类程序的可变数据存放于此,而那些静态数据,如帮助文本,
级别定义等内容,必须放在别处,最合理的就是/usr/share/games下。
2.5.5 /var/lib
该目录保存着那些与某个应用程序或系统有关的状态信息。状态信息,即程序
运行过程中不断修改的那些数据,且仅于具体系统有关。对于控制程序的具体行为,
用户不应该通过修改/var/lib下的文件来完成。
在应用程序启动期间或同一个程序不同实例之间,状态信息通常用于保护程序
的所处的状态的。
一个应用程序(或一组相关联的应用程序)必须为其状态数据占有/var/lib下的一
个子目录。不过还有一个misc子目录,专门提供给那些不需要一个子目录的程序。
其他子目录应仅提供给那些包含在发行版中有这一需求的应用程序。
/var/lib下的各子目录的命名应该被所发布的所有软件包支持,当然,不同的发布
版可以使用不同的命名方式。
2.5.6 /var/lock
锁文件必须被保存在/var/lock目录结构中。
锁文件用于硬件设备和其他被多个应用程序共享的资源,例如串行设备锁文
件,最初可能放在/usr/spool/locks或/usr/spool/uucp中,现在,必须放在/var/lock中。锁
文件的命名方式必须是“LCK..”后接设备及本名。例如,/dev/ttyS0的锁文件名
为“LCK..ttyS0”。
锁文件的内容组织格式,必须是HDB UUCP锁文件格式。HDB格式以10字节
ASCII数字字符存储进程ID,尾部带有换行符。例如,进程1230,它的锁文件将包含
11字符:空格、空格、空格、空格、空格、空格、1、2、3、0和换行符。
2.5.7 /var/log
该目录包含了各种日志文件。大多数日志文件都保存在这里或相应的子目录
中。下表所列的文件或符号连接应该出现在/var/log中,不过这取决于系统中安装的
具体软件。
名称 功能
lastlog 每个用户上次登陆记录。
messages 由syslogd产生的系统消息。
wtmp 所有登陆于登出地记录。
表2-5-3
2.5.8 /var/mailbox
此处是用户邮箱文件的存放处,所有邮箱文件必须以UNIX邮箱格式存储。
mail spool必须能够访问/var/mailbox,而且mail spool文件取自具体用户。此目录移自
/var/spool/mail,就是为了能够与几乎所有的Unix实现相一致。这一改变,使得一个
/var/mail共享于多个主机以及多个不同的Unix实现成为可能。更为重要的是,这不
需要转移mail spool的物理位置,不过,程序和头文件还需要转移到/var/mail中。
2.5.9 /var/opt
/opt下软件包的可变数据都应该安装到/var/opt下对应的子目录中去,这个子目录
名称应该与/opt下的子目录名称相一致,里面内容的组织结构没有特殊要求。
2.5.10 /var/run
从系统启动开始至当前的系统运行状态信息就保存于此。该目录下的文件,在
系统启动进程执行开始,必须被清空。程序可以在/var/run下拥有一个子目录;鼓励
程序使用不止一个运行时文件。进程标识文件(PID文件),原来是放置在/etc中
的,现在必须放置在/var/run中。PID文件的命名规则是“<程序名>.pid”,例如:
crond的PID文件名就是crond.pid。
PID文件的内部格式依然保持不变,必须以ASCII码的数字字符描述进程标识
符,同时末尾带有换行符。如,若crond的进程ID为25,则/var/run/crond.pid将包含三
个字符:1、2和换行符。
记录当前谁正在使用系统的utmp文件也保存在这里。那些使用Unix-domain套接
字的系统程序,必须将它们的套接字文件放置在该目录中。
2.5.11 /var/spool
/var/spool中的数据就是供以后用的(可以被程序,用户,管理员处理),一旦
被处理完毕,就会被删除。
下表所列的目录或符号连接,必须放置在/var/spool,不过这取决于系统中安装
的具体软件。
名称 功能
lpd 打印机spool目录。
mqueue 邮件待发队列。
news 新闻spool目录。
rwho Rwhod文件。
uucp UUCP的spool目录。
表2-5-4
2.5.12/var/tmp
/var/tmp目录使得应用程序要求在系统重起期间保护临时文件或目录成为可能,
因此,保存在/var/tmp中的数据,比/tmp中的数据得到更多的保护。
/var/tmp中的文件或目录,在系统启动时不得删除。
2.5.13 /var/yp
网络信息服务(NIS),著名的Sun Yellow Pages(YP),其可变数据,必须保存在
该目录中。/var/yp是NIS(YP)的标准目录,而且几乎被它所独占。
(未完...待续......)
Magic Linux开发入门指南(五)2.6 虚拟文件系统
前面几节所介绍的目录和文件,都是真真正正、实实在在的存储在具体的外部
存储设备上的,它们可能是在本机的硬盘、闪存、光盘中,可能保存在不只一个磁
盘分区中,也可能保存在网络中其它主机的存储设备中的。本节所介绍的几个文件
系统,虽然它们出现在根文件系统中,但它里面的内容却无法在任何外部存储设备
中找到,因为它们都在内存中。
2.6.1 proc文件系统
proc是一个重要虚拟文件系统,通过它里面的一些文件,可以获取系统状态信
息并修改某些系统的配置信息。proc文件系统本身不占用磁盘空间,它仅存在于内
存之中,为操作系统本身和应用程序之间的通信提供了一个安全的接口。当我们在
内核中添加了新功能或设备驱动时,经常需要得到一些系统状态的信息,一般这样
的功能可能需要经过一些象ioctl()这样的系统调用来完成。系统调用接口对于一些功
能性的信息可能是适合的,因为应用程序必须将这些信息读出后再做一定的处理。
但对于一些实时性的系统信息,例如内存的使用状况,或者是驱动设备的统计资料
等,我们更需要一个比较简单易用的接口来取得它们。proc文件系统就是这样的一
个接口,我们可以简单的用cat、strings程序来查看这些信息。例如,执行下面的命
令:
#cat /proc/meminfo
你可能会得到如下结果:
MemTotal: 254272 kB
MemFree: 104416 kB
Buffers: 23940 kB
Cached: 103972 kB
SwapCached: 0 kB
Active: 64660 kB
Inactive: 69276 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 254272 kB
LowFree: 104416 kB
SwapTotal: 522072 kB
SwapFree: 522072 kB
Dirty: 48 kB
Writeback: 0 kB
Mapped: 11708 kB
Slab: 11364 kB
CommitLimit: 649208 kB
Committed_AS: 16064 kB
PageTables: 320 kB
VmallocTotal: 770040 kB
VmallocUsed: 7916 kB
VmallocChunk: 760564 kB
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 4096 kB
同样的,free、df、top、ps等程序的功能实现,强烈依赖于proc文件系统,为了使用
那些程序,一定要使内核支持proc文件系统,并将其挂接到根文件系统的/proc目录
下。
下表所列的文件或符号连接会出现在/proc目录下,但并不详尽,具体都包含那
些,取决于你的内核配置和具体的硬件设备。
名称 功能
apm 高级电源管理信息。
buddyinfo Buddy算法内存分配信息。
cmdline 内核的命令行参数。
config.gz 当前内核的.config文件。
cpuinfo cpu信息
devices 可以用到的设备(块设备/字符设备)
diskstats 磁盘I/O统计信息。
dma 使用的DMA通道
execdomains 执行区域列表。
fb Frame buffer信息
filesystems 支持的文件系统
interrupts 中断的使用情况,记录中断产生次数。
iomem I/O内存映射信息
ioports I/O端口分配情况
kcore 内核核心映像,GDB可以利用它查看当前内核的所有数据结构状态。
key-users 密钥保留服务文件
kmsg 内核消息
ksyms 内核符号表
loadavg 负载均衡信息
locks 内核锁
mdstat 磁盘阵列状态
meminfo 内存信息
misc 杂项信息
modules 系统已经加载的模块文本列表
mounts 已挂接的文件系统列表
partitions 磁盘分区信息
pci 内核识别的PCI设备列表
self 访问proc文件系统的进程信息
slabinfo 内核缓存信息
splash splash信息
stat 全面统计状态表
swaps 交换空间使用情况
uptime 系统正常运行时间
version 内核版本
vmstat 虚拟内存统计表
zoneinfo 内存管理区信息
表2-6-1
下表所列的目录会出现在/proc目录下,但并不详尽,具体包含那些,取决于你
的内核配置和具体的硬件设备。
名称 功能
[number] 进程信息
acpi 高级配置与电源接口
asound ALSA声卡驱动接口
bus 系统中已安装的总线信息
dirver 空目录
fs 空目录
ide IDE设备信息
irq 中断请求设置接口
net 网络各种状态信息
scsi SCSI设备信息
sys 内核配置接口
sysvipc 中断的使用情况,记录中断产生次数。
tty tty驱动信息
表2-6-2
proc文件系统的详细内容可以通过执行 man proc命令获得,这里就不再进行更为详
细的介绍了。
2.6.2 sysfs文件系统
与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系
统。它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把连接在系
统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。其实
sysfs是从proc和devfs中划分出来的。
/sys目录下会包含下表所列的目录,但并不详尽,不过具体内容还取决于内的配
置和具体的硬件设备。
名称 功能
block 块设备
bus 系统总线
class 设备组
devices 系统设备
firmware 固件
kernel 内核
module 内核模块
power 供电系统
表2-6-3
讲述sysfs文件系统各目录的详细内容已经超出本文的范围,感兴趣的读者可以查看
有关Linux驱动程序开发的书籍。
2.6.3 devfs文件系统
devfs,也叫设备文件系统(Device Filesystem),设计它的唯一目的就是提供一
个新的(更理性的)方式管理通常位于/dev的所有块设备和字符设备。典型的 /dev
树包含数百个块特殊文件和字符特殊文件,它们全都在根文件系统上。每个特殊文
件都可以让用户空间进程轻松地与内核设备实现交互。举例来说,通过对这些特殊
文件执行操作,你的X服务器就能够访问视频硬件,fsck可以执行文件系统检验,lpd
可以通过并行端口向打印机发送数据。
实际上,通常Linux和Unix更“酷”的方面是,设备不是简单地隐藏在晦涩的
API 之后,而是真正地与普通文件、目录和符号链接一样存在于文件系统上。因为
字符和块设备是映射到普通文件系统名称空间的,我们通常可以用有意义的方式来
与硬件交互,可以仅使用标准 Unix 命令,如 cat 和 dd。除了有趣之外,这还使我
们有更强的能力,并提高生产力。
下表所列的文件或符号连接可能会出现在/dev目录下,但并不详尽,具体内容取
决于你的具体硬件设备。
名称 功能
audio 音品设备
cdrom[0-9] cdrom设备,这往往是一个符号连接
console 系统控制台
core 内核镜像,为/proc/kcore的符号连接
dmmidi 固件
dsp 内核
dvd dvd设备,这往往是一个符号连接
fb Frame buffer,一般为/dev/fb0的符号连接
fb0 Frame buffer设备
fd 文件描述符,/proc/self/fd的符号连接
fd[0-9]+ 软盘设备
floppy 软盘设备,一般为fd[0-9]的符号连接
full 满设备,任何写入操作都会失败,并把errno设为ENOSPC,以表示没有剩余空间。
fuse 用户空间的虚拟文件系统
hd[a-z][1-9]+ IDE硬盘
sd[a-z][1-9]+ SCSI或SATA硬盘
hpet HPET设备
initctl 用户与init进程交互的通道。
kmem 存取经过内核虚拟之后的内存
kmsg 任何对该文件的写入都将作为printk的输出
log syslog的本地套接字
loop[0-9] 回环设备,一个磁盘文件模拟一个块设备。
lp[0-9] 打印机
md[0-9] RAID设备
mem 直接存取物理内存
midi MIDI设备
mixer 混音器
null 控设备。任何写入都将直接被丢弃,任何读取都将得到EOF
parport[0-9] 并口
port 存取I/O端口
ppp ppp拨号设备
psaux PS/2鼠标
ptmx 所有PTY master的复用器
pty[a-z]+[1-9]+ 伪终端,用于创建ssh等登陆会话
ram[0-9]+ RAM disk,initrd只能使用ram0
ramdisk ram0的符号连接
random 随机数发生器。完全有用户的输入来产生随机数。如果用户停止所有动作,则停止产生随机数。
root 根分区,这是一个到根分区设备的符号连接。
rtc 实时时钟
stderr 标准错误输出,/proc/self/fd/2的符号连接
stdin 标准输入,/proc/self/fd/0的符号连接
stdout 标准输出,/proc/self/fd/1的符号连接
tty[1-9,a-z]+ tty虚拟控制台,tty为当前控制台
ttyS[0-9] 串口
urandom 更快,但是不够安全的随机数发生器。
vcs[1-9,a-z]+ 虚拟控制台的文本内容,一一对应。
X0R /dev/null的符号连接
XOR /dev/null的符号连接
zero 0字节源,可以读取到无限多的0字节。
表2-6-4
下表所列的目录可能会出现在/dev目录下,但不是全部,具体情况与你的内核配
置和硬件设备有关。
名称 功能
cpu cpu信息
disk 磁盘/光盘信息,里面的内容为具体设备的符号连接
input 设备组
mapper RAID和LVM控制接口
net VMware和Qemu等使用的虚拟网卡
pts devpts文件系统挂载点,用于实现伪终端
raw 裸设备操作
shm Linux独有的tmpfs文件系统挂接点。
snd 声卡控制接口
表2-6-5
devfs文件文件系统是内核自动挂接的,当挂接好devfs文件系统后,才能正确执
行init程序。Linux上devfs文件系统与后面讲述的tmpfs文件系统使用相同结构。
2.6.4 tmpfs文件系统
tmpfs文件系统是Linux特有的文件系统,唯一的标准挂接点是/dev/shm。当然,用
户可以将其挂接在其他地方。
tmpfs有些像虚拟磁盘(ramdisk),但不是一回事。说其像虚拟磁盘,是因为它
可以使用你的RAM,但它也可以使用你的交换分区。传统的虚拟磁盘是一个块设
备,而且需要一个mkfs之类的命令格式化它才能使用。tmpfs是一个独立的文件系
统,不是块设备,只要挂接,立即就可以使用。
tmpfs的大下是不确定的,它最初只有很小的空间,但随着文件的复制和创建,
它的大小就会不断变化,换句话说,它会根据你的实际需要而改变大小;tmpfs的速
度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越;
由于tmpfs是驻留在RAM的,因此它的内容是不持久的,断电后,tmpfs的内容就消失
了,这也是被称作tmpfs的根本原因。
有效的使用tmpfs可以极大的提高应用程序或整个系统的性能。有关tmpfs的详细
内容,还请读者参阅其他相关著作,毕竟本文讲述的是一个Linux发行版的开发。
2.6.5 usbdevfs文件系统
顾名思义,usbdevfs就是USB设备文件系统,它是一个动态生成的文件系统,有
些类似于proc文件系统。它的标准挂接点是/proc/bus/usb,当然,也可以挂接到其他
地方。它主要用于:用户级驱动、即插即用、提供USB设备信息、应用程序轮询
USB设备的变化等。
2.6.6 devpts文件系统
devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要
pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文
件。挂接时,UID、GID及其工作模式会指定给devpts文件系统的所有pty文件。这可
以保证伪终端的安全性。
讨论devpts文件系统的详细内容,已经超过本文范围,还请读者参考其他专
著。
2.7 结束语
贯穿Linux的整个文件组织结构,无不洋溢着那继承自Unix的深厚的文化底蕴,
美不胜收。使我不得不感叹,Linux是多么伟大的一个操作系统。正是由于这些,
Linux才长久不衰,越发显得青春洋溢,让我们这些热爱Linux的人为其流连忘返。
到了这里,对于Linux的文件组织结构,我相信各位读者应该有了感官上的认识
了,我也再次感谢你,为了Magic Linux,能够坚持将这些丑陋的文字读到这里。在
下一章里,我将讲述如何利用本章与上一章的知识,来构建一个能够工作的基本系
统。
(未完...待续...)
chengshiding 于 2010-11-16 19:12:56发表:
呵呵:0)1
wangyu 于 2010-11-16 09:05:42发表:
额,此文被称为中国版LFS,还是蛮多有用的,不过我还是看原帖吧
nxzcc 于 2010-11-16 08:42:44发表:
好长噢
wst021 于 2010-11-16 04:34:22发表:
{:2_92:} 强贴,不顶不收藏,真是不会学习 Linux 。
相思爱文 于 2010-11-15 23:38:57发表:
先加入书签,慢慢再看。
长帖,是一定要读完的
shenhao0129 于 2010-11-15 23:27:09发表:
额。。。我就交叉编译过内核,呵呵