24

程序从某种语言编译为 ASM --> 机器代码(直接可执行)。当人们说这取决于平台时,意味着形成的二进制文件将(正确)仅在具有相同指令集架构(如 x86、x86-64)的 CPU 上运行。由于 ISA 的不同,它可能(错误地)/可能(根本不)在其他进程上运行。正确的?

现在,二进制文件的概念让我感到困惑。一切都与“机器语言代码”和“CPU”有关。操作系统在哪里发挥作用?我的意思是编译后的二进制文件在加载到内存时有直接的 CPU 指令。& CPU 一次执行一条指令。除了进程管理链接文本之外,我在任何地方都看不到操作系统的作用。无论操作系统如何,它都应该在相同 ISA 的 CPU 上运行。正确的?

然而事实并非如此。如果我在 Windows 机器上构建 x86 的代码。它不会在 Mac x86 机器或 Linux x86 机器上运行。

我在这里遗漏了一些东西。请清除我的困惑。

4

9 回答 9

19

对于初学者来说,现代 CPU 有(至少)两种模式,一种是运行操作系统本身的核心模式(“内核模式”),另一种是运行程序的模式(“用户模式”)。在用户模式下,CPU 不能做很多事情。

例如,在内核中通常会注意到鼠标单击,而不是用户模式。但是,操作系统将事件分派到用户模式并从那里分派到正确的程序。反过来也需要配合:一个程序不能自由地画到屏幕上,需要经过操作系统和内核模式才能画出自己的部分。

同样,启动程序的行为通常是一种合作。操作系统的外壳部分也是一个用户模式程序。它获取您的鼠标单击,并确定它是旨在启动进程的鼠标单击。然后外壳程序告诉操作系统的内核模式部分为该程序启动一个新进程。

当内核态需要启动一个新进程时,它首先分配内存用于记账,然后继续加载程序。这涉及从二进制文件中检索指令,还包括将程序连接到操作系统。这通常需要找到二进制文件的入口点(通常是int main(int argc, char** argv)),以及程序要调用操作系统的所有点。

不同的操作系统使用不同的方式将程序与操作系统挂钩。因此,加载过程不同,二进制文件的文件格式也可能不同。这不是绝对的;二进制文件的 ELF 格式用于许多操作系统,Microsoft 在其所有当前操作系统上使用其 PE 格式。在这两种情况下,格式确实描述了二进制文件的精确格式,因此操作系统可以决定程序是否可以连接到操作系统。例如,如果它是 Win32 二进制文件,它将是 PE 格式,因此 Linux 不会加载它,Windows 2000 会加载,Windows 7-64 也会加载。另一方面,Win64 二进制文件也是 PE 格式,但 Windows 2000 会拒绝它。

于 2009-10-21T08:48:15.113 回答
13

它不会在其他处理器上运行,因为 01010110011 在 x86 上意味着某些东西,而在 ARM 上意味着其他东西。x86-64 恰好向后兼容 x86,因此它可以运行 x86 程序。

二进制文件采用您的操作系统可以理解的特定格式(windows = PE,mac/linux = ELF)

对于任何普通的二进制文件,您的操作系统会将其加载到内存中并使用某些值填充许多字段。这些“某些值”是存在于共享库(dll、so)(如 kernel32 或 libc)中的 api 函数的地址。需要 API 地址,因为二进制文件本身不知道如何访问硬盘驱动器、网卡、游戏手柄等。程序使用这些地址来调用存在于您的操作系统或其他库中的某些功能。

从本质上讲,二进制文件缺少一些需要由操作系统填充以使一切正常运行的重要部分。如果操作系统填写了错误的部分,则二进制文件将无法工作,因为它们无法相互通信。如果您将 user32.dll 替换为另一个文件,或者您尝试在 mac osx 上运行 linux 可执行文件,就会发生这种情况。

那么 libc 是如何知道如何打开文件的呢?

libc 使用系统调用,这是对操作系统核心功能的低级访问。它有点像函数调用,除了你通过填充某些 CPU 寄存器然后触发中断(特殊 CPU 指令)来完成它

那么操作系统如何知道如何打开文件呢?

这是操作系统所做的事情之一。但是它怎么知道如何与硬盘通信呢?我不确切知道这些东西是如何工作的,但我想操作系统是通过写入/读取恰好映射到 BIOS 功能的某些内存位置来做到这一点的。

那么 BIOS 是如何知道如何与硬盘通信的呢?

我也不知道,我从来没有做过那个级别的编程。我想 BIOS 是硬连线到硬盘驱动器连接器的,并且能够发送正确的 1 和 0 序列来与硬盘驱动器对话“SATA”。大概只能说“读这个扇区”之类的简单的话

那么硬盘是如何知道如何读取扇区的呢?

我真的不知道这一点,所以我会让一些硬件人员继续。

于 2009-10-21T08:48:43.770 回答
8

两种方式:

首先也是最重要的答案是“系统调用”。每当您调用需要执行任何 I/O、与设备交互、分配内存、派生进程等的函数时,该函数都需要执行“系统调用”。虽然 syscall 指令本身是 X86 的一部分,但可用的系统调用和它们的参数是特定于操作系统的。

