手工定制Linux系统

引子

陆续研究LFS(Linux from Scratch)有一段时了,发现一些问题,比如无论是官网教程,还是一些第三方的分析文章,都有一个共同点,描述过于的形而下,也就是太过于注重细节。过于实际和过于理论有同一样的缺点——不利提高学习效率[注]。另外,【形而下】风格的教程给人不通透的感觉,例如你是把东西做出来了,可是你很可能有几步是不解的,只是盲目照做。

到目前为止,我看官方教程没有提供足够的背景信息,因而它不是面向一般Linux普通用户,包括Linux下的应用开发者,它面向系统开发的程序员。如果定制Linux有客观真实的价值,并且定制OS是一项艰巨的任务,那么像这样的第三方补充信息还需要很多,本文只是以小结一下学习经验,对LFS教程补充一些【形而上】方面的信息,弥补一下学习市场对【自上而下】方式的缺失。

注:由于效率是功效与时间之比,功效本身很难准确度量,效率也变得无法捉摸,任意猜测。

自上而下与自下而上

学习和研究一个新事物到底以自下而上的顺序学习效率高,还是反之,自上而下的效率高?所谓【自上而下】是指是先学习抽象的概括性的内容,而【自下而上】是指先学习具体的细节性的内容。我个人一向使用自上而下的方式;不知道是否因为文化的原因,国人独爱具体的东西,国内很多教程和文章都是自下而上的方式讲述。自下而上有比较明显的优点和【不那么明显】的缺点,优点是可操作性强,容易上手;缺点是学习的【综合效率】不如自上而下。因为具体的东西一般在应用于一个特殊的场境里,有很多特殊的东西,而在【同样描述水平】的前提下,相对于具体的细节,概括性的内容【离我们更近】,由认知规律可知,概括性的内容更易于我们的大脑同化吸收它。

定制自己的Linux?

定制自己的Linux?这个目标听着都让熟悉Linux的人兴趣盎然,甚至兴奋异常。可是实际上又如何呢?真的每个人都有能力去定制一个OS么?如果只有少部分黑客和计算机爱好者有这个能力,OS对其他大部分人来还是有如洪水猛兽般的复杂,这个目标是不是空想呢?我认为如是。LFS的用意的很友好,目的也很正确,意义也很有意义,只是方式有点不符合实际;用教程方式引导一名Linux普通用户定制自己的Linux系统本身近似一种空想。因为难度太大,学习曲线太陡。我觉得,之所以有人觉得定制Linux是有可能是因为他们都“玩过”Linux,如果换为定制CPU呢,这就不认为有可能性和必要性了。CPU与OS除在实体形式的差别,它们的复杂度一定是OS低于CPU么?我不以为然。

为什么需要定制?

从学习的角度看,是为了透切理解Linux系统(内部结构)和训练对Linux系统全方位的掌控的能力。包括从源码构建系统模块、掌控系统的体积和安全性。

从生产的角度看,有各种各样的理由有自定义的Linux系统的需求,比如系统性能,比如嵌入式系统等。

定制什么?

LFS的作者说是因为想定制一下启动脚本的样式而发起这个项目的,显然这种定制任务太小case了,并且不实用。实用的定制当然要数为嵌入式系统定制了。由于定制的目标各异,定制什么也因情况而不同。不过有一个默认的假设,就是定制的系统是完整的系统,可用的系统。

那么一个完整的可用的LINUX系统除了内核外还有一些什么必需的工具(软件包)呢?

一个完整的可用的系统并不仅仅意味着一个内核,可能还需要有引导程序、登陆程序、汇编程序、编译程序、脚本解释程序、调试程序、文本编辑程序、邮件程序等。如果对【可用的系统】的定义是交叉的,那么需要的软件包也是有多种的可能组合。比如,桌面应用的发行版既可用作WEB服务器,亦可用作开发工作站,前者不需编译程序,后者需要。显然“完整的可用的LINUX系统”的完整性取决于你的【系统的目标用途】,也就是它是用于安装嵌入式设备、用作 WEB服务器、数据库服务器,还是用作桌面应用等。

系统定制

问题又来了,有没有【所有应用】都需要的【非内核】的通用软件包呢?答案是有的,一些软件包的确是可用的系统所必须的,像bootloader和glibc;但有一些软件包是因系统用途不同而可选的。系统应用可大致分为以三类:

  • 程序开发(需要编译调试程序、C静态代码库)
  • 系统管理及维护(包括软硬件安装、用户管理等)
  • 应用计算(包括网络应用和桌面应用)

