为什么软件OS是特定的?


77

我正在尝试确定为什么使用某些操作系统的编程语言生产的软件只能在它们上使用的技术细节。

我的理解是,二进制文件特定于某些处理器,这是因为它们了解特定于处理器的机器语言以及不同处理器之间的指令集不同。但是,操作系统的特异性来自何处?我曾经假设它是操作系统提供的API,但后来我在一本书中看到了以下图表: 图表

操作系统-内部和设计原则第7版-W.Sallings(Pearson,2012年)

如您所见,API未表示为操作系统的一部分。

例如,如果我使用以下代码在C中构建一个简单程序:

#include<stdio.h>

main()
{
    printf("Hello World");

}

编译此程序时,编译器是否在进行任何特定于OS的操作?


15
您是否打印到窗口?或控制台?或图形内存?您如何将数据放在那里?查看Apple的printf会与Mac OS 7安静地不同,而与Mac OS X完全不同(只是坚持使用一“行”计算机)。

3
因为如果您为Mac OS 7编写了该代码,它将在新窗口中以文本形式显示。如果您在Apple] [+上完成此操作,它将直接写入内存的某个部分。在Mac OS X上,它会将其写到控制台。因此,这就是基于库层处理的执行硬件编写处理代码的三种不同方式。

2
@StevenBurnap是的- en.wikipedia.org/wiki/Aztec_C

10
您的FFT函数将很高兴在Windows或Linux(在同一CPU上)下运行,而无需重新编译。但是,您将如何显示结果?当然,使用操作系统API。(printf来自msvcr90.dll的信息与printf来自libc.so.6的信息不同)
immibis

9
即使API“不是操作系统的一部分”,但如果您从一个操作系统转到另一个操作系统,它们仍然会有所不同。(根据图表,这当然引起了“不是操作系统的一部分”这个词组的真正含义的问题。)
Theodoros Chatzigiannakis 2014年

Answers:


78

您提到了代码如何特定于CPU,为什么也必须特定于OS。实际上,这是这里许多答案都假定的一个有趣的问题。

CPU安全模型

在大多数CPU架构上运行的第一个程序在称为内部环或环0的内部运行。特定的CPU架构实现环的方式各不相同,但事实是,几乎每个现代CPU至少都有两种操作模式,一种是特权模式,并运行“裸机”代码,该代码可以执行CPU可以执行的任何合法操作,而另一种是不受信任,并运行只能执行定义的安全功能集的受保护代码。但是,某些CPU的粒度要高得多,为了安全地使用VM,至少需要1或2个额外的环(通常用负数标记),但这超出了此答案的范围。

操作系统进来的地方

早期的单任务操作系统

在早期的DOS和其他早期的基于单任务的系统中,所有代码都在内部环中运行,您运行的每个程序都具有整个计算机的全部功能,如果行为不当,包括擦除所有数据甚至造成硬件损坏,实际上可以执行任何操作。在一些极端的情况下,例如在非常老的显示屏上设置无效的显示模式,更糟糕的是,这可能是由简单的错误代码引起的,没有任何恶意。

实际上,此代码在很大程度上与操作系统无关,只要您具有能够将程序加载到内存中的加载程序(对于早期的二进制格式来说就非常简单),并且该代码不依赖任何驱动程序,而是实现了所有硬件访问本身就应该在以下条件下运行只要在环0上运行,任何操作系统都可以。请注意,如果这样的简单操作系统仅用于运行其他程序,并且不提供其他功能,则通常称为监视器

现代多任务操作系统

包括UNIX在内的更现代的操作系统,以NT开头Windows版本以及其他现在不为人所知的OS决定在这种情况下进行改进,用户需要诸如多任务处理之类的附加功能以便可以一次运行多个应用程序并提供保护,因此存在一个错误(或恶意代码)在应用程序中将不再对计算机和数据造成无限的破坏。

这是使用上述环完成的,操作系统将只在环0中运行,而应用程序将在外部不受信任的环中运行,只能执行操作系统允许的一组受限操作。

但是,增加的实用性和保护需要付出一定的代价,程序现在必须与OS配合使用才能执行不允许自己执行的任务,例如,它们无法再通过访问其内存并随意更改来直接控制硬盘。数据,相反,他们必须要求OS为它们执行这些任务,以便它可以检查是否允许他们执行该操作,而不更改不属于它们的文件,还可以检查该操作确实有效,并且不会使硬件处于未定义状态。

每个OS都针对这些保护方案决定了不同的实现方式,部分基于该OS所设计的体系结构,部分基于所讨论的OS的设计和原理,例如UNIX将重点放在适合多用户使用的计算机上, Windows被设计为更简单,可在单个用户的较慢硬件上运行的情况下可用的功能。在X86上,用户空间程序与OS进行通讯的方式与在ARM或MIPS上进行通讯的方式完全不同,例如,迫使多平台OS围绕在目标硬件上工作的需要做出决策。

这些特定于OS的交互通常称为“系统调用”,涵盖了用户空间程序如何通过OS完全与硬件交互,它们在根本上因OS的功能而异,因此通过系统调用完成其工作的程序需要特定于操作系统。

程序加载器

除了系统调用外,每个OS还提供了不同的方法来从辅助存储介质加载程序并将其加载到内存中,为了可由特定的OS加载,该程序必须包含一个特殊的标头,向OS描述如何加载并运行。

这个头文件曾经很简单,以至于用不同格式编写一个加载器几乎是微不足道的,但是对于像elf这样的现代格式,它支持动态链接和弱声明等高级功能,现在操作系统几乎不可能尝试加载二进制文件这不是为它设计的,这意味着,即使没有系统调用不兼容性,也很难以一种可以运行程序的方式将程序放在ram中。

图书馆

程序很少直接使用系统调用,但是,通过将系统调用以一种稍微友好的格式包装用于编程语言的库,它们几乎可以独占地获得其功能,例如,C在Linux下具有C标准库和glibc,在Linux下具有类似的库和win32库在Windows NT及更高版本中,大多数其他编程语言也具有类似的库,它们以适当的方式包装系统功能。

这些库甚至可以在某种程度上克服如上所述的跨平台问题,有一系列库的设计目的是为应用程序提供统一的平台,同时在内部管理对各种操作系统(例如SDL)的调用,这意味着程序不能与二进制兼容,使用这些库的程序在平台之间可能具有共同的来源,从而使移植与重新编译一样简单。

例外情况

尽管我在这里已经说了很多,但仍在尝试克服无法在多个操作系统上运行程序的局限性。Wine项目就是一个很好的例子,该项目成功地模拟了win32程序加载器,二进制格式和系统库,使Windows程序可以在各种UNIX上运行。还有一个兼容层,允许多个BSD UNIX操作系统运行Linux软件,当然,苹果自己的填充程序也允许一个人在MacOS X下运行旧的MacOS软件。

但是,这些项目需要大量的手动开发工作。根据这两个OS的不同程度,难度从很小的填充到对另一个OS的接近完全仿真的范围,这通常比编写整个操作系统本身要复杂得多,因此这是例外,而不是常规。


6
+1“为什么软件操作系统特定?” 因为历史。
Paul Draper 2014年

2
源于x86的CPU安全模型?为什么以及何时发明模型?
n611x007 2014年

8
@naxa否,它早于x86,它于1969年首次为Multics实施,这是第一个具有有用的多用户分时功能的OS,需要在GE-645计算机中使用该模型,但是该实现不完整,依赖于它的后继产品Honeywell 6180是软件支持,这是硬件中第一个完整且安全的实现。这是完全基于硬件的,并允许Multics从多个用户运行代码,而没有机会相互干扰。

@Vality另外,IBM LPAR是〜1972 。
Elliott Frisch 2014年

@ElliottFrisch哇,真令人印象深刻。我还没有意识到那太早了。感谢您提供的信息。

48

如您所见,API未表示为操作系统的一部分。

我认为您对图表的阅读过多。是的,操作系统将为如何调用操作系统功能指定一个二进制接口,还将为可执行文件定义文件格式,但从提供可被调用的功能目录的意义上讲,它将提供一个API。调用OS服务的应用程序。

我认为该图只是想强调操作系统功能通常是通过与简单库调用不同的机制来调用的。大多数常见的OS使用处理器中断来访问OS功能。典型的现代操作系统不会让用户程序直接访问任何硬件。如果要向控制台写入字符,则必须要求操作系统为您完成操作。用来写入控制台的系统调用因操作系统而异,因此正确的例子说明了为什么软件是特定于操作系统的。

printf是C运行时库中的函数,在典型的实现中是相当复杂的函数。如果您使用Google,则可以在线找到多个版本的源代码。请参阅此页面的导览。尽管最终它会进行一个或多个系统调用,但是这些系统调用中的每一个都特定于主机操作系统。


4
如果所有程序都执行该操作,那就是添加两个数字,而没有输入或输出。该程序仍然是特定于操作系统的吗?
Paul

2
操作系统旨在将大多数硬件特定的东西放在抽象层的后面/之中。但是,操作系统本身(抽象)可能因实现而异。POSIX遵循(或多或少)某些操作系统,也许还有其他操作系统,但总体而言,操作系统在抽象的“可见”部分方面差别太大。如前所述:您无法在Windows上打开/ home / user,也无法在* N * X系统上访问HKEY_LOCAL_MACHINE \...。您可以为此编写虚拟(“仿真”)软件,以帮助将这些系统更紧密地结合在一起,但始终是“第三方”(来自OS POV)。
RobIII

16
@保罗是的。特别是,将其打包为可执行文件的方式将取决于操作系统。
OrangeDog 2014年

4
@TimSeguine我不同意您使用XP vs 7的示例。Microsoft进行了大量工作,以确保7中存在与XP中相同的API。显然,这里发生的是该程序被设计为针对特定的API或合同运行。新操作系统仅遵循相同的API /合同。但是,对于Windows,该API是非常专有的,这就是为什么没有其他OS供应商支持它的原因。即便再有很多的程序实例赫克说不要在7上运行
艺术

3
@Paul:没有输入/输出的程序空程序,应编译为无操作。
Bergi 2014年

14

编译此程序时,编译器是否在进行任何特定于OS的操作?

大概。在编译和链接过程中的某个时候,您的代码将变成特定于操作系统的二进制文件,并与任何所需的库链接。必须以操作系统期望的格式保存程序,以便OS可以加载程序并开始执行程序。此外,您正在调用标准库函数printf(),该函数在某种程度上是根据操作系统提供的服务实现的。

库提供了一个接口-从操作系统和硬件的抽象层-并且可以为不同的操作系统或不同的硬件重新编译程序。但是这种抽象存在于源代码级别-程序被编译和链接后,就连接到特定于给定OS的该接口的特定实现。


12

有多种原因,但是一个非常重要的原因是,操作系统必须知道如何将构成程序的一系列字节读取到内存中,找到与该程序一起使用的库并将它们加载到内存中,以及然后开始执行您的程序代码。为此,操作系统的创建者会为该字节系列创建一种特殊的格式,以便操作系统代码知道在哪里寻找程序结构的各个部分。因为主要操作系统的作者不同,所以这些格式通常彼此之间没有任何关系。特别是Windows可执行文件格式与大多数Unix变体使用的ELF格式几乎没有共同点。因此,所有这些加载,动态链接和执行代码必须特定于OS。

接下来,每个操作系统都提供一组不同的库,用于与硬件层对话。这些是您提到的API,它们通常是向开发人员提供更简单接口的库,同时将其转换为对OS本身更深入的更复杂,更特定的调用,这些调用通常是无证的或安全的。该层通常是灰色的,较新的“ OS” API部分或完全基于较旧的API构建。例如,在Windows中,微软多年来创建的许多较新的API本质上是原始Win32 API之上的层。

在您的示例中没有出现的问题,但是开发人员面临的更大问题之一是与窗口管理器的接口,以提供GUI。窗口管理器是否是“ OS”的一部分,有时取决于您的观点以及OS本身,Windows中的GUI与OS进行了更深层次的集成,而Linux和OS X中的GUI则与OS集成在一起。更直接分开。这点非常重要,因为今天人们通常所说的“操作系统”比教科书所描述的要大得多,因为它包含许多许多应用程序级组件。

最后,严格来说,不是操作系统问题,而是可执行文件生成中的一个重要问题,即不同的机器具有不同的汇编语言目标,因此实际生成的目标代码必须不同。从严格意义上讲,这并不是严格意义上的“ OS”问题,而是硬件问题,但这确实意味着您将需要针对不同硬件平台的不同版本。


2
可能值得一提的是,仅使用少量的RAM(如果有)就可以加载更简单的可执行格式,而仅保留存储的代码所需的RAM,而更复杂的格式可能在某些情况下(在某些情况下)需要更大的RAM占用空间。即使在加载之后 MS-DOS可以通过简单地将顺序字节读取到RAM中从任意段的偏移量0x100开始的方式加载高达63.75K的COM文件,使用结束地址加载CX,然后跳转到该地址。单遍编译可以通过以下方式完成,而无需进行后修补(对软盘有用):
supercat

1
...使编译器在每个例程中包含所有补丁点的列表,每个补丁点都将包括前一个此类列表的地址,并将最后一个列表的地址放在代码末尾。操作系统只是将代码作为原始字节加载,但是代码中的一个小例程可以在运行代码的主要部分之前应用所有必需的地址补丁。
超级猫2014年

9

从我的另一个答案

考虑一下早期的DOS计算机,Microsoft对世界的真正贡献是:

Autocad必须为可以打印到的每台打印机编写驱动程序。莲花1-2-3也是如此。实际上,如果要打印软件,则必须编写自己的驱动程序。如果有10台打印机和10个程序,则必须分别独立地编写100个基本上相同的代码。

Windows 3.1试图实现的功能(以及GEM和许多其他抽象层)使得打印机制造商为其打印机编写了一个驱动程序,而程序员为Windows打印机类编写了一个驱动程序。

现在有10个程序和10台打印机,只需要编写20条代码,并且由于代码的Microsoft方面对每个人来说都是相同的,因此来自MS的示例意味着您要做的工作很少。

现在,程序不仅限于他们选择支持的10台打印机,还包括制造商在Windows中提供驱动程序的所有打印机。

因此,操作系统为应用程序提供服务,因此应用程序不必执行多余的工作。

您的示例C程序使用printf,它将字符发送到stdout-stdout,这是特定于操作系统的资源,它将在用户界面上显示字符。该程序不需要知道用户界面在哪里-它可以在DOS中,可以在图形窗口中,可以通过管道传输到另一个程序,并用作另一个进程的输入。

因为操作系统提供了这些资源,所以程序员只需很少的工作就可以完成更多的工作。

但是,即使启动程序也很复杂。操作系统期望可执行文件在开始时就具有某些信息,该信息告诉操作系统应如何启动,以及在某些情况下(如android或iOS等更高级的环境),将需要哪些资源才能获得批准,因为它们会接触到外部资源。 “沙盒”-一种安全措施,可帮助保护用户和其他应用程序免受程序错误的影响。

因此,即使可执行的机器代码是相同的,并且不需要操作系统资源,为Windows编译的程序也不会在没有附加仿真或转换层的OS X操作系统上运行,即使在相同的硬件上也是如此。

早期的DOS风格的操作系统通常可以共享程序,因为它们在硬件(BIOS)中实现了相同的API,并且OS挂接到硬件中以提供服务。因此,如果您编写并编译了一个COM程序(这只是一系列处理器指令的内存映像),则可以在CP / M,MS-DOS和其他几种操作系统上运行它。实际上,您仍然可以在现代Windows计算机上运行COM程序。其他操作系统不使用相同的BIOS API挂钩,因此,如果没有仿真或转换层,COM程序将无法在它们上运行。EXE程序遵循的结构不仅仅包含处理器指令,因此,伴随着API问题,它不会在不了解如何将其加载到内存并执行的计算机上运行。


7

实际上,真正的答案是,如果每个操作系统都确实理解相同的可执行二进制文件布局,并且您仅将自己局限于操作系统提供的标准功能(例如C标准库中的功能),那么您的软件就会,实际上可以在任何OS上运行。

当然,事实并非如此。一个EXE文件不具有相同的格式ELF文件,即使两者包含相同的CPU的二进制代码。*所以,每个操作系统都需要能够解释所有的文件格式,而且他们根本没有这样做的一开始,没有理由让他们以后再这样做(几乎可以肯定是出于商业原因,而不是出于技术原因)。

此外,您的程序可能需要执行C库未定义如何执行的操作(即使对于诸如列出目录内容之类的简单操作),在这种情况下,每个OS都会提供自己的函数来实现您的目标。任务,自然意味着您不会使用最低的公分母(除非您自己创建分母)。

因此,原则上,这是完全可能的。实际上,WINE 直接在Linux上运行Windows可执行文件。
但这是大量工作,而且(通常)在商业上不合理。

*注意:有一个很多更不仅仅是二进制代码的可执行文件。有大量信息可以告诉操作系统文件依赖于哪些库,需要多少堆栈内存,导出至可能依赖于该文件的其他库的功能,操作系统在哪里可以找到相关的调试信息,如何“重新定位如果有必要”的文件在内存中,如何正确地进行异常处理工作,等等等等....再次,有可能是这个单一的格式,每个人都同意,但我们确实没有。


有趣的事实:有一个标准化的posiz二进制格式,可以在各种OS上运行。只是不常用。
Marcin 2014年

@Marcin:似乎您不认为Windows是操作系统。(或者您是说Windows可以运行POSIX二进制文件?!)就我的回答而言,POSIX不是我所指的那种标准。POSIX中的X代表Unix。即使Windows确实具有POSIX子系统,也从未打算将其用于Windows。
Mehrdad 2014年

1.某些东西可以跨多个操作系统运行,而不必跨所有操作系统运行;2. Windows,因为NT能够运行posix二进制文件。
Marcin 2014年

1
@Marcin:(1)就像我说的那样,POSIX中的X代表UNIX。它不是其他操作系统要遵循的标准,它只是试图在各个Unix之间达成一个共同的标准,这虽然很棒,但并不令人惊讶。关于多种Unix操作系统的事实,与我一直试图就Unix以外的其他操作系统之间的兼容性问题完全无关。(2)您可以为#2提供参考吗?
Mehrdad 2014年

1
@Mehrdad:Marcin是对的;Windows SUA(Unix应用程序子系统)符合POSIX
MSalters 2014年

5

该图具有“应用程序”层(大部分),通过“库”与“操作系统”层分开,这意味着“应用程序”和“ OS”不需要彼此了解。这是图中的简化,但事实并非如此。

问题在于“库”实际上包含三个部分:实现,与应用程序的接口以及与OS的接口。原则上,就操作系统而言,前两个可以“通用”(取决于您对它进行切片的位置),但是第三部分(与操作系统的接口)通常不能。操作系统的接口必定取决于操作系统,它提供的API,打包机制(例如Windows DLL使用的文件格式)等。

因为“库”通常以单个软件包的形式提供,所以这意味着一旦程序选择了要使用的“库”,它将提交给特定的OS。这是以下两种方式之一:a)程序员提前完全选择,然后库和应用程序之间的绑定可以是通用的,但是库本身绑定到了OS;或b)程序员进行了设置,以便在您运行程序时选择库,但是程序和库之间的绑定机制本身依赖于OS(例如Windows中的DLL机制)。每种方法都有其优点和缺点,但是无论哪种方式,您都必须事先进行选择。

