内容简介:Mach-O文件可以分成segment。通常大写字母表示,例如
一、Mach-O结构
1. Mach-O术语:
- Executable (可执行文件)
- Dylib (动态库)
- Bundle —— iOS特有的动态库,不可链接,只能运行时使用dlopen()
Image —— Executable、Dylib、Bundle都是 Image
Framework —— 带有特殊目录结构的Dylib,用来保存资源和头文件。
2. Mach-O内部结构
Mach-O文件可以分成segment。通常大写字母表示,例如 __TEXT , __DATA , __LINKEDIT 。
segment的大小由硬件决定的,是页大小的整数倍。arm64的页大小是16KB,其他的是4KB。
- __TEXT :__TEXT位于文件的头部,包含Mach header,机器指令以及readonly的常量,例如c字符串。
- __DATA :readwrite,包含全局变量
- __LINKEDIT :包含函数的名称和地址相关的信息
3. Mach-O通用文件
为32位和64位设备编译会分别获得一个Mach-O文件。将两个文件合并后会获得一个Mach-O通用文件,可以同时运行在32位和64位的设备上。
Fat Header记录其中的架构以及偏移位置。
二、虚拟内存
every problem can be solved by adding a level of indirection.
虚拟内存解决的问题是,多进程系统如何管理物理内存。
每个进程是由物理内存页映射的逻辑地址空间。 这种映射并 不是一一对应的 ,例如 有的逻辑地址不对应物理内存空间 ,也有 可能不同的逻辑地址对应着相同的物理内存 。
Page fault
有些逻辑地址没有对应的内存空间,当程序访问该逻辑地址时,会触发page fault。
此时内核就会停下当前的线程,来xxxxx
共享内存
不同的进程中的逻辑地址空间可以映射相同的物理内存空间,这两个进程共享内存中的内容。
file backed pages
可以通过mmap()调用,告诉虚拟内存,将文件中的某一片段映射到进程中的某段地址空间,而不是将整个文件都读进内存。没当第一次访问之前没有访问过的地址空间,都会触发一个page fault,内核会从文件中读取一页。从而实现了文件的懒读取,提升加载速度。
copy on write
当两个进程访问__DATA内容的时候,一个进程只读,一个进程想要写,这是就会出现copy on write。内核会将这一页copy到新的地址空间,然后重新映射。这时,进程都有自己copy出来的这一页。只读的那一页是clean page,拷贝出来的进行写操作的这一页叫做dirty page。dirty page意味着其中包含了进程相关的信息。对于clean page,内核之后可以重新生成,重新从硬盘中读取。dirty page代价更为昂贵。
加载动态库的过程
假设动态库的大小为8页,两个进程需要加载这个动态库。
进程1:
- dyld需要加载Mach-O的header,即图中RAM1。发现没有映射,所以触发page fault,将RAM1读取到物理内存中,然后映射到进程1的虚拟内存中。
- dyld读取Mach-O header,然后发现还需要一些信息在__LINKEDIT中。此时加载__LINKEDIT中的信息,触发page fault,将RAM2读取到物理内存中,然后映射到进程1的虚拟内存中。
- dyld读取__LINKEDIT内容,需要修改__DATA page的内容使动态库可运行。此时同上,加载__DATA中的信息,触发page fault,将RAM3读取到物理内存中,然后映射到进程1的虚拟内存中。
- 当dyld要往__DATA page中写内容的时候,就会触发copy on write。RAM3就变成的dirty page。
进程2:
- dyld需要加载Mach-O的header,即图中RAM1。内核会告诉dyld,内存中已经有这个页了。直接映射到进程2的虚拟内存中,无需IO。
- 同理,RAM2也是。
- 读取RAM3的时候,内核会查看RAM3的clean page依然在内存中,如果在,直接映射,如果不在,重新加载进内存。当dyld需要往__DATA page中写内容的时候,就会触发copy on write。RAM3就变成的dirty page。
只有dyld加载动态库的时候,才会需要__LINKEDIT,一旦完成后,__LINKEDIT中的内容可以回收。
此时,一共用了2个dirty page 和1个clean page。如果是直接读取整个文件到内容中,则一共产生了16个dirty page!
两个安全技术
- ASLR(address space layout randomization):地址空间布局随机化,将加载地址随机化。
- code sign:为了在运用时验证文件,整个文件在运行时重新读取,所以Mach-O中的每一页都有独立的code sign,存放在__LINKEDIT中。这样就能保证每一页都没有被篡改。
参考
- 深入理解iOS App的启动过程
- WWDC 2016: Optimizing App Startup Time
- WWDC 2017: App Startup Time: Past, Present, and Future
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。