Page 121 - 《软件学报》2024年第6期
P. 121

谢汶兵 等: 二进制翻译技术综述                                                                2697


                 主机实地址的直接代换, 彻底消除软件模拟虚实地址转换引发的性能开销问题.

                 3.2   原子操作时序维护

                    原子操作保证了资源访问的互斥性和访问时序. 翻译多节拍原子操作时, 需要维持其原来的时序和语义特性.
                 然而, 基于软件模拟的指令翻译方式很容易破坏原子操作的原有逻辑时序, 引发正确性问题.
                    原子操作翻译过程中首先面临的一个挑战是原子指令的等价翻译. 例如                         x86  平台有将近   20  条原子指令, 如
                 inc、xadd  原子操作以及   cmpxchg  比较并交换   (compare and swap, CAS) 指令, 而  MIPS、ARM、Alpha 等平台并
                 没有  CAS  指令, 其对应的是链接加载       (load linked, LL) 和条件存储  (store conditional, SC) 指令组成的  LL/SC  指令
                 对. 对于  CAS  原子指令的翻译有两种常用的翻译方法: (1) 将源程序中所有的内存写操作转为原子写, 以保证访问
                 内存数据的一致性. 但是这种做法对性能影响大. (2) 维持源程序逻辑, 利用                   CAS  和  LL/SC  指令对进行功能的等价
                 模拟. Kristien  等人  [92] 研究发现  QEMU  和  ARCSim [93] 在利用  CAS  模拟  LL/SC  指令时存在正确性问题, 为了保证
                 从  LL/SC  到  CAS  翻译的正确性, 提出利用软件模拟页翻译缓存更新和失效机制. Natarajan              等人  [94] 提出基于内存
                 事务解决多线程应用之间的数据竞争问题, 将元数据访问封装在一个事务性原子块内完成, 保证了多线程中锁指
                 令翻译的正确性和性能. 此外, 基于         CAS  模拟  LL/SC  指令还可能会引发    ABA  问题  [95] . 针对  ABA  问题, Rigo 等人  [96]
                                                         [4]
                 提出利用   Helper 函数软件模拟实现      LL/SC  锁指令翻译. Jiang  等人  [97] 提出无锁队列引用计数的内存保护方法, 只
                 有内存引用节点计数值为         0  时才允许访问该内存. Cota 等人      [15] 提出通过位图维护    Cache 行和哈希完整地址行检
                 查来避免数据竞争并保证数据访问的原子性. Zhao               等人  [98] 利用插桩的方式维护一个非阻塞哈希表来记录更新内
                 存, 保证了锁变量地址与被访问内存地址的一致性.
                    原子操作翻译过程中还可能会引发锁地址非对齐问题, 例如                     x86  平台支持多种地址非对齐的原子操作, 而
                 MIPS、ARM、Alpha 等平台要求      LL/SC  指令满足  32  位地址对齐. 对于   x86  非对齐指令的翻译通常要求先将访存
                 地址对齐, 但在不同位宽的原子指令翻译时依旧可能会引发错误. 图                     7  是  16  位非对齐指令的访存场景, 假设起始
                 地址满足   32  位对齐, 其中  (1) 和  (3) 是  16  位地址对齐, (2) 和  (4) 是非  16  位地址对齐. 在该场景下, 用  32  位对齐原
                 子指令模拟    16  位非对齐原子指令, 此时      (1)(2)(3) 可以被同一个  32  位访存指令访问覆盖, 而      (4) 横跨两个  32  位内
                 存地址, 翻译可能出错. 针对该问题, Jiang       等人  [97] 利用  LL/SC  指令对模拟  CAS  指令, 采用多内存地址比较交换方
                 法解决锁地址非对齐可能引发的错误, 但是该方法基于软件模拟                     CAS  指令, 可能会引发大量的冗余判断.

                                                                         (1)

                                          0x***0000                      (2)
                                                                         (3)
                                                                              (4)

                                          图 7 32  位对齐指令模拟翻译       16  位非对齐指令


                 3.3   异常和中断处理
                    代码块执行时遇到异常或者中断, 其需要调用回退机制实现源程序状态的精准还原, 确保逻辑正确. 二进制翻
                                                                             [3]
                 译为了实现回退机制, 需要加入额外的保存、恢复机器状态的代码. IA-32 EL 采用还原点检测机制, 一旦发生异
                 常便从最近的还原点恢复出精确的机器状态. Crusoe 采用硬件支持的影子寄存器保存精确状态. 然而, 回退机制
                                                                    4
                 是比较耗时的, 研究表明        IA-32 EL  做一次回退机制需要多达        10 个时钟周期. 此外, 考虑到中断是异步事件.
                                                                                                     [9]
                 QEMU  使用主动处理中断的策略在生成翻译代码阶段插入中断检查代码, 避免回退机制产生的开销. Captive 使
                 用硬件虚拟化在客户模式下运行一个完整的翻译系统. 它的外部中断通过中断控制器传播到客户机系统, 并在中
                 断处理程序中设置中断挂起标志. 之后, 在每个翻译块的末尾检查此标志, 如果设置了该标志, 则将其清除并执行
                 相关的中断处理程序. 但事实上, 与执行翻译块相比, 中断发生的概率要低得多, 翻译器对大多数挂起的中断检查
                 都是非必要的. Niu    等人  [99] 利用信号和运行时二进制重写技术通知线程何时交付中断, 不再重复检查挂起的中断,
   116   117   118   119   120   121   122   123   124   125   126