[原创]换个角度看LFS——反向分析LFS
时间:2006-06-21
来源:互联网
2006-08-12:修改有歧义的句子一处。
2006-06-21:增加对结尾插图的说明。
2006-06-21:修改最后一副插图。
2006-06-21:修改笔误一处。
前言
写了几篇关于LFS的制作过程中的文章,但分析性的文章还没怎么写过,论坛上也有一些分析性的文章,但大多数都是真对某个特定部分的,最近酝酿了一下,准备写点分析性质的文章调剂调剂。
这次用的标题大概已经能说明本文分析的角度,按照LFS的顺序写,似乎总不能摆脱LFS的制作过程的牵制,总觉得像写制作教程,所以决定反过来写,利用一个大家熟悉的情景为开始反过来推出整个LFS的过程,本文不能算专业的分析文章,只是想简单的说明白LFS为什么要这样的过程。
本文并不是要完全还原LFS,只是为了说明一种分析过程,因此文中部分内容和实际的LFS略有出入。
限于水平的问题,我只能将我现在的理解来写,如果有什么错误或者不当的地方希望大家及时指出。
本文的读者应该是一个已经经历过LFS至少一次的朋友,如果你从来没搞过LFS,建议亲自动手制作一遍后再看本文,应至少看过下面文章中的一篇:
《Linux from scratch》英文版本
《LFS-Book 6.1.1 中文正式版》
《手把手教你如何建立自己的Linux系统(LFS速成手册)》
更新,由于篇幅比较长所以难免出现一些错误或者笔误,也有可能加入新内容,因此难免会进行修正或增删一些内容,如果本文被转载您可以在本人的Blog或者http://www.linuxsir.org的LFS版中中查看最新版本。
我的Blog:http://youbest.cublog.cn
linuxsir:http://www.linuxsir.org/bbs/showthread.php?t=262010
如须转载请注明作者为冲天飞豹(youbest),并请保持文章的完整和提供转载出处。
工作情景:
我正在用VIM编辑一篇文件
分析:
问:那么要完成这个任务我需要些什么呢?
答:硬件(略),本文将不对硬件做任何分析。
软件:VIM
问:那么运行VIM又需要什么条件呢?
答:一个Linux内核
一组支持VIM运行的动态库,按照比较标准的组合,应该是glibc和ncurses这两个库来承担VIM的运行时动态库
问:那么内核需要什么条件呢?
答:符合内核运行的硬件环境。
问:glibc又需要什么条件呢?
答:于glibc相适应的Linux内核
问:ncurses需要什么条件呢?
答:合适的glibc
最后我们来画一副图来描述这个关系
那么这个关系图基本上就可以描述一个VIM运行的环境需求,当然在启动VIM的过程中少不了一个shell的参与,通常我们用BASH来完成,那么这个任务的整个大致环境和关系大致如下图所示。
清楚了这个问题,下面需要解决的就是这个运行环境从哪里来的呢?
通常Linux下的软件都提供了源代码,我们可以用这些源代码来组合成我们自己需要的二进制程序。
在这个例子中,我们想要有一个VIM,那么我们要先下载一份VIM的完整源代码,然后利用一组编译工具来完成VIM的编译。
对于一个通常编译的过程大致如下图
这其中最复杂的就是make阶段,make会调用目录中的Makefile来执行一系列的工作,其中包括创建必要的文件,以及调用gcc和binutils来编译源代码和链接目标文件,最终生成需要的可执行文件和附属文件。
所以make过程中一般需要用到的是gcc,binutils,make和一些系统程序(不同的软件需求不同)。
这样我们再来画最开始的运行环境的图
好了,这样我们就清楚了这个整个运行时候的环境是从哪里来的了。用红色虚线框起来的就是整个构造运行时环境的必要条件了,其实就是我们通常在LFS中最常见到的一个词汇:工具链。
那么下面的一个问题就是这些工具运行的条件是什么?
这些编译环境中的应用程序也和其它程序一样必须有运行的环境:
GCC依赖于glibc
binutils依赖于glibc
make依赖于glibc
头文件是在编译时候gcc所需要的,但本身都是一些文本文件,因此没有需要的运行环境。
常用工具依赖于glibc和各种需要用到的动态库。
这里的一个新问题就是编译环境中使用的glibc和Linux内核和“运行时环境”中的glibc与Linux内核是否是同一个。
答案很显然,绝对不是,因为“运行时环境”中的glibc和Linux内核是依靠工具链中编译工具来完成的,所以工具链所依赖的glibc和Linux内核于目标系统的不可能是同一个(但版本什么的是可以一样的,这里说的不同是指已经编译成二进制的实体不同)。
说明:
为了说明方便,下面将“运行时环境”称为目标系统。
但就LFS而言目标就是要做一个通用的可自主扩展(编译)的系统,所以在完成目标系统的glibc后又编译了一整套工具链中的东西,目的是将工具链中的工具全部移植到目标系统中,以便在完成目标系统后可以抛弃工具链而又能够自主的进行编译,而这些工具依赖的环境就是目标系统的glibc了。
内核这东西比较特殊,虽然运行任何程序都需要用的内核,但本身在制作目标系统过程中,目标系统的Linux内核却不需要先进行编译,因为使用Linux内核并不像glibc那样是依靠动态链接库的方式被调用的,内核不是用动态库的方式被调用的,因此不需要先编译,只要提供对应的头文件即可,后面将不再探讨Linux内核的问题。
作为LFS的另一个目标就是要建立一个“纯净”的系统,因此编译glibc的编译器和最后目标系统里的编译器应该保持一致,同时目标系统是完全依靠工具链编译出来的,而工具链应该是在目标系统建立完毕后可以很方便的剥离掉的,而且为了保持工具链的稳定工具链中的工具所依赖的glibc以及其它用到的动态库应该是不会被替换掉的。
要解决上面这个问题,那么最好的方法就是将工具链放置在一个独立的目录中,LFS将其放在了/tools目录下,因此在用工具链建立目标系统的过程中将PATH设置为/bin:/usr/bin:/tools/bin来让bash调用命令时能首先调用目标系统中已经建立好的命令,如果没有则从工具链中调用。这样的话在目标系统还没有编译工具的情况下使用工具链来进行编译。
好了,现在已经可以用工具链来完成目标系统的编译了,下面的问题就是这个工具链是如何来的呢?
这个问题就要回到前面所提到的工具链运行时所依赖的环境,还是这个glibc,要编译这个glibc必须是在工具链的编译工具生成之前,而工具链的编译工具又依赖于glibc,那么这个glibc就不能是现在工具链中的编译工具编译出来的,那么是谁编译的呢?
这个问题的答案:当然还是编译器编译的这个glibc和工具链里的编译工具,也就是说在工具链中的编译工具编译目标系统之前需要另一个编译器来编译这个在使用的工具链中的编译器和编译器所依赖的glibc。
用蓝色的虚线框起来的部分就是生成工具链的工具链,我暂时称为“预工具链”。
“预工具链”的存在则能完成工具链中的glibc,以及工具链中的编译工具,并且工具链中的工具将被编译成依赖于工具链中的glibc。
那么现在要解决的问题就是这个“预工具链”是如何建立起来的。
答案还是一样,需要一套编译工具来编译出这个“预工具链”。
现在要提到的一个问题就是,用不同版本的gcc编译出来的程序可能不一样,而不同的gcc编译出来的目标文件要用能正确处理的binutils的版本来链接成可执行文件,因此我们现在已经能确定的gcc、binutils版本是工具链中的版本,那么用工具链编译出来的目标系统是符合我们的要求的。
那么编译工具链中的glibc和gcc以及binutils的gcc和binutils的版本现在还没有确定,但根据LFS的“纯净”目标,也就是说gcc和binutils也必须和工具链中的gcc和binutils相同才行。而其它的常用工具及make虽然参与,但不会对编译的二进制产生影响(前提是必须能正确处理它应该做的事情)。
现在的问题就集中到“预工具链”中的gcc和binutils上了,只要能编译出和工具链中的gcc与binutils相同版本就可以了,也就是说只要一套编译环境能编译gcc和binutils就可以了。
那么问题是这一套编译环境是怎么来的呢?
如果还继续按照前面的套路,这个问题就成了无穷无尽的了,这个不是我们想做的,现在已经能“纯净”的建立一套符合条件的工具链就已经达到我们的目的了,因此编译“预工具链”的binutils和gcc的任务我们就采用一套能正确编译的发行版或者预制好的编译环境就可以了,我们可以称这个发行版或者预制好的编译环境为“主系统”。
那么我们现在只要能用这个“主系统”编译出“预工具链”的binutils和gcc就可以了,“预工具链”中的其它部分就直接采用“主系统”里的就可以了。
但这里有一个问题就是:“主系统”中的gcc和binutils与“预工具”中要求的gcc和binutils版本不同(通常会老些),但只要能正确编译binutils、gcc就行了,gcc具备自我编译的功能,因此建议编译不同版本的gcc采用bootstrap的方式比较好。当然“主系统”中的在参与编译过程中的其它工具也需要符合要求就成。
最后用一副图来表达简单的表达整个过程,也算本文的结尾。
(转载请保持文章的完整性,请注明作者和出处)
作者:冲天飞豹(youbest)
Email:[email protected]
2006年6月21日
希望大家能多写一些分析性的文章,共同提高。
说明:上图的内容实际上并不是标准的LFS编译关系,按照LFS的做法,应该在预工具链编译完成工具链中的glibc、gcc和binutils,工具链后续的部分是由工具链的gcc和binutils来完成的,但本文并不追求和LFS完全一致,只是为了说明整个过程是如何反向推出来的,而且我认为按照图里的方法也完全没问题,完全不影响目标系统的“纯净”度。
图中实际上也表现了一种可以用来编译不同平台上的工具链的方法,因为在编译整个工具链的过程中都由预工具链来完成的(此方法我自己没实验过,只是一个设计)。
更新日志:
2006年6月21日:
修改笔误一处
由linuxsir上的doom3d发现并报告
2006年6月21日:
在结尾的图片上加入gcc的bootstrap的标记。
由linuxsir上的doom3d建议
2006年6月21日:
对结尾的图片做出说明。
2006年8月12日:
修改有歧义的句子一处。
作者: youbest 发布时间: 2006-06-21
作者: 晨想 发布时间: 2006-06-21