现在,这并不意味着不可能做,但是您必须非常聪明。为了解决这个问题,您必须在运行时选择库,并且必须提出一种不依赖于操作系统的通用绑定机制(因此,您有责任维护它,还有很多工作)。有时候值得。

您不必这样做,但是如果您要付出努力,那么很可能您也不希望绑定到特定的处理器,因此您将编写虚拟机并进行编译您的程序为处理器中性代码格式。

现在,您应该已经注意到我要去的地方。像Java这样的语言平台正是这样做的。Java运行时(库)定义Java程序和库之间与OS无关的绑定(Java运行时如何打开和运行程序),并且它提供特定于当前OS的实现。.NET在一定程度上做了同样的事情,除了Microsoft不为Windows提供任何东西的“库”(运行时)(但是其他人提供了-参见Mono)。而且,实际上,Flash也做同样的事情,尽管它的范围仅限于浏览器。

最后,有一些方法可以在没有自定义绑定机制的情况下做同样的事情。您可以使用常规工具,但是将绑定步骤推迟到库,直到用户选择操作系统为止。这就是分发源代码时发生的情况。当用户准备好运行程序时,它将使用您的程序并将其绑定到处理器(编译)和OS(链接)。

这完全取决于您如何分割图层。归根结底,您始终拥有一台由运行特定机器代码的特定硬件制成的计算设备。这些层在很大程度上是作为概念框架。


