LFS构造原理的分析
摘 要:随着Linux用户的增加,越来越多的人愿意自己定制自己的操作系统,LFS就是一种流行的从源代码构建Linux的一种。本文该方法的构建原理,重点分析了Binutils、Gcc和Glibc在构建过程种编译多次的原因。关键词:定制 LFS Binutils Gcc Glibc
一、引言
Linux 是和 Unix很相似的一种操作系统,具有Unix的全部特征,并和POSIX兼容。它是一个真正的多用户多任务操作系统,是一个优秀的软件开发平台。Linux最大的特点是它是自由的,这种自由有双重含义。一方面, Linux的自由的意义是它是免费的,不必花费成本就可以得到它。Linux自由还有另一个重要的体现,那就是Linux 可以提供无限宽广的技术发挥的自由空间。在购买到Linux之后得到的不仅仅是一个操作系统,还得到了系统的源代码。这样如果不喜欢Linux 的工作方式,就可以改变它(不仅仅是做微小的改动,你甚至可以安装你的需求去改动整个操作系统)。只要按照通用公共许可证(General Public License)的要求,即可以无偿地自由采用,改进,。这也正是Linux发展如此迅速的一个原因。
开放源代码,也使越来越多的人不甘于使用现成的发行版,要想对Linux完全满意,必须从头构建自己的系统。本文的LFS正是构造Linux的一个方法。
二、LFS及其特点
LFS是Linux From Scratch的缩写。"From Scratch"是一个词组,它的意思是"从零做起,白手起家,从无到有"的意思,因此"Linux From Scratch"本质上不应当理解为一个Linux发行版名称。它最贴切的含义应当是一种"方法/思想":一切从源代码开始的方法/思想。如果把 LFS 比作建筑房子,那么LFS 提供房子的框架蓝图,但是需要你自己建造它。
使用现有的 Linux 系统来开发自己定制的系统,这个"完美的"Linux 系统将拥有各种发行版的优点而没有它们的缺点。用户可以控制系统的所有特征,包括目录布局、脚本设置和安全设置等等。最终的系统将从源代码直接编译生成,用户可以指定在哪里安装、为什么安装以及怎样安装每一个程序。可完全按照自己的需求定制Linux 系统,而且对系统有更多的控制权。
三、LFS的好处
LFS 存在的一个重要原因是可以帮助人们 Linux 系统内部是如何工作的。构建一个 LFS 系统会帮助演示是什么使 Linux 运转,各种组件如何在一起互相依赖的工作。最好的事情是通过这种学习可以获得完全根据自己的需求定制 Linux 系统的能力。
LFS 的一个关键的好处是它让用户对于系统有更多的控制,而不是依赖于他人的 Linux 实现。在 LFS 的世界里,你自己坐在司机的位置,掌控系统的每一个细节。
LFS 的另一个好处是可以创建一个非常小巧的 Linux 系统。当安装一个常规的发行版时,人们经常要被迫安装一些可能永远不会用到的程序。这些程序浪费宝贵的磁盘空间,更糟的是占用 CPU 资源。自己定制 Linux 系统的另一个好处是安全性。通过从源码编译整个系统,你能够审查任何东西,打上所有的安全补丁,而不需要等待别人编译好修补安全漏洞的二进制包。除非是你发现并制作补丁,否则你无法确保新的二进制包被正确编译并修正了。
四、LFS的构造原理
要基于源代码的方式来编译整个系统,那首先要解决的就是工具链的问题,即需要一个编译环境。所以构造LFS系统分两大步:一是构造一个临时的编译环境;二是构建LFS系统。
临时编译环境其实也相当于一个小的 Linux 系统。只不过这个系统将仅包含必要的工具,能够构建最终的LFS系统。构建这个小系统分两步进行,第一步是构建一个新的不依赖于宿主系统的工具链(编译器、汇编器、连接器、库文件以及一些有用的软件),第二个步骤是利用这个工具链去构建其它基本的工具。
在工具链中最基本的是:Binutils、GCC和Glibc。Binutils 是一组开发工具,包括连接器,汇编器和其它用于目标文件和档案的工具。GCC 软件包包含 GNU 编译器,其中有 C 和 C++ 编译器。 Glibc 包含了主要的C库。这个库提供了基本的例程,用于分配内存、搜索目录、打开关闭文件、读写文件、字串处理、模式匹配、数学等等。其它的工具必须在他们的基础上建立。所以在下面讲解的过程主要围绕着这三个工具的安装,以及工具链的调整为主。
那么如何来创建一个这样的编译环境呢?那就需要一个现成的系统,称它为宿主系统。现在总体的思路就是在宿主系统创建一个临时的环境,可以chroot到这个环境,在该环境的基础上构建一个干净、没有的LFS系统。为了尽量的与宿主系统分开,所以要创建一个自包含、自依赖的工具链。由于这里的工具链只起临时作用,在完成LFS后可以将其剥离,所以可将编译的所有文件都放在同一个文件中($LFS/tools)。在构建过程中应注意以下几点:
1.这个过程在原理上与交叉编译类似,通过把工具安装在同一个目录(使用相同的“prefix”)中以便协同工作,还利用了一点 GNU 的 “magic”。
2.小心处理标准连接器的库文件搜索路径,确保程序仅连接到指定的库上。
3.小心处理 Gcc 的 specs 文件,告诉编译器要使用哪个动态连接器。
下面看看具体时如何实现的。
为了创建一个干净的工作环境,在宿主系统中新创建一个lfs用户组,并添加了lfs用户,在安装过程中将一直使用该用户。
首先编译Binutils ,这时是使用宿主系统的环境。毫无疑问现在利用Binutils生成的程序会受到宿主系统的。例如:使用生成的ld(标准连接器)程序将会连接到默认的/lib目录(宿主系统)下的二进制文件。
然后编译Gcc,仍然需要宿主系统的环境。显然Gcc也受宿主系统的影响,这可以从它的编译来看,它依赖的是宿主的Glibc,而Binutils可以使用刚生成的。Glibc提供了动态连接器,用来找到并加载一个程序运行时所需的共享库,在做好程序运行的准备之后,运行这个程序。此时生成的Gcc会使用/lib(宿主系统)下的动态连接器,而不是$LFS/tools/lib下的。
通过上面两步,我们就可以使用刚生成的bintuils和Gcc来编译Glibc了。现在我们将bintuils、Gcc和Glibc都重新编译了一次。接下来就要通过调整工具链来解决刚提到的两个问题。一是重新编译ld,将ld连接到$LFS/tools/lib下的函数库。二是调整动态连接器,修改Gcc的SPEC文件,将动态连接器连接到/tools/lib/ld-Linux.so.2(ld-Linux.so.2是动态连接器的名字)。
到这里看似可以编译其它的工具了,但是接下来的工作并不是如此,而是再次编译了bintuils和Gcc,然后用第一次生成的`Glibc和现在生成的bintuils、Gcc来编译其它的工具,整个临时环境才搭建成功。
这里有个问题是为什么要将Bintuils和Gcc编译两次,可以直接用宿主系统?第一次编译bintuils和Gcc的目的一方面是为了编译Glibc;另一方面是为了能自己编译出第二遍的Gcc,即使得Gcc是自我编译的。如果直接使用宿主系统可以满足编译Glibc的要求,但是Gcc就不是自我编译了。这里为了保证制造的正确性以及使Gcc是自我编译,所以Binutils和Gcc比其它的工具都多编译一次。
接下面其它工具的编译都是使用第二次编译的Binutils和Gcc以及第一次编译的Glibc。至此,工具链就准备好了,我们可以利用这些工具生成最终的系统。同样最先生成的软件还是Binutils和Gcc,不过在编译它们之前,我们先编译出Glibc,它也是我们最终需要的C库。再次调整工具链,让随后编译的工具都连接到这个库上。不难理解,在前面的调整中我们将工具链使用的库从宿主系统转向新安装的库目录。同样,现在将工具链所使用的库从临时的库转向LFS系统最终的库目录。
系统中的其它软件都是由最新编译的Binutils、Gcc和Glibc编译的。最后就是经过配置一些简单的系统启动脚本、创建fstab文件、编译内核并安装引导程序,最终的LFS系统就可以启动了。
五、LFS的扩展
通过上面的大致的过程:通过宿主系统搭建了临时的工具链,然后通过工具链构建了最终的目标系统LFS。将LFS建成以后,就可以脱离临时编译环境。本文所说的编译都是相同的平台,前面也提到构造临时编译环境的原理与交叉编译环境相似,结合我们搭建交叉编译环境的过程,如果我们搭建的临时工具链是一个交叉编译环境,最终就应该可以实现一个跨平台的目标系统。当然具体的细节问题还需要进一步探讨。
六、小结
直接从源代码构建Linux操作系统,不仅能够定制自己理想的操作系统,更重要的通过构造LFS能够加强我们了解Linux内部是如何工作。对于有志Linux系统的人,这个构造很有必要。同时,我们应该抓住开放源代码的机遇,认真研究,同时做出自己的贡献,推进其。
[1] 王景中. Linux安全最大化.出版社.2000.5.P3.
[2] Gerard Beekmans. http://www.linuxfromscratch.org.
[3] http://youbest.cublog.cn/ .换个角度看LFS——反向分析LFS. 2006.6.
[4] http://www.linuxsir.org/bbs/showthread.php?t=240687. LFS (版本6.1.1) 安装心得.