即使您的程序没有进行任何系统调用(我不确定这是否可能,而且肯定不会很有用),围绕机器代码的格式对于不同的操作系统也是不同的。所以exe(PE)和linux可执行文件(通常是ELF)的文件格式是不同的,这就是为什么exe文件不会在Linux上执行的原因。

编辑:这些是低级细节。更高级别的答案是说任何需要访问文件、控制台/GUI、分配内存等的东西都是特定于操作系统的。

于 2009-10-21T08:31:07.237 回答
3

当您尝试访问它在硬件级别为您抽象出来的“服务”时,操作系统开始发挥作用,例如在称为文件系统的“数据库”中打开一个文件,生成一个随机数(每个现代操作系统都有这个功能)。

例如,在 GNU/Linux 下,您必须填写寄存器并调用 int 80h 才能访问“服务”(实际上称为“系统调用”)。

您的程序也不会在另一个操作系统上运行,因为可执行文件有不同的文件格式,例如 Win 有 COFF/PE,Linux 有 ELF 文件格式(就像任何其他文件格式一样,这也包含“元数据”,例如HTML(或 SGML)文件格式)。

于 2009-10-21T08:31:51.540 回答
2

操作系统提供 (a) 机器代码运行的环境,以及 (b) 标准服务。如果没有 (a),您的代码将永远无法执行,如果没有 (b),您将必须自己实现所有内容并直接使用硬件。

于 2009-10-21T08:32:08.420 回答
1

由高级语言生成的机器指令将适用于提供您进行的调用的库的调用约定,包括任何系统调用(尽管这些通常包装在某处的用户空间库中,因此有关如何进行系统调用的细节可能没有必要)。

此外,它适用于目标指令集架构,但有一些例外(例如,必须注意关于指针大小、原始类型、结构布局、C++ 中的类实现等的假设)。

文件格式将规定必要的挂钩/公开可见的功能和数据,以使操作系统能够将您的代码作为进程执行,并将进程引导到所需的状态。如果您熟悉 Windows 下的 C/C++ 开发,子系统的概念决定了引导级别、提供的资源和入口点签名(通常main(int, char **)在大多数系统上)。

有一些很好的例子说明了高级语言、指令集架构和可执行文件格式的选择如何影响在任何给定系统上运行二进制文件的能力:

汇编语言必须针对特定的 ISA 进行编码。它们使用特定于 CPU 类型系列的指令。如果这些 CPU 支持给定的指令集,这些指令可能适用于其他 CPU系列。例如,x86 代码在一定程度上可以在 amd64 操作系统上工作,并且肯定可以在运行 x86 操作系统的 amd64 CPU 上工作。

C 抽象了 ISA 的许多细节。一些明显的例外包括指针大小和字节序。各种众所周知的接口,将通过 libc 提供到预期的级别,例如printfmainfopen等。这些包括预期的寄存器和堆栈状态,以便进行这些调用,使 C 代码能够在不同的操作系统和架构上工作而无需更改。可以直接提供其他接口,也可以通过将特定平台包装到预期的接口中来提供其他接口,以增加 C 代码的可移植性。

Python 和其他类似的“虚拟化”语言在另一个抽象级别上运行,同样也有一些例外,例如特定平台上不存在的功能或字符编码差异,可以在许多系统上运行而无需修改。这是通过为许多不同的 ISA 和操作系统组合提供统一的接口来实现的,但代价是性能和可执行文件大小。

于 2009-10-21T09:47:51.767 回答
0

操作系统提供用于访问某些功能和硬件的工具和 API。

例如,要在 Microsoft Windows 上创建一个窗口,您需要操作系统的 DLL 来创建该窗口。

除非您希望自己编写 API,否则您将使用操作系统提供的 API。这就是操作系统发挥作用的地方。

于 2009-10-21T08:30:22.497 回答
0

另外我想添加操作系统处理程序的启动。它准备进程空间并对其进行初始化,以便程序可以开始,加载程序指令并将控制权交给程序。

于 2009-10-21T08:56:31.083 回答
0

一个比喻:

假设您从另一个国家聘请管家。他听不懂你说的一个词,所以你得到了一个类似星际迷航的翻译设备。现在他可以理解你的高级语言,因为当你说话时,他会听到他自己的(相当粗略的)语言。

现在假设你想让他从 A 走到 B。你不会直接对他的腿或脚说话,而是当着他的面问他!他可以控制自己的身体。如果 1) 你正确地传达了你的要求,并且 2) 他认为这属于他的工作职责,他将从 A 转到 B。

现在你得到了一个新仆人,和上一个来自同一个国家(因为你不想买一个新的星际迷航翻译器)。你也希望他从 A 走到 B。但本仆要求你说话的声音大一点,问的时候说请。你忍了,因为他比较灵活:如果你愿意,你可以要求他从 A 经 C 到 B——以前的管家可以这样做,但拖着脚抱怨。

另一个幸运的突破是您可以调整您的翻译设置来处理这个问题,因此,从您的语言角度来看,没有任何变化。但是如果你用新的设置和老管家说话,他会很困惑,即使你说他的语言也听不懂。

如果不清楚,管家是具有相同 ISA 但操作系统不同的计算机。翻译器是您针对其 ISA 的交叉编译器工具链。

于 2015-03-28T20:45:52.067 回答