3

软件并不总是特定于操作系统。两者的Java和早期的P码系统(甚至的ScummVM)允许软件是跨操作系统移植。Infocom(ZorkZ-machine的制造商)也有一个基于另一个虚拟机的关系数据库。但是,在某种程度上,某些东西甚至必须将那些抽象转换为要在计算机上执行的实际指令。


3
但是,Java运行在不是跨OS的虚拟机上。您必须为每个操作系统使用不同的JVM二进制文件
Izkata 2014年

3
@Izkata是的,但是您不重新编译软件(只是JVM)。另外,请参阅我的最后一句话。但是我要指出,Sun确实有一个可以直接执行字节码的微处理器。
Elliott Frisch 2014年

3
Java是一种操作系统,尽管通常不将其视为一种。Java软件特定于Java OS,并且对于大多数“实际” OS都有Java OS仿真器。但是,您可以对任何主机和目标操作系统执行相同的操作-例如使用WINE在Linux上运行Windows软件。
immibis 2014年

@immibis我会更具体。Java基础类(JFC,Java的标准库)是一个框架。Java本身是一种语言。JVM类似于OS:它的名称中带有“虚拟机”,并且从其中运行的代码的角度来看,其执行与OS类似的功能。

1

你说

使用某些操作系统的编程语言生产的软件只能与它们一起使用

