【问题标题】:How can the Linux kernel compile itself?Linux内核如何编译自己?
【发布时间】:2009-01-30 03:22:54
【问题描述】:

我安装的时候不太明白Linux内核的编译过程 我机器上的 Linux 系统。

以下是一些让我感到困惑的事情:

  1. 内核是用 C 语言编写的,但是如何在没有安装编译器的情况下编译内核?
  2. 如果在编译内核之前在我的机器上安装了 C 编译器,如何在没有安装编译器的情况下编译编译器本身?

这两天我很困惑,谢谢你的回复。

【问题讨论】:

  • 据了解,C 编译器是由 AT&T 实验室的一些疯狂而聪明的人用某种汇编程序编写的,用于为给定的计算机编译 UNIX(请注意,历史始于 UNIX,而不是linux,所以我担心你会错过一章......或其中几个!)。简短的故事是,只要这些计算机具有适当的 C 语言编译器,就不需要为不同的计算机重写 unix 内核。这些编译器是在目标计算机的特定汇编器中编写的。粗鲁的说法“首先用给定计算机的 assambler 编写编译器,然后用 C 编写 UNIX”

标签: c linux compiler-construction


【解决方案1】:

您的 Linux 机器的第一轮二进制文件是在其他一些 Linux 机器上构建的(可能)。

第一个 Linux 系统的二进制文件是在某个其他平台上构建的。

该计算机的二进制文件可以将其根追溯到构建在另一个另一个平台上的原始系统。

...

再往前走,你会发现编译器是用更原始的工具构建的,而这些工具又是在它们的主机之外的机器上构建的。

...

继续往下推,你会发现计算机内置了可以通过设置机器前面板上的开关来输入指令。

非常酷的东西。

规则是“构建工具来构建工具来构建工具......”。非常类似于运行我们物理环境的工具。也称为“自力更生”。

【讨论】:

  • 不一定不干净。只是未优化。第一个编译器将针对 386 进行优化,但重新编译的版本针对您拥有的任何架构进行了优化。
  • 你可以添加第三阶段,如果一切正常,第二阶段的输出应该等于第三阶段的输出。
  • 这不仅仅是软件,而是硬件。没有计算机就不可能制造出像 P4(甚至是 486)这样的东西。
  • @BCS:哦,是的。我们已经达到了这样一个点,即软件和硬件工具是紧密相连和相互依存的。
  • “一个有效的复杂系统总是被发现是从一个有效的简单系统演变而来的。” en.wikipedia.org/wiki/Gall's_law
【解决方案2】:

我认为你应该区分:

compile,v:使用编译器处理源代码并生成可执行代码[1]

安装,v:连接、设置或准备使用[2]

编译从源代码生成二进制可执行文件。安装只是将这些二进制可执行文件放在正确的位置以便以后运行它们。因此,如果二进制文件可用,则安装和使用不需要编译。相应地考虑“编译”和“安装”,就像“烹饪”和“服务”一样。

现在,你的问题:

  1. 内核是用 C 编写的,但是如何在没有安装编译器的情况下编译内核?

没有编译器就无法编译内核,但可以从已编译的二进制文件中安装

通常,当您安装操作系统时,您会安装预编译的内核(二进制可执行文件)。是别人编的。只有当你想自己编译内核时,你才需要源代码和编译器,以及所有其他工具。

即使在像 gentoo 这样的“基于源代码”的发行版中,您也是从运行已编译的二进制文件开始的。

因此,您可以终生不编译内核,因为它们是由其他人编译的。

  1. 如果在编译内核之前在我的机器上安装了 C 编译器,如何在没有安装编译器的情况下编译编译器本身?

如果没有内核 (OS),则无法运行编译器。所以必须安装一个编译过的内核来运行编译器,但不需要自己编译内核。

同样,最常见的做法是安装编译器的已编译二进制文件,并使用它们编译其他任何东西(包括编译器本身和内核)。

现在,鸡和蛋的问题。第一个二进制文件是由其他人编译的...请参阅 dmckee 的出色回答。

