在Objective C(Mac OS X)中运行时检测CPU架构(32位/64位)

4
我正在编写一个Cocoa应用程序,需要执行一些针对32位和64位进行了优化的(控制台)应用程序。因此,我想检测应用程序运行在哪种CPU架构上,以便启动正确的控制台应用程序。
简而言之:如何检测应用程序是否运行在64位操作系统上?
编辑:我知道Mach-O fat二进制文件,但这不是我的问题。我需要知道这一点,以便启动另一个非捆绑(控制台)应用程序。一个针对x86优化,另一个针对x64优化。

2
即使您没有源代码,也可以组合两个已有的二进制文件。请记住,对于非捆绑控制台二进制文件,通用二进制文件也适用!只需按照cgkanchi的建议执行以下操作:lipo -create -arch i386 bin32 -arch x86_64 bin64 -output bin_universal - Yuji
我已经完成了。运行得非常好。 - Ger Teunis
7个回答

11

有一种超级简单的方法。编译两个版本的可执行文件,一个是32位的,一个是64位的,然后使用lipo将它们组合起来。这样,正确的版本将始终被执行。

gcc -lobjc somefile.m -o somefile -m32 -march=i686
gcc -lobjc somefile.m -o somefile2 -m64 -march=x86_64
lipo -create -arch i686 somefile -arch x86_64 somefile2 -output somefileUniversal

编辑:或者一开始就用 gcc -arch i686 -arch x86_64 编译一个通用二进制文件。

回应楼主的评论:

if(sizeof(int*) == 4)
    //system is 32-bit
else if(sizeof(int*) == 8)
    //system is 64-bit

编辑:糟糕!我没有意识到您需要运行时检查... 通过运行sysctl -A的输出,两个变量看起来可能有用。尝试解析sysctl hw.optional.x86_64sysctl hw.cpu64bit_capable的输出。我没有一个32位的Mac来测试这个,但在Core2Duo Mac上的Snow Leopard中这两个都设置为1。


你甚至可以在一行代码中完成这个操作,gcc -arch i386 -arch x86_64 somefile.m -o somefileUniversal :) - Yuji
这似乎是我情况下最好的解决方案。谢谢。 虽然我仍然想知道如何检测x64或x86,只是为了以后参考。 - Ger Teunis
1
Ger Teunis,你应该注意到这里提出的sizeof测试与下面的__LP64__建议一样“编译时”。 - Logan Capaldo
没错,但是他们提供了可行的lipo解决方案。我同意原问题没有得到回答,但是lipo解决了我的问题。 - Ger Teunis
是的和不是的。这取决于情况。这将检测您是否正在运行64位二进制文件,但如果您在32位二进制文件中运行,则无法检测到您是否在64位架构上。 - AdamIerymenko
显示剩余2条评论

8

使用[[NSRunningApplication currentApplication] executableArchitecture],它会返回以下常量之一:

  • NSBundleExecutableArchitectureI386
  • NSBundleExecutableArchitectureX86_64
  • NSBundleExecutableArchitecturePPC
  • NSBundleExecutableArchitecturePPC64

例如:

switch ([[NSRunningApplication currentApplication] executableArchitecture]) {
  case NSBundleExecutableArchitectureI386:
    // TODO: i386
    break;

  case NSBundleExecutableArchitectureX86_64:
    // TODO: x86_64
    break;

  case NSBundleExecutableArchitecturePPC:
    // TODO: ppc
    break;

  case NSBundleExecutableArchitecturePPC64:
    // TODO: ppc64
    break;

  default:
    // TODO: unknown arch
    break;
}

那个不起作用。如果你的可执行文件是使用32位编译的,无论操作系统是什么,它都会提供NSBundleExecutableArchitectureI386。 - Andreas Löw
1
当然,它就是这样工作的。它告诉你应用程序运行在哪个体系结构上。x86_64 CPU 可以模拟和运行 i386 中的应用程序。如果您的二进制文件仅编译为 i386,则它将在 i386 上运行。如果它是通用的,它通常会在 x86_64 CPU 上的 x86_64 上运行,除非您已更改内核选项以在 i386 中运行或者如果您通过 arch -32 ... 来运行您的应用程序。如果您想知道 CPU 架构而不考虑正在运行的应用程序的架构,请通过 sysctl 读取 hw.machine,例如由 bleater 回答的方式。 - Mirek Rusin