但是您作为示例给出的程序可以在许多操作系统上运行,甚至可以在某些裸机环境下运行。

这里重要的是源代码和已编译二进制文件之间的区别。C编程语言经过专门设计,以源代码形式独立于OS。它通过将诸如“打印到控制台”之类的解释交给实施者来完成。但是C可能符合特定于OS的内容(出于原因,请参阅其他答案)。例如,PE或ELF可执行格式。


6
很显然,OP正在询问二进制文件,而不是源代码。
Caleb 2014年

0

其他人已经很好地涵盖了技术细节,我想提到一个不太技术的原因,即UX / UI方面:

写一次,到处感到尴尬

每个操作系统都有其自己的用户界面API和设计标准。可以为一个程序编写一个用户界面,并使它在多个操作系统上运行,但是这样做几乎可以保证该程序在任何地方都感觉不合适。打造良好的用户界面需要调整每个受支持平台的详细信息。

其中许多都是小细节,但是如果弄错了,就会让用户感到沮丧:

  • 在Windows和OSX中,确认对话框的按钮顺序不同。弄错了,用户将通过肌肉记忆点击错误的按钮。Windows依次具有“确定”,“取消”。OSX交换了顺序,“执行”按钮的文本简短说明了要执行的操作:“取消”,“移至废纸“”。
  • 对于iOS和Android,“返回”行为是不同的。iOS应用程序会根据需要绘制自己的后退按钮,通常在左上角。Android会根据屏幕旋转情况在左下方或右下方有一个专用按钮。如果忽略操作系统后退按钮,则Android的快速端口将无法正常运行。
  • 在iOS,OSX和Android之间,动量滚动不同。可悲的是,如果您不编写本机UI代码,则可能必须编写自己的滚动行为。

