学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

【译】Mach-O格式

Mach-O可执行格式概述

Mach-O是OS X中二进制文件的本机可执行格式,是发布代码的首选格式。可执行格式决定二进制文件中的代码和数据读入内存的顺序。代码和数据的顺序影响内存使用和分页活动,因此直接影响程序的性能。

Mach-O二进制文件被组织成段(segments)。每个段包含一个或多个节(sections)。不同类型的代码或数据进入每个节(section)。段总是从页面边界(a page boundary)开始,但节不一定是页对齐的。段的大小是由它所包含的所有节(sections)部分的字节数来衡量的,并四舍五入到(rounded up to)下一个虚拟内存页边界。因此,一个段总是4096字节的倍数,或4千字节,4096字节是最小大小。

Mach-O可执行文件的段和节是根据它们的预期用途命名的。段名称的约定是使用全大写字母,前面加双下划线(例如,__TEXT);section名称的约定是使用全小写字母加双下划线(例如,__text)。

在Mach-O可执行文件中有几个可能的段,但其中只有两个段与性能有关:__TEXT段和__DATA

__TEXT段:只读

__TEXT段是一个只读区域,包含可执行代码和常量数据。按照惯例,编译器工具创建的每个可执行文件至少有一个只读的__TEXT段。因为这个段是只读的,内核只能将__TEXT段直接从可执行文件映射到内存中,只有一次。当这个段被映射到内存中时,它可以被所有对其内容感兴趣的进程共享。(这主要是框架和其他共享库的情况。)只读属性还意味着构成__TEXT段的页永远不需要保存到备份存储中。如果内核需要释放物理内存,它可以丢弃一个或多个__TEXT页,并在需要时从磁盘重新读取它们。

表1列出了一些可以出现在__TEXT段中的更重要的部分。有关段的完整列表,请参阅Mach-O运行时体系结构(Mach-O Runtime Architecture)。

__DATA 段: 读写

__DATA段包含可执行文件的非常量数据。这个段既可读又可写。因为它是可写的,所以框架或其他共享库的__DATA段在逻辑上被复制给每个与该库链接的进程。当内存页可读可写时,内核将其标记为写时复制(copy-on-write)。这种技术将延迟(defers)复制页面,直到共享该页的进程之一尝试写入该页。当这种情况发生时,内核为该进程创建一个页面的私有副本。

__DATA段有许多sections,其中一些只被动态链接器使用。表2列出了__DATA段中可能出现的一些更重要的sections。有关段的完整列表,请参阅Mach-O运行时体系结构

Mach-O性能影响

Mach-O可执行文件的__TEXT__DATA段的组合直接影响性能。优化这些部分的技术和目标是不同的。然而,他们有一个共同的目标:提高内存的使用效率

典型的Mach-O文件大部分由可执行代码组成,这些代码占据了__TEXT, __TEXT部分。如__TEXT段:Read Only中所述,__TEXT段是只读的,直接映射到可执行文件。因此,如果内核需要回收一些__text页所占用的物理内存,它不必将这些页保存到备份存储(backing store)中,然后再将它们换入。它只需要释放内存,当代码稍后被引用时,从磁盘读入它。尽管这比交换更划算(因为它只涉及一个磁盘访问而不是两个),但成本仍然很高,特别是如果必须从磁盘重新创建许多页面时。

改善这种情况的一种方法是通过过程重新排序来改善代码的引用位置,如改进引用位置中所述。这种技术根据方法和函数的执行顺序、调用频率以及它们彼此调用的频率将它们分组在一起。如果__text节组中的页面在逻辑上以这种方式工作,则不太可能需要多次释放并读取它们。例如,如果您将所有启动时初始化函数放在一个或两个页面上,则在这些初始化发生后不必重新创建页面。

与__TEXT段不同,__DATA段可以被写入,因此__DATA段中的页面是不可共享的。框架中的非常量全局变量(non-constant global variables)会对性能产生影响,因为与框架链接的每个进程都有自己的这些变量副本。这个问题的主要解决方案是将尽可能多的非常量全局变量通过声明为const来移动到__TEXT,__const部分减少共享内存页描述了这一点和相关的技术。这对于应用程序来说通常不是问题,因为应用程序中的__DATA部分不会与其他应用程序共享。

编译器将不同类型的非常量全局数据存储在__DATA段的不同节(section)。这些类型的数据是未初始化的静态数据符号常量,与ANSI C“暂定定义”(tentative definition)的概念一致,没有声明为extern。未初始化的静态数据位于__DATA段的__bss部分。暂定定义符号位于__DATA段的__common部分。

ANSI C和c++标准规定,系统必须将未初始化的静态变量设置为零。(其他类型的未初始化数据保持未初始化状态。)由于未初始化的静态变量和暂定定义符号存储在不同的sections中,系统需要区别对待它们。但是,当变量位于不同的sections时,它们更有可能在不同的内存页中结束,因此可以分别交换进出(swapped in and out separately),从而使代码运行速度变慢。如减少共享内存页中所述,这些问题的解决方案是将非常量全局数据合并到__DATA段的一个section中。