Linux和Windows程序的不同之处

Rah*_*190 2 c linux windows ram processor

我在GCC编译了一个小的"Hello World"程序,它在Linux中运行.然后我将.exe扩展名添加到程序中,看看它是否适用于Windows.但它没有用.是什么原因导致该程序无法在Windows中运行.基本上二进制和汇编指令在windows&linux下编译的程序中是相同的(如果未使用OS特定库).操作系统只需将程序加载到RAM中,处理器就会执行它.那么,为什么它不起作用.

m0s*_*it0 9

现代操作系统并不那么容易.如果您使用Linux可执行文件,添加了EXE扩展,并在Windows中运行它,它将无法正常工作.除了代码和数据之外,现代操作系统在可执行文件中还有许多额外的信息.Linux使用ELF可执行格式,Windows使用EXE格式.此格式包括重定位信息,内存大小,分区和有时仅与使用此可执行格式的操作系统相关的信息.因此,只更改文件的扩展名将无效.

此外,OS系统调用以及它们的调用方式也不同.Linux使用软件中断(INT 0x80 IIRC)进行系统调用,而Windows则使用另一个系统.这意味着您必须为每个taget重新编译,因为甚至系统调用都不会以相同的方式完成,并且C编译器需要链接到该目标的右侧libc.


dat*_*olf 9

我会把你的问题分解成更小的子集

我在GCC编译了一个小的"Hello World"程序,它在Linux中运行.

这是可以预期的,因为系统上安装的通常GCC和binutils被配置为在执行时编译相同的目标和主机.或者通俗地说,它们将生成适用于Linux内核和GNU C标准库(或兼容的libc)运行时的可执行文件.

还有一些编译器可以构建程序来运行在不同类型的操作系统和/或CPU架构上,而不是它们自己执行的程序.这些都是所谓的交叉编译器,您可以使用它们在台式计算机上构建Smartphone应用程序.当您使用Windows Android NDK创建本机Android可执行文件时,您实际上是针对Linux内核和不同的CPU(很可能是ARM或MIPS)进行交叉编译.

然后我将.exe扩展名添加到程序中,看看它是否适用于Windows.

在这里,你陷入了一种误解,我永远不会理解为什么它会成为第一位.文件名后缀必须对它能做的事情做任何事情.Windows使用它在注册表中查找,如何处理它,但就是这样.在Windows中,您可以注册要识别为"可执行"的任何后缀.Linux(作为一个Unixoid)做的事情更简单,但恕我直言更强大:每个文件都带有一组标志,如果可以执行,如果可以,谁实际可以做到.

但实际上对于系统来说,作为可以运行的程序是有用的,需要不同的东西,并且所有可执行文件(对于给定的OS)共享,而不管它们的文件名是什么:特定的内部格式.Windows使用所谓的PE格式(PE代表可移植可执行文件,这有点可笑,因为支持它的唯一操作系统是Windows).Linux和*BSD使用ELF(可执行和链接格式),MacOS X使用称为Mach二进制格式的东西,并将英特尔CPU的转换扩展为Mach通用二进制格式.从技术上讲,Mach内核(MacOS X的基础)也可以理解ELF格式,但是在MacOS X中没有启用AFAIK.

虽然两种文件格式基本上都做同样的事情(描述如何将文件加载到内存中,它依赖于哪些库,必须加载它们,以及加载它们的位置,程序实际启动的哪个点)它们的结构是非常不同的.Windows不知道如何加载和执行ELF,Linux不知道如何加载和执行PE.

即使要么支持另一个,系统仍然缺少程序期望加载和运行的库.至少你需要(操作系统)相关的运行时环境,它负责从操作系统检索启动参数,提供操作系统特定的内存管理功能的接口等等"平凡"的东西.这些也从根本上Linux和Windows之间有所不同.

请注意,完全可以提供一个兼容性包装器,它构建在操作系统特定的东西之上,它将另一个操作系统模拟到程序,并且还知道如何加载,链接和启动外部可执行文件.在Linux上加载和执行Windows程序的包装器称为WINE,还存在名为LINE的反向事件,它在Windows上运行Linux可执行文件.

但它没有用.是什么原因导致该程序无法在Windows中运行.

Windows与Linux不同.它有一个完全不同的系统级API,它使用不同的二进制格式,甚至stdin/stdout(由C标准库定义)等基本内容的实现方式也大不相同.哎呀,他们甚至使用不同的调用 约定

Linux使用标准的C调用约定(stdcall).Windows主要使用cdecl约定,它是pascal和stdcall约定的混合体.

基本上二进制和汇编指令在windows&linux下编译的程序中是相同的(如果未使用OS特定库).

呃,不,他们绝对没有.调用约定的差异会产生不同的汇编.

操作系统只需将程序加载到RAM中,处理器就会执行它.

这不是那么简单.该程序还必须与操作系统"连接",以做任何有用的事情.你想看到控制台上打印的字符串?然后,您必须将其连接到某些操作系统功能,这样做.

在Linux上的控制台已经存在,并有特殊的文件/dev/console,/dev/stdin,/dev/stdout,/dev/stderr,这是内部连接到当前ptstty.在进程中,它们始终映射到文件描述符0,1和2.

在Windows中,程序可能或可能不必打开自己的控制台窗口(如果它从CLI启动它将使用它).所以它必须打电话AllocConsole,如果失败作为后备AttachConsole(parent_PID); 然后它可以GetStdHandle用来获取stdin,stdout,stderr.的HANDLEs此得到是任意的且需要被连接到内部执行C STDIO(printf的,fwrite的等等)和C++的iostream的.

此外,CPU不会"神奇地"执行一段二进制文件,因为它最终会在RAM中的某处.操作系统必须创建一个新进程,为此,它必须知道某些重要代码所在的二进制位置(入口点).这些入口点在二进制文件格式的特殊位置描述(以及其他内容).正如我已经解释过的,Windows只能理解与Linux不同的可执行二进制格式(PE).一旦二进制文件已加载并链接,它必须实际启动.如上所述,启动二进制文件的步骤在Linux和Windows之间也是不同的.

编辑后期,但必须写:

*当程序启动时,理解可执行文件的二进制文件根本不会完全加载到RAM中,这一点非常重要.所发生的是,创建了所谓的"虚拟内存映射".这意味着虚拟地址空间由操作系统创建,并与一些其他数据结构一起,这通常称为进程.虚拟地址空间依次由一组所谓的"页面"支持,这些"页面"是映射到实际存储器或存储设备的地址空间块.实际上,RAM 通常用作磁盘操作的缓存.映射程序二进制文件时,虚拟内存页面将始终显示指向磁盘存储器,RAM仅作为缓存.动态分配的内存取自缓存存储设备上交换空间的磁盘缓存.如果没有交换设备,那么它只是从通用磁盘缓存中获取.*

如您所见,这些是完成不同的任务列表.再加上调用约定和二进制格式的差异.

那么,为什么它不起作用.

因为可执行文件不仅仅是惰性装配指令.程序需要与操作系统通信以产生有意义的东西.Windows和Linux非常非常不同.