从编译代码的角度来看,为一个操作系统编译的程序与为另一个操作系统编译的程序(例如 Linux 与 Windows)有什么区别。程序不是直接在 CPU 上运行吗?还是因为程序需要引用特定于操作系统的库?
答案1
普通的编译程序确实可以在 CPU 上“直接运行”,但程序并不在真空中运行:
许多程序依赖于外部动态加载库(
DLLs
或.so
多个库)。链接它们的方式取决于编译器/链接器,每个操作系统都有不同的标准。但是,也有“静态链接”程序提供所有自己的代码。现代操作系统不会将计算机的完全控制权交给正在运行的程序。程序依靠“系统调用”进行输入/输出、访问硬件以及信号和进入睡眠状态等操作。可用的服务和接口由操作系统定义。操作系统还控制系统的哪些部分(内存、寄存器、中断)允许程序使用。
GUI 程序还必须通过图形用户环境才能在屏幕上绘制自身。但您可能已经考虑过这一点了。
出于这些原因,独立于操作系统的应用程序必须依赖某种“虚拟机”,例如 Java 运行时提供的虚拟机。至关重要的是,虚拟机为操作系统资源(输入/输出、信号等)提供了标准接口。当然,Java 或 Python 也会解释“字节码”,而不是处理英特尔指令集的怪癖;但那是另一回事了。
答案2
不同的操作系统也有不同的功能。Windows 有 I/O 完成端口,Linux 没有。FreeBSD 有 kqueue,Linux 没有。Linux 有 futexes,Windows 没有。它们做同样的事情也有不同的方法——打开文件需要传递什么参数?这些参数的顺序是什么?具体如何调用操作系统的“打开文件”功能?
答案3
一般来说,程序不兼容,因为它们的应用程序二进制接口 (ABI)。
程序不是直接在CPU上运行吗?
不!这就是操作系统的工作,防止应用程序无法“直接”在 CPU 上运行。通常,在最低级别(即 OS API 所基于的级别),应用程序与操作系统的核心。
是不是因为编译后的程序本身需要引用操作系统特定的库?
是的。许多操作系统库都是为了方便与操作系统本身交互而编写的,但也有同样多的库是为跨平台而编写的。这些库向开发人员隐藏了低级操作系统接口,并假定该操作系统的编译版本将在运行时可用(见下文)。
尽管图书馆可以书面以跨平台的方式,编译时它们不能跑步跨平台。它们仍然需要针对特定目标操作系统重新编译,再次利用操作系统的特定底层组件(内核)。
一个操作系统与另一个操作系统的编译程序之间有什么区别?
最后,可执行文件本身通常包含非常具体的二进制加载头等(例如PE可执行文件Windows 的文件格式 [.exe、.dll 等...],或极低频对于 Linux [none、.o、.so 等...])。这些还可以包括用于加载特定软件库的已编译操作系统特定二进制文件的代码。
最后,从程序员的角度来看:调用约定。编译后的代码以给定的方式(即通过寄存器或在堆栈上)以非常特定的顺序将变量传递给函数。即便如此,也需要就谁负责“清理”函数调用(调用者还是被调用者?)达成一致。尽管有几种标准且广泛使用的x86 调用约定,有些可能不受某些操作系统支持(这是 ABI 的一部分)。