iOS发动时刻优化-二进制重排的宿世

背景

在计算机范畴里,任何一个技能概念,都是为了处理实践问题而诞生的,为什么有二进制重排,本篇文章一起研究下

目标

  • 了解物理内存的两大弊端
  • 了解虚拟内存怎样处理物理内存两大问题
  • 了解发生页终断的原因
  • ASLR技能诞生的原因

研究

虚拟内存的诞生

在早期的计算机中 , 并没有虚拟内存的概念 , 任何运用被从磁盘中加载到运行内存中时 , 都是完好加载和按序摆放的 .

iOS发动时刻优化-二进制重排的宿世

直接运用物理内存 , 会出现以下问题:

  1. 安全问题 : 因为在内存条中运用的都是实在物理地址 , 并且内存条中各个运用进程都是按顺序依次摆放的 . 那么在 进程1 中经过地址偏移就能够拜访到其他进程的内存 。
  2. 功率问题 : 随着软件的开展 , 一个软件运行时需求占用的内存越来越多 , 但往往用户并不会用到这个运用的一切功用 , 形成很大的内存糟蹋 , 而后面翻开的进程往往需求排队等候。

为了处理上面两个问题,虚拟内存随机诞生

虚拟内存处理安全问题

如下图,了解虚拟内存和物理内存的联系

iOS发动时刻优化-二进制重排的宿世

  • 引用了虚拟内存后 , 在咱们进程中以为自己有一大片接连的内存空间实践上是虚拟的 , 也就是说从 0x000000 ~ 0xffffff 咱们是都能够拜访的 . 可是实践上这个内存地址只是一个虚拟地址 , 而这个虚拟内存地址经过一张映射表映射到物理内存的实在地址。
  • 表映射后才能够获取到实在的物理地址,体系对实在物理内存拜访做了一层约束 , 只有被写到映射表中的地址才是被认或许够拜访的 .
  • 例如 , 虚拟地址0x000000~0xffffff这个规模内的恣意地址咱们都能够拜访 , 可是这个虚拟地址对应的实践物理地址是计算机来随机分配到内存页上的 .

虚拟内存处理进程间安全问题原理

明显 , 引用虚拟内存后就不存在经过偏移能够拜访到其他进程的地址空间的问题了 .

因为每个进程的映射表是独自的 , 在你的进程中随便你怎样拜访 , 这些地址都是受映射表约束的 , 其实在物理地址永远在规则规模内 , 也就不存在经过偏移获取到其他进程的内存空间的问题了 .

并且实践上 , 每次运用被加载到内存中 , 实践分配的物理内存并不一定是固定或许接连的 , 这是因为内存分页以及懒加载以及ASLR(地址随机加载)所处理的安全问题 .

了解经过虚拟内存地址怎样找到数据

引进虚拟内存后 ,cpu在经过虚拟内存地址拜访数据的过程如下 :

  • 经过虚拟内存地址 , 找到对应进程的映射表 .
  • 经过映射表找到其对应的实在物理地址 , 从而找到数据 .

这个过程被称为地址翻译

虚拟内存处理功率问题

前面说到虚拟内存和物理内存经过映射表进行映射, 可是这个映射并不或许是一一对应的 , 那样就过分糟蹋内存了 ,为了处理功率问题 , 实践上实在物理内存是分页的,而映射表同样是以页为单位的 .

在linux体系中 , 一页内存巨细为4KB, 在不同渠道或许各有不同:

  • Mac OS体系中 , 一页为4KB,
  • iOS体系中 , 一页为16KB.

能够在终端经过pagesize指令获取映射表的巨细,例如mac如下:

iOS发动时刻优化-二进制重排的宿世

那么为什么说内存分页就能够处理内存糟蹋的功率问题呢 ?

内存分页原理

iOS发动时刻优化-二进制重排的宿世
(上图中咱们也看出 , 实践物理内存并不是接连以及某个进程完好的)

