最近干了这三件事。记录之。
LFS:Linux from scratch。
LFS是我的一个心结,很久之前就说要好好做一遍这个,却一直借口没有时间,其实真正做下来也就花了25个小时左右,两次机房血战就差不多能做一次了。
LFS的基本思想是借助一台已有的linux环境,从碎片开始安装linux。自己搭建工具链,自己编译内核,自己编译shell。所有的一切都是从源代码开始一点一点慢慢编译。
做LFS学到的东西比我预期少了点,init之后的启动流程还是云里雾里,估计这阶段是不会认真去读那堆启动脚本了,留个遗憾。
最大的收获就是工具链的制作原理以及常用命令都是来自哪个包。
GNU的工具链分为三个部分,binutil,Glibc,GCC。
GCC负责编译,把c语言代码编译成汇编语言。这期间会用到C库(glibc)。
Glibc主要包括C库,还有动态加载器(/lib/ld-linux.so.2),此动态加载器负责加载二进制文件到进程地址空间。
Binutil是二进制工具,处理二进制文件。包括ar,as(汇编器,把汇编文件翻译成二进制文件),ld(静态链接器),以及我最喜欢的反编译objdump。
这三者的关系如下:
Gcc编译过程需要链接Glibc。
Binutil编译过程需要链接Glibc。
Glibc是自包含的,编译过程不需要链接到Glibc,但是它需要工具链来编译自己,并受到工具链的影响,工具链是什么?工具链就是gcc+glibc+binutil(严格来讲编译glibc时不需要宿主的glibc,只要gcc和binutil就可以)。
基本上算是循环依赖= =!。
那么如何制作一个pure的工具链?LFS曾今的做法也是有缺陷的,后来被一名内核黑客提出了改进办法[1]。
现今的办法如下:
(注:gcc0表示第0代gcc,也就是宿主上原来的gcc程序,gcc1表示第一次编译出来的gcc。其他同)
gcc0 + glibc0 + binutil0 -> binutil1,gcc1 (一代gcc和binutil链接都是宿主上的glibc,所以受宿主的影响)
gcc1 + binutil1 -> glibc1 (一代glibc 只受一代gcc影响,开始隔离宿主)
gcc1 + glibc1 + binutil1 -> binutil2, gcc2 (二代工具链用的是新生成的glibc1)
gcc2,binutil2,glibc1 三者构成了临时编译环境。用这套工具链生成了一堆临时环境需要用到的各种工具,比如make,sed等等。
Gcc2 + binutil2 -> glibc2
Gcc2 + binutil2 + glibc2 -> gcc3 ,binutil3
Gcc3,binutil3,glibc2 三者构成了最终LFS的编译环境,LFS上最后运行的所有程序都是来自这三个家伙。
(吐槽一句,其实无论怎么折腾,这也是个鸡生蛋蛋生鸡的问题,只要在gcc0中做手脚,后面的gcc永远不会干净。在此向伟大的C语言之父Dennis Ritchie致敬。)
LFS意外收获:
- cp abc{,.new} = cp abc abc.new
- tar xf file.tar.** 不用指定压缩算法,tar自动探测,指定还容易指定错- -!
- W命令看当前登录用户,lastb 看最后失败登录。
[1]http://www.linuxfromscratch.org/hints/downloads/files/OLD/pure_lfs.txt