作者: youbest 发布时间: 2006-06-21
作者: demonlj 发布时间: 2006-06-21
作者: 信天游 发布时间: 2006-06-21
作者: wangtzh 发布时间: 2006-06-21
作者: 终极幻想
天啊。。youbest的文章,简直就是 精华贴 的代名词啊。。。。。
|
好了,现在已经可以用工具链来完成目标系统的编译了,下面的问题就是这个工具链是如何来的呢? 这个问题就要回到前面所提到的工具链运行时所依赖的环境,还是这个glibc,要编译这个glibc必须是在工具链的编译工具生成之前,而工具链的编译工具又依赖于glibc,那么这个glibc就不能是现在工具链中的编译工具编译出来的,那么是谁编译的呢? 这个问题的答案:当然还是编译器编译的这个glibc和工具链里的编译工具,也就是说在工具链中的编译工具编译目标系统之前需要另一个编译器来编译这个在使用的工具链中的编译器和编译器所依赖的glibc。 |
更正:
现在的问题就集中到“预工具链”中的gcc和binutils上了,只要能编译出和工具链中的gcc与binutils相同版本就可以了,也就是说只要一套编译环境能便宜gcc和binutils就可以了。 |
但这里有一个问题就是:“主系统”中的gcc和binutils与“预工具”中要求的gcc和binutils版本不同(通常会老些),但只要能正确编译binutils、gcc就行了,gcc具备自我编译的功能,因此建议编译不同版本的gcc采用bootstrap的方式比较好。当然“主系统”中的在参与编译过程中的其它工具也需要符合要求就成。 最后用一副图来表达简单的表达整个过程,也算本文的结尾。 |
最後,图中提及过头文件,我们上次探讨过内核头文件要用"净化过"的版本来编译 glibc,不知道这里是否适合加入一点补充?
060621141822a.png (74.4 KB, 101 次查看) |
作者: d00m3d 发布时间: 2006-06-21
作者: d00m3d 发布时间: 2006-06-21
应该是用插入图片的方式吧?应该不是上传的图片
作者: LanEast 发布时间: 2006-06-21
作者: d00m3d
便宜gcc和binutils 应该是 编译gcc和binutils 吧
|
作者: d00m3d
提议在最後的那张图加入 Bootstrap 的行径,如附图
|
作者: d00m3d
最後,图中提及过头文件,我们上次探讨过内核头文件要用"净化过"的版本来编译 glibc,不知道这里是否适合加入一点补充?
|
作者: d00m3d
弱问:youbest 你是怎样把图贴在文章之间的?这样排版比上传图片缩在後面好多了!
|
作者: youbest 发布时间: 2006-06-22
作者: LanEast
这样的文章真的不错,我之前也有过这样的考虑,还有些奇怪的想法,不过一直没整理过
应该是用插入图片的方式吧?应该不是上传的图片 |
作者: youbest 发布时间: 2006-06-22
我不熟识 Gimp,在练习图里加字及旋转的时候看见 gcc 源代码到 gcc 便把字加上去了,没看清楚