【讨论】:

    【解决方案3】:

    描述这种现象的术语是bootstrapping,这是一个值得阅读的有趣概念。如果您考虑嵌入式开发,就会发现很多需要软件的设备(例如闹钟、微波炉、遥控器)的功能不足以编译它们自己的软件。事实上,这类设备通常没有足够的资源来远程运行像编译器这样复杂的东西。

    他们的软件是在台式机上开发的,然后在编译后复制。

    如果您对这类事情感兴趣,我脑海中浮现的一篇文章是:Reflections on Trusting Trust (pdf),这是一篇经典且有趣的文章。

    【讨论】:

    • 您将交叉编译与引导程序混淆了。第一个涉及仅存在于 PC 上的编译器,它为目标架构创建操作码。显然,如果没有另一台计算机,您将无法做到这一点,因此存在先有鸡还是先有蛋的困境。解决这个难题的方法是自举,使用手写或预先存在的简单编译器生成更复杂的编译器。
    【解决方案4】:

    内核不会自行编译——它是由用户空间中的 C 编译器编译的。在大多数 CPU 体系结构中,CPU 在特殊寄存器中有许多位,这些位表示当前运行的代码所具有的权限。在 x86 中,这些是 代码段 (CS) 寄存器中的 当前特权级别 位 (CPL)。如果 CPL 位为 00,则称代码在 security ring 0 中运行,也称为 内核模式。如果 CPL 位为 11,则称代码运行在 security ring 3,也称为 user mode。其他两个组合,01和10(分别是安全环1和2)很少使用。

    在用户模式和内核模式下,关于代码能做什么和不能做什么的规则相当复杂,但可以说,用户模式的权限已严重降低。

    现在,当人们谈论操作系统的内核时,他们指的是操作系统代码的一部分,这些代码可以在具有提升权限的内核模式下运行。一般来说,内核作者出于安全原因会尽量保持内核小,这样不需要额外权限的代码就不会拥有它们。

    C 编译器就是这样一个程序的一个例子——它不需要内核模式提供的额外权限,因此它像大多数其他程序一样在用户模式下运行。

    在 Linux 的情况下,内核由两部分组成:内核源代码和内核编译后的可执行文件。任何带有 C 编译器的机器都可以将内核从源代码编译成二进制映像。那么,问题是如何处理该二进制图像。

    在新系统上安装 Linux 时,您安装的是预编译的二进制映像,通常来自物理介质(如 CD DVD)或网络。 BIOS 将从媒体或网络加载内核的引导加载程序(二进制映像),然后引导加载程序将内核的(二进制映像)安装到您的硬盘上。然后,当您重新启动时,BIOS 从您的硬盘加载内核的引导加载程序,引导加载程序将内核加载到内存中,然后您就可以关闭并运行了。

    如果你想重新编译你自己的内核,这有点棘手,但可以做到。

    【讨论】:

      【解决方案5】:

      第一个出现的是谁?鸡还是蛋?

      鸡蛋从恐龙时代就已经存在了。

      ..有些人混淆了一切,说鸡实际上是野兽的后代.. 长话短说:技术(蛋)在当前产品(鸡)之前就已经存在

      你需要一个内核来构建一个内核,也就是说,你需要一个内核来构建另一个内核。

      第一个内核可以是你想要的任何东西(最好是可以创建你想要的最终产品的合理的东西^__^)

      This tutorial from Bran's Kernel Development 教您开发和构建一个小型内核,然后您可以使用您选择的虚拟机对其进行测试。

      意思:你在某个地方编写和编译内核,然后在空的(无操作系统)虚拟机上读取它。

      这些 Linux 安装会发生什么遵循相同的想法,但增加了复杂性。

      【讨论】:

        【解决方案6】:

        这不是乌龟一路下来。就像您说的那样,您无法在运行该操作系统的系统上编译以前从未编译过的操作系统。类似地,至少一个编译器的第一次构建必须在另一个编译器上完成(通常还有一些后续构建,如果第一次构建还不能编译自己的源代码)。

        我认为最早的 Linux 内核是在 Minix 机器上编译的,尽管我对此并不确定。 GCC 当时可用。许多操作系统的早期目标之一是运行足够好的编译器来编译它们自己的源代码。更进一步,第一个编译器几乎肯定是用汇编语言编写的。第一个汇编程序是由那些不得不用原始机器代码编写的可怜人编写的。

        您可能想查看Linux From Scratch 项目。您实际上在书中构建了两个系统:一个“临时系统”构建在您自己没有构建的系统上,然后是构建在您的临时系统上的“LFS 系统”。本书目前的写作方式实际上是在另一个 Linux 机器上构建临时系统,但理论上你可以调整它以在完全不同的操作系统上构建临时系统。

        【讨论】:

          【解决方案7】:

          如果我正确理解您的问题。这些天内核并没有“自行编译”。当今大多数 Linux 发行版都通过 linux live cd 提供系统安装。内核从 CD 加载到内存中,并像安装到磁盘一样正常运行。在您的系统上启动并运行 linux 环境后,只需将必要的文件提交到您的磁盘就很容易了。

          如果您在谈论引导问题; dmckee 总结得很好。

          只是提供另一种可能性...

          【讨论】:

            猜你喜欢
            • 2021-08-03
            • 2010-12-13
            • 2017-03-09
            • 2012-02-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-03
            相关资源
            最近更新 更多