另外,同一种应用系统,对于不同用户所需的软件包也不是同的,各种系统以及它们所需的软件包可参考以下表格:

package安全网关WEB服务器开发工作站桌面应用
web用户系统用户开发用户系统用户桌面用户系统用户
bootloader (GRUB)
kernel
sysvinit
util-linux-ng(agetty)
……
bash
glibc
Coreutils
Linux API Headers
Binutils
GCC
M4
Autoconf
Automake
Make
Tar
Gzip
Module-Init-Tools
Udev
Inetutils
Man-pages
Man-DB
Squid
netfilter
apache
mysql
php
X11
Vim
图注:核心程序开发软件安装 系统管理系统维护 系统管理应用计算

软件包定制

以上是系统类型的定制,是一种粗粒度的定制;还有细粒度的定制——对各【软件包】按需定制,也就是软件包本身功能的定制,例如LFS作者对内核启动脚本样式的定制就是对内核的定制。

可能源于项目的实践性的目标的原因,原LFS教程最缺乏的一点,就是没有详细解释各软件包、代码库、数据文件和硬件之间的依存关系。本文尝试以以下的几点阐述一下各软件包的功能角色以及依存关系:

  1. 功能目的:
  2. 依赖:软硬体系的相关性,还有对本软件包的运作环境的一些假定
  3. 支持:对上一层软件包提供服务,构建其依赖环境,例如传递【运行时组态置信息】;
  4. 配置:软件包自身功能组态——【构建时组态信息】
  5. 安装位置:大部分程序安装在文件系统内,引导程序安装到引导扇区

GRUB

  • 功能目的: 接替固件(如BIOS)对系统进一步的初始化,并且可能的用户交互,载入指定的内核镜像。
  • 依赖:Bootloader是硬件高度相关的软件包,在裸处理器上运行。GRUB是X86处理器体系相关的,并且可能使用BIOS提供的原始驱动来访问基本设备,比如访问IDE硬盘。LFS默认假定在运行在X86机器上通用发行版上构建一个专用的X86Linux系统,所以选用常用的GRUB。
  • 支持:为kernel执行创建环境,另外根据与用户交互或先前配置(/boot/grub/menu.lst)传递内核所需的一些执行参数(动态配置参数),如根文件系统所在设备、init等
  • 配置:
    • 编译时静态配置
    • 运行时动态配置:/boot/grub/menu.lst
  • 安装位置:引导扇区,其它【辅助程序】安装到/boot/grub

Linux Kernel

  • 功能目的: 接替Bootloader对系统进更高级的系统初始化,构建虚拟机,开启多进程、虚拟内存等,最后执行动态配置项init指定程序,或者顺序执行默认init程序:/bin/init | /sbin/init | /usr/bin/init | /bin/sh。
  • 依赖:kernel有体系相关的初始化部分,因而对X86处理器体系有依赖。另外,kernel依赖Bootloader提供一些动态配置参数。
  • 支持: 为多用户多进程执行创建环境,并且负责执行第一支用户进程init
  • 配置
    • 编译时静态配置:-arch=X86
    • 运行时动态配置:只有命令行,如root | console | init ……更多详细请查阅【kernel文档】——kernel-parameters.txt
  • 安装位置:/boot