3
通常情况下,您不需要在运行时检查操作系统是否是64位或32位。如果您的宿主应用程序(我称其为启动64位或32位工具的应用程序)是一个fat二进制文件,则编译时检查就足够了。由于它将被编译两次(一次为fat二进制文件的32位部分,一次为64位部分),并且系统将启动正确的版本,因此通过编写类似以下内容的代码,您将编译正确的启动代码:
#if __LP64__
    NSString    *vExecutablePath = [[NSBundle mainBundle] pathForResource: @"tool64" ofType: @""];
#else
    NSString    *vExecutablePath = [[NSBundle mainBundle] pathForResource: @"tool32" ofType: @""];
#endif
[NSTask launchedTaskWithLaunchPath: vExecutableName ...];

如果用户在64位Mac上以32位模式启动应用程序,请相信他们知道自己在做什么。这种情况是边缘案例,为什么要因错误的完美主义而破坏那些高级用户的使用体验呢?如果你能告诉用户解决方法是以32位模式启动,你甚至会感到高兴,因为你可以发现仅限于64位的bug。
只有当你的应用程序本身是32位时(例如带有命令行助手的Carbon GUI),您才需要真正的运行时检查。在这种情况下,host_processor_info或sysctl或类似的工具可能是您唯一的选择,如果由于某种奇怪的原因你无法将这两个可执行文件合并。

3
您不必手动检测即可实现该效果。一个Mach-O可执行文件可以同时包含32位和64位Intel机器的二进制文件,内核会自动运行最适合的那个版本。如果您使用XCode,则在项目检查器中有一个设置选项,您可以设置要在单个通用二进制文件中包含的体系结构(ppc,i386,x86_64)。
此外,请记住,在OS X上,运行64位内核(使用Snow Leopard)和能够运行64位用户空间应用程序是两个正交的概念。如果您拥有一台64位CPU的计算机,即使内核以32位模式运行(使用Leopard或Snow Leopard),只要您链接的所有库都可用于64位,您就可以在64位模式下运行用户空间程序。因此,检查操作系统是否支持64位并没有太大用处。

我以为我已经讲清楚了。我试着更好地解释一下。我想调用一个非 FAT 二进制文件。其中一个是针对 x86 优化的,另一个是针对 x64 优化的。我没有创建这些二进制文件,也没有源代码。为了选择最佳的优化的非 FAT 二进制文件,我需要知道我运行在什么 CPU 上。 - Ger Teunis

2

如果您使用的是Snow Leopard,请使用NSRunningApplication的executableArchitecture。

否则,我会执行以下操作:

-(BOOL) is64Bit
{
#if __LP64__
  return YES;
#else
  return NO;
#endif
}

很遗憾,那是编译时。正在寻找运行时检查。无论如何,感谢您的回复。 - Ger Teunis
@Ger Teunis:被接受的答案也是编译时(sizeof部分),而且它并不重要。如果有人在64位机器上运行您的32位二进制文件,即使您提供了64位和32位版本,也没有必要不考虑世界是32位的事实。 - Fredrik
没错,但是他们提供了可行的lipo解决方案。我同意原问题没有得到回答,但是lipo解决了我的问题。 - Ger Teunis

1

编程获取CPU架构名称的字符串:

#include <sys/types.h>
#include <sys/sysctl.h>

// Determine the machine name, e.g. "x86_64".
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0); // Get size of data to be returned.
char *name = malloc(size);
sysctlbyname("hw.machine", name, &size, NULL, 0);

// Do stuff...

free(name);

在shell脚本中做同样的事情:

set name=`sysctl -n hw.machine`

0

检查操作系统版本(因此是否为Snow Leopard,64位操作系统)的标准方法详见此处


这并没有告诉你需要知道的信息——64位能力取决于操作系统版本和你所运行的CPU。 - Gordon Davisson
然后,您将根据Mach头文件中定义的内容,使用host_processor_info或host_info调用来跟进。 - Azeem.Butt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接