我知道 Linux 是可用的,并且已被移植到许多不同的平台,例如 X86、ARM、PowerPC 等。
然而,就移植而言,到底需要什么?
我的理解是,Linux是用C语言编写的软件。因此,当将Linux最初从X86移植到ARM或其他平台时,不就是用针对特定目标架构的编译器重新编译代码的问题吗?
抛开不同外设的设备驱动程序不谈,将 Linux 移植到新架构时还需要做什么。难道编译器没有为我们处理好一切吗?
答案1
尽管 Linux 内核中的大部分代码都是用 C 语言编写的,但该代码的许多部分仍然与其运行的平台非常相关,需要考虑到这一点。
一个特殊的例子是虚拟内存,它在大多数体系结构(页表的层次结构)上以类似的方式工作,但每个体系结构都有特定的细节(例如每个体系结构中的级别数,甚至在 x86 上,这种情况也在增加) Linux 内核代码引入了宏来处理遍历这些层次结构,在页表级别较少的体系结构上,编译器可以忽略这些宏(因此代码是用 C 编写的,但将体系结构的详细信息纳入其中)考虑。)
许多其他领域对于每种架构来说都是非常特定的,需要使用特定于架构的代码来处理。不过,其中大多数都涉及汇编语言代码。例子有:
上下文切换:上下文切换涉及保存被切换出的进程的所有寄存器的值以及从调度到CPU的进程的保存集中恢复寄存器。甚至寄存器的数量和集合对于每种架构来说都是非常特定的。该代码通常在汇编中实现,以允许完全访问寄存器并确保其尽可能快地运行,因为上下文切换的性能对系统至关重要。
系统调用:用户空间代码触发系统调用的机制通常特定于体系结构(有时甚至特定于特定的 CPU 型号,例如 Intel 和 AMD 为此引入了不同的指令,较旧的 CPU 可能缺少这些指令,因此这些指令的详细信息仍将是独一无二的。)
中断处理程序:如何处理中断(硬件中断)的详细信息通常是特定于平台的,并且通常需要一些汇编级粘合来处理平台使用的特定调用约定。此外,用于启用/禁用中断的原语通常是特定于平台的,并且也需要汇编代码。
初始化:有关初始化如何发生的详细信息通常还包括特定于平台的详细信息,并且通常需要一些汇编代码来处理内核的入口点。在具有多个 CPU (SMP) 的平台上,有关如何使其他 CPU 联机的详细信息通常也是特定于平台的。
锁定原语:锁定原语(例如自旋锁)的实现通常也涉及特定于平台的细节,因为某些体系结构提供(或更喜欢)不同的 CPU 指令来有效地实现这些指令。有些将实现原子操作,有些将提供可以原子测试/更新的 cmpxchg(但如果另一个编写器首先进入则失败),其他将包含对 CPU 指令的“锁定”修饰符。这些通常也涉及编写汇编代码。
内核中(或者具体来说,Linux 内核)中可能还需要特定于平台或体系结构的代码。查看内核源代码树,在下面arch/
和下面有特定于体系结构的子树include/arch/
,您可以在其中找到更多信息这方面的例子。
有些实际上是令人惊讶的,例如您会发现每个体系结构上可用的系统调用数量是不同的,并且某些系统调用将存在于某些体系结构中而不是其他体系结构中。 (即使在 x86 上,32 位和 64 位内核之间的系统调用列表也有所不同。)
简而言之,内核需要了解很多特定于平台的情况。 Linux 内核试图抽象其中的大部分,因此更高级的算法(例如内存管理和调度的工作原理)可以用 C 语言实现,并且在所有体系结构上以相同(或大部分相同)的方式工作。
答案2
除了移植 Linux 内核之外,您还需要定义应用程序二进制接口(ABI) 用于“用户空间”程序和端口用户空间软件堆栈的最低层。 Linux 通常与 GNU 项目中的低级用户空间组件一起使用,其中最关键的是:
- C 编译器、汇编器和链接器:海湾合作委员会和GNU Binutils。对于全新的CPU架构,您需要在开始移植内核之前移植该软件,因为内核本身是一个C程序,必须进行编译。如果您的平台的 CPU 已经有了“后端”支持,只是不使用 Linux 作为操作系统内核,那么您要做的工作就会少得多,并且您可能可以推迟大部分工作,直到内核启动为止。跑步。
- C 运行时库:“GNU 库“。这个库包含的代码使系统调用以及以其他方式直接与内核交互。
- “外部函数接口”库,库菲,它是许多高级语言解释器的重要组成部分,并执行为数不多的剩余任务之一需要少量手写汇编语言。
许多其他软件都有可选的依赖于平台的组件;例如,如果您为以下内容编写手动优化的加密原语,则 Web 浏览速度将大大加快国家安全局和开放式SSL适用于您的新 CPU 架构,以及即时编译后端离子猴和V8。但这些并不是建立新平台所必需的。
答案3
您必须告诉内核您要移植到的硬件。内核的工作是直接与硬件交互,因此为了使其正常工作,内核需要了解CPU、振荡器(时钟)和任何外设,例如各种串行端口(SPI、CAN、 I2C等)。
在过去,您可以通过编写特定于平台的代码来实现此目的,然后驱动程序将使用这些代码来运行。如今,这是通过编写一个来完成的设备树定义。