了解 .Net 中的 32 位与 64 位

Understanding 32-bit vs 64-bit in .Net

我一直回避构建 64 位桌面应用程序的问题,因为我不知何故意识到这不是一件微不足道的事情,而且我想我并不真正理解解决方案"configuration manager" 对话框、平台和配置。

无论如何,我只是尝试通过简单地将所有解决方案项目的平台更改为 x64 来转换现有应用程序,并且成功了。我遇到的唯一问题是其中一个项目引用的 C++ DLL 需要重建为 x64。

所以我的问题(最后)是:为什么我只有 C++ DLL 有问题,但我的解决方案中的项目引用的许多 .Net 程序集 DLL 都没有问题?

究竟是什么决定了我的应用程序是否构建为 64 位?我将解决方案的所有项目都更改为 "x64",但如果将它们保留为 "Any CPU",它是否仍然有效,我只将 WPF(启动)项目更改为 "x64"?

编辑

所以最后看来我只是能够将所有项目平台设置为 "Any CPU" 而不是 "x64"(TFS 服务器无法 运行 单元测试与后者)。即使是引用非托管 64 位 DLL 的项目似乎对此也很满意(不知道为什么)。

诀窍是取消选中 WPF(启动)项目属性中的 Prefer 32-bit 选项。构建的应用程序现在 运行 作为 64 位应用程序,并且 TFS/unit 测试 运行 愉快。

从正确性的角度来看,32/64 仅在与本机代码互操作、使用低级不安全功能或分配大量(> 4GB)内存时才重要。如果没有这些考虑,您还不如使用 AnyCpu。 AnyCpu 代码将默认为您的主机 OS 位数(尽管可以更改该默认值),因此对于现在的大多数人来说,它将在 64 位进程中结束 运行ning。

有时会出于性能原因明确首选 32 位或 64 位。 32 位应用程序通常对缓存更友好。当 运行 作为 64 位进程(尽管并非总是如此)时,受计算限制的应用程序应该表现更好。

TL;DR:.Net 程序集永远不会 assembled 进入机器特定指令,而是将它们编译成 运行s 下的 MSIL .Net 虚拟机,在 运行 时间,VM 使用 JITer 生成可以由 CPU.

执行的机器指令

“破坏”目标文件的 32bit64bit 之间的区别是:指针大小内存模型,最重要的是 指令集架构 (ISA)。让我们分解每个点,看看为什么 .Net 程序集大部分不受影响。

磅值(或长度)

32bit 执行环境中,指针是 32 位长,而正如您所期望的 int 64 位,它们是 64 位长。这个事实可以通过以下两种方式之一破坏您的程序集:

  1. 包含指针的结构将更改其内部内存结构,因为它们需要更多(或更少)字节来存储指针。
  2. 指针算法 - 一些聪明的开发人员喜欢摆弄指针、屏蔽它们、添加它们等等。在某些情况下,指针的大小可能是这些计算产生正确结果的整数。

为什么 .Net 程序集不受影响

因为 .Net 语言是 safe - 你不能玩指针(通常,除非你处于不安全的上下文中)。没有使用指针的选项解决了第二点。至于第一点——这就是你可以获得 class 的大小(使用 sizeof)而不是包含 class 引用的结构的大小的原因。

内存模型

旧的 32bit 处理器曾经有一个称为 Memory segmentation 的功能,它允许 OSSupervisor program 声明使用特殊 CPU registers 称为 Segment registers。在 64bit 中,此功能大部分被禁用。因此编译为在内存分段下工作的程序在非分段环境中可能会出现问题。

为什么 .Net 程序集不受影响

通常我们不会在这么低的级别处理内存,这是可能的,因为 .Net virtual machine 如下一点所述。

指令集架构

32bit64bitx86AMD64)相似但完全不同 ISAs 这意味着代码是 assemble d 到 运行 下一个不会 运行 下另一个。因此,与其他两点不同,无论您在其中写了什么,这一点都会破坏您的程序集。

为什么 .Net 程序集不受影响

那么 .Net 程序集怎么可能被编译为 Any CPU?诀窍在于,当您编译 .Net 语言时,您永远不会 assemble 它。这意味着当您按 compile 时,您只会将代码编译成一个不是 assembled 的中间对象(称为 .Net 程序集)(即不是 运行 通过 assembler).

通常当你在 C/++ 中编译代码时,你首先 运行 通过编译生成一些 assembly instructions 这些指令然后传递给 assembler 生成machine instructions。 CPU只能执行这些machine instructions.

.Net 语言不同,如上所述,当您编译 .Net 语言时,您 运行 它通过编译器并停在那里,没有 assembler 参与。这里的编译也会生成一些 assembler instructions,但与 c/c++ 编译生成的指令不同,这些指令与机器无关,它们是用称为 MSIL - Microsoft intermediate language 的语言编写的。没有 CPU 知道如何执行这些指令,因为就像普通的 assembler 指令一样,它们也必须 assembled 变成机器指令。

诀窍是这一切都发生在 运行 时间,当用户打开您的程序时,他会启动 .Net runtime 的实例,其中您的程序 运行s,您的程序永远不要 运行s 直接针对本机本身。当您的程序需要调用方法时,.Net 虚拟机中称为 JITer - just in time compiler 的特殊组件的任务是将此方法从 MSIL 组装为特定于安装 .Net 框架的机器的机器指令。