Sysvinit

  • 功能目的:init程序是kernel引导的约定出口,并且从名字可以猜到,init仍然在对系统进行初始化,不过这里被初始化的系统更逻辑更高级,对init有一定了解的程序员都知道运行级别概念——runlevel,一种运行级别代表一种系统,有各自的配置状态,需要初始化,运行级别3时,执行/etc/rc.d/rc.sysinit 和/etc/rc.d/rc 3脚本程序,更多参考这里(http://www.comptechdoc.org/os/linux/startupman/linux_suinit.html)。此外,init是系统真正的撑控者,因为init被执行后负责控制整个系统的运行,例如用户登入登出,死进程的清理和关机等,而内核只负责处理系统调用(system calls),用户程序的进程是通过系统调用【被执行的】。 init虽然是运行在用户空间的程序,但它的角色不一般。第一,它不会被用户kill掉;第二,它是所有用户进程的祖先。
  • 依赖:在kernel的初始化后半部分,引导代码已经不对硬件体系有任何依赖;sysinit当然不对硬件体系有任何依赖。不过作为一支用户程序,它会对系统的C库有一定的依赖,称为对软件体系的依赖。
  • 支持: 执行getty程序并且提供getty命令行参数,为让用户在默认终端上登陆作准备
  • 配置:
    • 运行时动态配置:/etc/inittab ……更多详细请查阅【Linux System Administrator's Manual INITTAB(5)】
  • 安装位置:/sbin
  • 关键组件:init, halt, poweroff, reboot, runlevel, shutdown, sulogin, telinit

util-linux-ng(agetty)

  • 功能目的:getty  opens  a  tty  port,  prompts  for  a login name and invokes the /bin/login command. It is normally invoked by init(8)。
  • 依赖:对系统的C库有一定的依赖
  • 支持:
  • 配置:
    • 运行时动态配置:
  • 安装位置:/sbin
  • 关键组件:/etc/issue, printed before the login prompt.

Bash

This package satisfies an LSB core requirement to provide a Bourne Shell interface to the system. It was chosen over other shell packages because of its common usage and extensive capabilities beyond basic shell functions.

Glibc

This package contains the main C library. Linux programs would not run without it.

Coreutils

This package contains a number of essential programs for viewing and manipulating files and directories.

Gcc

This package is the Gnu Compiler Collection. It contains the C and C++ compilers as well as several others not built by LFS.

Make

This package contains a program for directing the building of packages. It is required by almost every package in LFS.

inux-libc-headers

linux-libc-headers is a set of Linux kernel headers for building libc. Since Linux 2.6, the kernel authors have frowned upon userspace programs including the kernel headers directly. Instead, the recommendation was to use sanitized kernel headers for userspace programs. The Linux Libc Headers project provides such a set of sanitized headers. It was initiated as a PLD Linux specific project.

怎么定制?

定制过程的定义取决于定制的起点和定制的终点。这里的定制起点是一个可用的Linux开发工作站,可是安装好的发行版,也可以Live-CD;终点是一个也是可用的,但完全自定的Linux开发工作站。

定制的基本方法是在可用的Linux开发工作站上构建一个自定义的Linux系统。The LFS system will be built by using an already installed Linux distribution (such as Debian, Mandriva, Red Hat, orSUSE).

大概过程:

  1. 创建一个新的本地分区,用以编译和安装新LFS系统;create a new Linux native partition and file system. This is the place where the new LFS  system will be  compiled  and  installed.
  2. 下载所需的软件包以及其补丁到一个特定的地方;packages  and patches need  to be downloaded to build an LFS system and how to store them on the new file system.
  3. 组建适当的工作环境setup an appropriate working environment.
  4. 构建一个临时的独立于开发机器的编译工具链,installation of a number of packages that will form the basic development suite (or toolchain) which  is  used  to  build  the  actual  system
    1. build a first pass of the toolchain, including Binutils and GCC (first pass basically means  these  two  core packages will be  reinstalled).
    2. The next  step  is  to build Glibc,  the C  library. Glibc will be compiled by the toolchain programs built in the first pass.
    3. Then, a second pass of the toolchain will be built. This time, the toolchain will be dynamically linked against the newly built Glibc.
    4. The remaining Chapter 5 packages are built using this second pass toolchain. When this is done, the LFS installation process will no longer depend on the host distribution, with the exception of the running kernel.
  5. 利用新的开发工具链编译构建LSF的其它软件包
    • build a first pass of the toolchain, including Binutils and GCC (first pass basically means  these  two  core packages will be  reinstalled). The next  step  is  to build Glibc,  the C  library. Glibc will be compiled by the toolchain programs built in the first pass. Then, a second pass of the toolchain will be built. This time, the toolchain will be dynamically linked against the newly built Glibc. The remaining Chapter 5 packages are built using this second pass toolchain. When this is done, the LFS installation process will no longer depend on the host distribution, with the exception of the running kernel.
  6. full LFS system is built.
    • chroot (change root) program is used to enter a virtual environment and start a new shell whose root directory will be set to the LFS partition.
  7. set up the LFS-Bootscripts
  8. the kernel and boot loader are set up
裸男
Nakeman.cn 2023 Build by Gatsby and Tailwind, Deploy on Netlify.