Linux内核汇编与逻辑

10

我的问题可能有些奇怪,但我会尽力解释。

看着Linux内核所用的语言,我只找到了C和汇编语言,即使我读了一篇文章说“Unix的第二次迭代完全是用C写的”。

我认为那篇文章是误导性的,但当我说内核有汇编代码时,我又遇到了两个问题:

  1. 内核中有哪些汇编文件以及它们的作用是什么?
  2. 汇编是与特定架构相关的,那么Linux如何安装在多种CPU架构上呢?

如果Linux内核真的完全是用C写的,那么它怎么能得到编译所需的GCC呢?

我执行了一个完整的find / -name *.s命令,只找到了一个汇编文件(asm-offset.s),在/usr/src/linux-headers-`uname -r/路径下。

不知道这是否有助于GCC正常工作,那么如果Linux没有使用汇编语言,它如何工作,如果它确实使用了汇编语言,那么它在哪里,如何保持稳定并且不依赖于架构?

提前感谢。


3
与架构有关的文件存储在 arch 目录下,对于 x86 架构,它们的扩展名为 .S(大写)。例如,以下链接提供了在 x86 架构上实现 VDSO 的汇编源代码:http://lxr.free-electrons.com/source/arch/x86/vdso/vdso.S - amdn
1
你是否已经安装了Linux源代码?头文件中不会有太多的代码。此外,搜索*.S(大写),因为那些也是汇编文件。C代码还包含一些内联汇编和其他非标准的C语法,因此需要GCC编译器。 - Jester
@Jester 你说的资源没错。我没有安装它们。 但问题仍然存在。内核需要哪些汇编文件(我知道head.S),但它如何实现架构无关性? - daniels_pa
5个回答

12

1. 为什么要使用汇编语言?

因为有些任务只能使用汇编语言完成,并且使用汇编语言可以生成更快的代码。例如,"您可以访问处理器的不同编程模式(例如,在英特尔PC上接口启动、固件或旧代码的16位模式)"。更多原因请阅读这里

2. 使用哪些汇编文件?

来自:https://www.kernel.org/doc/Documentation/arm/README

"内核的初始入口是通过head.S进入的,它使用机器无关代码。机器是由入口处'r1'的值选择的,该值必须保持唯一性。"

来自https://www.ibm.com/developerworks/library/l-linuxboot/

"当调用bzImage(i386镜像)时,您将从./arch/i386/boot/head.S开始执行启动汇编例程(请参见图3以了解主要流程)。此例程进行了一些基本的硬件设置,并调用./arch/i386/boot/compressed/head.S中的startup_32例程。此例程设置了基本环境(堆栈等)并清除了由符号启动的块(BSS)。然后通过调用位于./arch/i386/boot/compressed/misc.c中的名为decompress_kernel的C函数将内核解压缩。将内核解压缩到内存中后,就会调用它。这是另一个startup_32函数,但此函数位于./arch/i386/kernel/head.S中。"

除了这些汇编文件外,许多Linux内核代码都使用内联汇编

3. 架构依赖性?

你说得对,这与架构相关,这就是为什么Linux内核代码被移植到不同的架构上。


谢谢您的回答!我有一个额外问题。您说:“内核是通过调用名为decompress_kernel的C函数进行解压缩的”,但是您如何使用C函数呢?您难道不需要gcc吗?我猜测gcc与汇编语言一起工作,而不是c语言(因为您怎么可能使用c编译gcc - 会出现无限循环)。 - daniels_pa
你可以在开发机上编译内核,然后将编译好的镜像(ELF)加载到目标机器上。无需在目标机器上重新编译。在目标机器上启动操作系统时,你会看到压缩的内核映像(vmlinuz),它会被解压成vmlinux。实际上,这是一个名为extract-vmlinux的脚本,可以在scripts/目录下找到并进行解压。 - brokenfoot
这是一篇关于Linux内核引导顺序的优秀文章:http://duartes.org/gustavo/blog/post/kernel-boot-process - brokenfoot
您真是个帮助别人的好人,我只能给您一个赞同的答案,感谢您再次的帮助 :) 顺便说一句,我认为这只是反向提取vmlinux,所以vmlinux被压缩了?因为我没有安装源代码,但有vmlinuz镜像(我假设是未压缩的)? - daniels_pa
哈哈,谢谢批准! - brokenfoot

8

主要使用汇编语言编写的Linux内容:

  • 引导代码:启动计算机并将其设置为可以开始执行C代码的状态(例如:在某些处理器上,您可能需要手动初始化缓存和TLB,在x86上,您必须切换到保护模式等)
  • 中断/异常/陷阱入口点/返回:您需要做非常特定于处理器的事情,例如:保存寄存器并重新启用中断,最终恢复寄存器并正确返回用户模式。有些异常可能完全在汇编中处理。
  • 指令仿真:某些CPU型号可能不支持某些指令,可能不支持非对齐数据访问,或者可能没有FPU。当获取相应的异常时,可以使用仿真选项。
  • VDSO:VDSO是内核映射到用户空间的虚拟库。它允许例如:为当前CPU选择最佳系统调用序列(在x86上,如果可用,则使用sysenter/syscall而不是int 0x80),并且实现某些系统调用而无需进行上下文切换(例如:gettimeofday())。
  • 原子操作和锁:也许在未来,某些原子操作可以使用C11支持编写。
  • 从/向用户模式复制内存:除了使用优化的复制之外,这些还检查是否越界访问。
  • 优化例程:内核具有一些例程的优化版本,例如:加密例程,memset,clear_page,csum_copy(在一次传递中对IP数据进行校验和和复制到另一个位置),...
  • 支持挂起/恢复和其他ACPI / EFI /固件事项
  • BPF JIT:较新的内核包括用于BPF表达式的JIT编译器(例如由tcpdump,secmode mode 2等使用)
  • ...

为了支持不同的架构,Linux为其支持的每个架构(有时,为了使用相同的CPU架构,某些代码的多个实现针对不同的平台进行了重新编写)编写(重新编写)汇编代码。只需查看arch/下的所有子目录即可。


4
需要汇编语言的原因有以下几点:
  1. 操作系统需要许多指令来进行运行,而这些指令在大多数处理器上没有 C 语言的等效指令。例如,在 Intel x86/64 处理器上,iret 指令可以用于从硬件或软件中断返回。这些中断对于处理硬件事件(例如按键)和旧处理器上程序的系统调用非常重要。
  2. 计算机启动时并不处于立即可执行 C 代码的状态。以 Intel 处理器为例,当执行启动例程时,处理器可能尚未处于 32 位模式(或 64 位模式),C 所需的堆栈也可能还没有准备好。某些处理器中存在的其他特性(例如分页)也需要从汇编语言中打开。
但是,Linux 内核的大部分代码都是用 C 语言编写的,并通过标准化接口与一些平台特定的 C/汇编代码进行交互。通过这种方式将部分内容分离出来,Linux 内核的大部分逻辑可以在不同平台之间共享。构建系统只需将独立于平台和依赖于平台的部分编译成特定平台的内核文件,从而为不同平台(以及内核配置)生成不同的可执行内核文件。

非常有帮助,非常感谢您参与这个问题的讨论 :) - daniels_pa

0

Linux不是Unix的第二个版本(或Unix的一般版本)。它是与Unix兼容,但Unix和Linux有着独立的历史,并且在其内核代码库方面是完全独立的。Linus Torvald的想法是编写一个开源的Unix。

一些低级别的东西,例如一些架构相关的内存管理部分,是用汇编语言完成的。旧的(但仍然可用的)x86 Linux内核API,int 0x80,是用汇编语言实现的。内核中可能还有其他地方是用汇编语言实现的,但我不知道其他的。

当您编译内核时,您选择一个目标架构。根据目标,适合该架构的正确汇编文件将包含在构建中。

您找不到任何内容的原因是因为您正在搜索头文件,而不是源文件。从kernel.org下载tar ball并搜索其中内容。


你确定 int 0x80 系统调用的实现需要汇编吗?在我看来,它可以通过 gcc 调用约定(__syscall)来完成。我可能错了,不是内核/系统专家,只是觉得很奇怪,想知道原因。 - TypeIA
我上次查看(一两年前,当我在编写爱好内核时),发现没有办法让GCC在x86上生成裸函数(无序言/结语)。这是您无法使用C函数清晰地处理中断的主要原因。您可以正确访问寄存器,但是结语中有一个ret,而不是iret。自己执行iret仍然需要您负责撤消编译器在序言中对堆栈所做的任何操作。 - Tyler
为了确认,我查了一下。int 80 在 "arch/x86/kernel/entry_32.S" 中处理。 - Tyler
这也非常有帮助:)谢谢这个小讨论,它对我有很大帮助:) - daniels_pa

0

内核中的汇编代码通常用于无法直接从C语言进行的低级硬件交互。它们就像是平台特定的基础,被内核中用C语言编写的高级部分所使用。

内核源代码树包含各种系统的汇编代码。当您为特定类型的系统(例如x86 PC)编译内核时,只有该平台适用的汇编代码会被包含在构建过程中。


感谢您抽出时间回答问题,非常感谢:)) - daniels_pa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接