即使在技术上可以编写一个可在任何地方运行的UI代码库,也最好对每个受支持的操作系统进行调整。


-2

此时的一个重要区别是将编译器与链接器分开。编译器很可能产生或多或少相同的输出(差异主要是由于#if WINDOWSs 引起的)。另一方面,链接器必须处理所有特定于平台的内容-链接库,构建可执行文件等。

换句话说,编译器主要关心CPU体系结构,因为它产生实际的可运行代码,并且必须使用CPU的指令和资源(请注意,.NET的IL或JVM的字节码将被视为虚拟CPU的指令集。在这种情况下)。这就是为什么您必须例如分别为x86和编译代码的原因ARM

另一方面,链接器必须获取所有这些原始数据和指令,并以加载程序(如今,这几乎总是操作系统)可以理解的格式进行放置,以及链接任何静态链接的库。 (还包括动态链接,内存分配等所需的代码)。

换句话说,您只能将代码编译一次并使其在Linux和Windows上都可以运行-但是您必须将其链接两次,从而生成两个不同的可执行文件。现在,在实践中,您通常也必须在代码中留出余地(这是(预)编译器指令的出处),因此即使两次编译一次链接也不用很多。更不用说人们在构建过程中将编译和链接视为一个步骤(就像您不再关心编译器本身的各个部分一样)。

DOS时代的软件通常是二进制可移植的,但是您必须了解,它并不是针对DOS或Unix而是针对大多数IBM风格的PC通用的特定合同进行编译的-将当前的API调用卸载了软件中断。这不需要静态链接,因为您只需要设置必要的寄存器,例如调用int 13h图形功能,CPU就会跳转到在中断表中声明的内存指针。当然,再次,实践是比较棘手的,因为要获得踏板般的性能,您必须自己编写所有这些方法,但基本上就等于完全绕开了OS。当然,总有一些东西需要与OS API交互-程序终止。但是,如果您使用的是最简单的格式(例如COM在DOS上,它没有标题,只是指示信息),并且不想退出,好吧-幸运的是您!当然,您也可以在运行时处理正确的终止,因此您可以在同一可执行文件中同时包含Unix终止和DOS终止的代码,并在运行时检测要使用哪一个:)


这似乎只是重复解释点这个这个这是昨天公布答案之前
蚊蚋
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.