映射表左边的01代表当时地址有没有在物理内存中 . 为什么这么说呢 ?

  • 当运用被加载到内存中时 , 并不会将整个运用加载到内存中 . 只会放用到的那一部分 . 也就是懒加载的概念 , 换句话说就是运用运用多少 , 实践物理内存就实践存储多少 .
  • 当运用拜访到某个地址 , 映射表中为0, 也就是说并没有被加载到物理内存中时 , 体系就会立刻堵塞整个进程 , 触发一个咱们所熟知的缺页中止 – Page Fault.
  • 当一个缺页中止被触发 , 操作体系会从磁盘中从头读取这页数据到物理内存上 , 然后将映射表中虚拟内存指向对应物理内存地址 ( 如果当时内存已满 , 操作体系会经过置换页算法 找一页数据进行掩盖 , 这也是为什么开再多的运用也不会崩掉 , 可是之前开的运用再翻开时 , 就从头发动了的根本原因 ).

经过这种分页掩盖机制 , 就完美的处理了内存糟蹋和功率问题 .

了解ASLR(地址空间随机布局)技能

问题:当运用开发完成以后因为采用了虚拟内存 , 那么其间一个函数无论怎样运行 , 运行多少次 , 都会是虚拟内存中的固定地址 .

假定运用有一个函数 , 根据首地址偏移量为 0x00a000,都是固定的 , 那么虚拟地址从 0x000000 ~ 0xffffff , 根据这个 , 那么这个函数我无论怎样只需求经过 0x00a000 这个虚拟地址就能够拿到其实在的完成地址 .

而这种机制就给了许多黑客可操作性的空间 , 他们很容易的提前写好程序获取固定函数的完成进行修改hook操作。

为了处理这个问题 ,ASLR(地址空间随机布局)应运而生 . 其原理就是每次虚拟地址在映射实在地址之前 , 添加一个随机偏移值 , 以此来处理咱们刚刚所说到的这个问题 .

几乎一切的操作体系开始全民引进ASLR技能 , 而实践上自从引进ASLR后 , 拉高了黑客的hook门槛.

总结

问题1: 物理内存的两大弊端

  • 内存安全问题:各个进程都能够经过地址偏移获取不同进程的数据
  • 功率问题:发动进程,会一次加载全部代码和数据到物理内存,且当app一部分功用不运用也会常驻内存,后面进程请求内存需求排队组织

问题2: 虚拟内存怎样处理内存安全性问题

  • 进程内存都需求经过虚拟映射表和物理内存进行映射,获取数据,因为每一个进程拜访的空间规模都是独自的映射表提供,所以处理了数据安全问题

问题3: 虚拟内存怎样处理内存功率问题

  • 映射表内部采用懒加载的方式请求物理内存,运用发动不是一切数据都被写入物理内存,采用按需加载
  • 当进程内存去映射表请求数据不存在,会触发分页终断page fault 操作,从磁盘读取数据,然后写入物理内存,返回数据
  • 当映射表去物理内存请求空间,出现内存不足时,物理内存会对那些不运用的空间进行掩盖写入,用到掩盖的数据的进程发动需求再次触发分页终断,从头请求数据

问题4: 什么时候出现缺页终断

  • 当进程内存去映射表请求数据不存在,会触发缺页终断page fault 操作,从磁盘读取数据,然后写入物理内存,返回数据,这个过程因为体系会堵塞进程,一起iOS会涉及签名问题,会引起一部分时刻的消耗,后续会解说怎样经过较少分页终断优化发动时刻

问题5: 为什么需求ASLR操作

  • 未运用ASLR(地址空间随机布局)之前,进程发动,函数的地址都是固定不变,黑客很容易hook函数地址进行操作
  • 运用ASLR(地址空间随机布局)之后,进程发动,函数的地址是随机的,提高了黑客hook函数的本钱,提高了安全性

感谢您的阅读,本篇文章已阅读完毕,欢迎阅读我的其他文章!