今天工作太累了,要休息了。。。
作者: d00m3d 发布时间: 2006-06-22
作者: 终极幻想
天啊。。youbest的文章,简直就是 精华贴 的代名词啊。。。。。
|
作者: zlbruce 发布时间: 2006-06-22
作者: soloforce 发布时间: 2006-06-22
作者: lvjinhua 发布时间: 2006-06-22
作者: 毛毛熊 发布时间: 2006-06-22
作者: hongfeng 发布时间: 2006-06-23
作者: toxicbug 发布时间: 2006-07-02
youbest兄虽然建议安装过的人再来看,但提前了解这些内容对安装也是很有帮助,知道了为什么这么做,第一次安装就可以学习到更多的东西。
再次感谢youbest所做出的贡献!
作者: windyzhousir 发布时间: 2006-07-06
作者: zoujh 发布时间: 2006-07-07
内核这东西比较特殊,虽然运行任何程序都需要用的内核,但本身在制作目标系统过程中,目标系统的Linux内核却不需要先进行编译,因为使用Linux内核并不像glibc那样,是依靠动态链接库的方式被调用的,因此后面将不再探讨Linux内核的问题。 |
但这里有一个问题就是:“主系统”中的gcc和binutils与“预工具”中要求的gcc和binutils版本不同(通常会老些),但只要能正确编译 binutils、gcc就行了,gcc具备自我编译的功能,因此建议编译不同版本的gcc采用bootstrap的方式比较好。当然“主系统”中的在参 与编译过程中的其它工具也需要符合要求就成。 |
作者: 晨想 发布时间: 2006-08-12
作者: 终极幻想
这个,是因为装了 kernel header 的原因。:)。。
|
已在原文改正。
作者: 终极幻想
gcc 的 bootstrap 不是用来解决这个版本问题的,纯属是用来检查编译出来的 gcc 是否正确。。bootstrap 和 非 bootstrap 编译出来的gcc 是一样的。不同的是 bootstrap 在编译了再一次后,又用编译出来的gcc编译一个gcc,再用第二个gcc编译最后一个gcc,如果结果相同,那么就可以认为这个gcc是正确的。这样也就解释了,为什么在 CrossLFS 的时候,一般不用 bootstrap,因为后几次的无法进行,不同架构的说。当然,相同体系的话,还是可以进行。。。
|
关于bootstrap的作用,我觉得在LFS确实是为了保证gcc是用和自己相同版本编译的原因,因为在整个LFS中三次编译gcc,只有第一次用了bootstrap,那就是因为主系统的版本不确定,而后两次已经能确定gcc的版本一致,所以没有再用bootstrap,如果按照检查是否正确的说法的话,后两次的gcc的作用更重要,却不做bootstrap就说不过去了,所以我认为,bootstrap就是为了在不同的gcc版本编译的时候用的.
作者: youbest 发布时间: 2006-08-12
作者: sumargin 发布时间: 2006-08-12
作者: sumargin
youbest,肯定花了大把的时间在LFS上了吧?
|
作者: youbest 发布时间: 2006-08-12
作者: 旅行者2号 发布时间: 2006-08-12
作者: youbest
这里纯属LFS分析,所以没考虑CLFS(不过在我的CLFS2中的分析里也写了一些).
关于bootstrap的作用,我觉得在LFS确实是为了保证gcc是用和自己相同版本编译的原因,因为在整个LFS中三次编译gcc,只有第一次用了bootstrap,那就是因为主系统的版本不确定,而后两次已经能确定gcc的版本一致,所以没有再用bootstrap,如果按照检查是否正确的说法的话,后两次的gcc的作用更重要,却不做bootstrap就说不过去了,所以我认为,bootstrap就是为了在不同的gcc版本编译的时候用的. |
bootstrap 的作用:
For a native build issue the command `make bootstrap'. This will build the entire GCC system, which includes the following steps:
1. Build host tools necessary to build the compiler such as texinfo, bison, gperf.
2. Build target tools for use by the compiler such as binutils (bfd, binutils, gas, gprof, ld, and opcodes) if they have been individually linked or moved into the top level GCC source tree before configuring.
3. Perform a 3-stage bootstrap of the compiler.
4. Perform a comparison test of the stage2 and stage3 compilers.
5. Build runtime libraries using the stage3 compiler from the previous step.
所以说,这个不是校正版本用的,只是用来检查。不管版本是否相同。
作者: 晨想 发布时间: 2006-08-13
作者: 终极幻想
恩,不管 CLFS 还是 LFS,我们讨论的是 bootstrap。
bootstrap 的作用: For a native build issue the command `make bootstrap'. This will build the entire GCC system, which includes the following steps: 1. Build host tools necessary to build the compiler such as texinfo, bison, gperf. 2. Build target tools for use by the compiler such as binutils (bfd, binutils, gas, gprof, ld, and opcodes) if they have been individually linked or moved into the top level GCC source tree before configuring. 3. Perform a 3-stage bootstrap of the compiler. 4. Perform a comparison test of the stage2 and stage3 compilers. 5. Build runtime libraries using the stage3 compiler from the previous step. 所以说,这个不是校正版本用的,只是用来检查。不管版本是否相同。 |
作者: d00m3d 发布时间: 2006-08-16
作者: d00m3d
常言道,引用應註明出處。。。(閃)!
|
嘿嘿,因为是从 gcc 包里边的文件看到的,所以没写出引用。。sorry。
(给我追,看你往哪闪)。。
作者: 晨想 发布时间: 2006-08-17
作者: iveney 发布时间: 2007-06-21
作者: unix_org 发布时间: 2007-06-22
作者: beyond1314 发布时间: 2007-12-11
作者: biosxjj 发布时间: 2007-12-11
作者: biosxjj
我觉得linux 真实非常的复杂这么多工具包 为什么不综合为1个 程序库呢 毕竟统一才是王道
|
我想LINUX就象是玩具,别人都给你把部件做好了,就看你怎么玩
作者: proyj 发布时间: 2007-12-11
作者: linux001 发布时间: 2007-12-11
作者: linux001
当一个已经完成的LFS系统需要进行 glibc 的主要版本升级,过程会是怎么样的?我觉得这个问题更有意义。
|
作者: d00m3d 发布时间: 2007-12-11
我的疑惑是, 编译一套纯净的系统, 所以要编译 预工具链 , 再编译 工具链, 最后用完全用工具链, 但是, 我疑惑的是, 你编译的工具链 终归揭底都是 用 主系统的 gcc binutils等工具编译的, 还是依赖于主系统的阿, 那 我何不 别编译 预工具链 工具链了, 而是直接 编译最后的运行环境 如图中的 内核 readline vim, 这样难道脱离了 主系统 就不能运行了吗?
作者: zyl19861126 发布时间: 2008-03-23
内核:应该可以运行,但是它是用宿主机的编译器编译的,而不是LFS中的版本,可能优化得就没那么好了。
readline vim等等,如果用静态方式编译,应该也可以运行。不过正如前述,它不是用LFS中的版本。要是动态编译,一般来说没戏。因为它会依赖于宿主机的动态库,一旦找不到了或者版本错误,就会拒绝运行。
另外,你还是没有看懂LFS的步骤。一套纯净的系统意味着:所有的代码都是用系统中安装的GCC版本编译出来的,所依赖的库全部都在系统内部。
这意味着,一个纯净的系统至少要编译两次工具链:首先用宿主系统中的版本来编译目标版本的工具链,然后再用目标版本的工具链来编译系统。至于LFS的三次编译,目的是为了尽量隔离主系统的影响以免无意中使编译出来的东西依赖于主系统。
编译两次的方法也有,那叫CLFS-SYSROOT。请参考楼主的专门文章。
作者: 地球发动机 发布时间: 2008-03-23
作者: 地球发动机
简单的答案是:是的,这样的编译法要是脱离了主系统,很可能就不能运行了。
内核:应该可以运行,但是它是用宿主机的编译器编译的,而不是LFS中的版本,可能优化得就没那么好了。 readline vim等等,如果用静态方式编译,应该也可以运行。不过正如前述,它不是用LFS中的版本。要是动态编译,一般来说没戏。因为它会依赖于宿主机的动态库,一旦找不到了或者版本错误,就会拒绝运行。 另外,你还是没有看懂LFS的步骤。一套纯净的系统意味着:所有的代码都是用系统中安装的GCC版本编译出来的,所依赖的库全部都在系统内部。 这意味着,一个纯净的系统至少要编译两次工具链:首先用宿主系统中的版本来编译目标版本的工具链,然后再用目标版本的工具链来编译系统。至于LFS的三次编译,目的是为了尽量隔离主系统的影响以免无意中使编译出来的东西依赖于主系统。 编译两次的方法也有,那叫CLFS-SYSROOT。请参考楼主的专门文章。 |
作者: zyl19861126 发布时间: 2008-03-23
佩服啊!!!
其实各个distribution的朋友都可以来看看lfs,对linux分析的很深入啊。很漂亮,虽然我很菜。
顶。
作者: nblinux 发布时间: 2008-04-22
作者: love简单 发布时间: 2008-05-03
作者: rollin7 发布时间: 2008-07-10
作者: djhoo 发布时间: 2008-07-27
作者: lionking_twx 发布时间: 2008-10-14
好帖! 不顶不行!
作者: wangjiren 发布时间: 2008-10-23
作者: pengyu80s 发布时间: 2008-10-23
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28