什么是对齐异常?

简单来说,当CPU访问内存地址时,如果发现访问的地址是不对齐的,硬件(部分)就会自动触发对齐异常。对齐即要求被访问的地址满足其数据类型的位宽要求,比如要访问一个4字节int型的数据,但是提供的地址不是4字节对齐的,那就是不对齐了。也就是说要访问的数据的位宽长度是多少,那么访问的地址就必须是按这个位宽长度对齐的。如果是char类型的,那就没有没有对齐要求了。

为什么在部分硬件上出现?

部分CPU硬件支持非对齐访问,典型的就是X86,X86硬件会自动处理非对齐访问情况,对软件透明,代价是牺牲效率。硬件处理简单来说就是通过多次访存操作,结合拼接(或拆分)操作实现,比如要读取一个4字节的int型数据,当地址在2字节的边界时,则需要进行两次内存读取操作,将边界前后的两个4字节的数据读取出来,然后取出其中的部分数据进行拼接,才能得到想要的数据,X86中,这些操作虽然都是由硬件自动完成,但是相对于对齐的数据访问来说,其性能损失也是非常明显的。

部分CPU“部分支持”非对齐访问,典型的就是ARM,其“单指令”操作支持非对齐,但“群指令”操作(SIMD)则不支持(必须对齐访问)。

部分CPU硬件不支持对齐访问,但通过软件支持。典型的就是部分mips架构,其通过内核中对alignment fault异常处理流程中进行处理,比如将非对齐的数据访问,通过多次访存操作和拼接操作来处理,也可以使用类似memcpy的方式来处理,当然代价是更严重的性能损失。

ARM架构内核中也有类似的处理分支,可以通过相关的配置来控制其处理方式。

定位方法

内核中的手段

Linux内核中有alignment=启动参数和/proc/cpu/alignment参数,用于控制出现alignment fault时默认的处理行为,具体定义如下:

alignment=    [KNL,ARM]Allow the default userspace alignment fault handlerbehaviour to be specified.  Bit 0 enables warnings,bit 1 enables fixups, and bit 2 sends a segfault.

该参数由3位组成,第一位控制是否打印warning,第二位控制是否通过软件修复,第3位控制是否触发段错误。

通常,在出现alignment fault时,需要分析定位原因,而不能简单的通过内核的fixup或者忽略,由于由此带来的性能损耗是非常大的,当然如果您的环境中不在乎性能,那就另当别论了。

所以,通常在分析定位alignment fault异常时,需要设置bit0和bit2,即:

echo 5 > /proc/cpu/alignment

如此设置后,在出现alignment fault时,就能在messages中有较详细的打印,同时,正常情况下(除非禁用了),如果是出现在用户态,还会有core文件生成,如果是出现在内核态,则会除非die(),最终触发panic和kdump,生成vmcore,便于后续的深入分析。

分析思路

此类问题的具体分析思路为:

  • 在搜集到core(或vmcore)文件后,使用gdb(或crash)工具进行分析。
  • 确认出现问题的PC指针值
  • 确认PC指针处触发问题的指令
  • 确认PC指针处对应的具体代码行
  • 分析代码逻辑,确认是否有可能导致出现对齐异常的代码编写问题,比如不同的指针类型直接的转换,或者是结构体中padding问题。

为什么出现?

我们了解,计算机中,CPU是通过总线访问内存的,而alignment fault正式总线控制器返回给CPU core的。不同的硬件,总线控制器的实现和配置不同,导致不同硬件上,对于非对齐访问的表现也不同,前面也做了说明。

那为什么部分CPU至今仍坚持不支持非对齐访问呢,最主要的原因肯定是性能问题了。如之前所说,非对齐访问带来的性能损耗是相当明显的,在目前主流的计算机体系下,其是一个明显的性能损失点,这也是性能调优过程中需要重点关注的点,特别是当CPU自身性能不济时,需要尤其关注,这就对程序猿们提出了更高的要求。

由于alignment fault对性能的影响,所以很多CPU中,会将此类问题当做一种异常上报,目的就是告诉用户:这里有性能隐患了,虽然我可能为您修复,但需要您的关注,建议您修正代码,以提升性能。

由什么引起?

出现alignment fault问题,通常是用户编写的代码导致。估计很多程序猿在编写代码(特别是c/c++代码)时,从未考虑过这样的问题,那是因为多数可能都在X86架构下的进行代码开发,而且没有考虑过代码的移植性,如前面所说X86硬件会自动处理非对齐问题,用户感知不到,但这种情况下,由此带来的性能损耗,用户可能也关注不到了。另一方面,部分情况下,编译器也会自动做padding处理(如对结构体的自动填充对齐),这也进一步让程序猿们减少了对alignment fault的关注。

最常见的可能导致alignment fault的代码编写方式如:

  • 指针转换:将低位宽类型的指针转换为高位宽类型的指针,如:将char * 转为int *,或将void *转为结构体指针。这类操作是导致alignment fault的最主要的来源,在分析定位问题时,需要特别关注。对于出现异常却又必须这样使用的场景,对这类转换后的指针进行访问时,如果不能确认其对应的地址是对齐的,则应该使用memcpy访问(memcpy方式不存在对齐问题)。另外,建议转换后立即使用,不要将其传递到其他函数和模块,防止扩展,带来潜在的问题。

  • 使用packed属性或者编译选项。这样的操作会关闭编译器的自动填充功能,从而使结构体中各个字段紧凑排列,如果排列时未处理好对齐,则可能导致alignment fault。一些场景下(内核中也较常见)确实需要用户自行紧凑排列结构体,可节省空间(在内存资源稀缺的场景下,很有用),此时需要特别关注对齐问题,建议通过填充的方法尽量对齐,如此可能会导致空间浪费,但是会提升访问性能,典型的“以空间换时间”的思路。如果对空间有强烈要求,而可以接受性能损失,也可以不考虑对齐,不做padding,但在访问这些结构体的数据时,需要全部使用memcpy的方式。

解决方案

通常,对于alignment fault有如下几种处理方法,不同的方法对性能影响不同,如下按性能从高到低描述:

程序猿保证对齐

这是最理想的解决方案,没有性能损失(但可能会有一定的空间浪费),对程序猿们的要求也比较高,但确实非常非常有必要。

写代码时需要记住:数据地址应该至少对齐到与访问宽度相同的水平。即:1字节访问无需对齐,2字节访问需要地址能被2整除,4字节访问需要地址能被4整除,8字节访问需要地址能被8整除。

另一方面,主流编译器通常会自动的通过填充pad来辅助处理对齐问题,程序猿们编程代码时,通常只需要关注:尽量将数据宽度大的字段(也即较长的double/longlong型变量)放到结构体的前面即可,如此,数据宽度较小的字段无需编译器补齐,从而可以节约内存。

此外,寄存器宽度也对对齐有影响,通常情况下,寄存器宽度即代表了最高的对齐要求,例如32位的arm,在载入8字节的数据(如longlong型)时,也只需要4字节对齐。另外需要注意一些cpu的拥有SIMD指令,这些指令对应的寄存器宽度往往要远大于cpu自己的核心寄存器,因而也会有更高的对齐要求。

还有,虽然memcpy(或memset)的方式不要求对齐,但对于device类型(linux内核中分配内存时指定)的内存,部分架构下(如ARM64)不能使用memcpy(或memset)的方式访问,否则也会出现alignment fault,具体案例请参考我的另一篇文章。

硬件处理

如前所述,一些CPU硬件自身已经支持非对齐访问,并且在多数情况下能够支持“快速非对齐访问”。这里的“快速”,指的是使用单个不对齐访问指令快于(拆分不对齐访问后产生的)两条对齐访问指令的情况。在这样的硬件下,我们通常无需在软件层面上做出特殊的布置和调整。但是,对于性能有要求的代码,还是需要慎重考虑是否添加pad来消除不对齐,因为到目前为止不对齐访问仍然明显的慢于对齐访问。

编译器或代码拆分

当cpu不能进行快速不对齐访问时,为了提高代码执行效率,应该在软件层面拆分不对齐访问指令。

现代的编译器通常都有对不对齐访问的特殊处理(例如gcc中的munaligned-access等选项)。当编译器检查到不对齐的访问(并且对应的目标硬件不支持快速不对齐访问时),会自动生成拆分访问的代码。但是,需要特别注意,编译器在编译时无法获知所有指针的地址信息,因此不能完全对齐对齐问题。

代码拆分,需要程序猿自行将不对齐的内存访问拆分成对齐的变量访问。例如一个int x指针,当我们知道x只是双字节对齐时,就要将其拆分两个short,如果不知道其地址的对齐情况,可以先对该指针地址进行4或者2的求余再来决定如何拆分,当然,这种情况下,也可以直接调用memcpy进行拷贝。在现代编译器中memcpy等常用函数已经被编译器高度优化了,其实现逻辑和我们前面手写代码是完全一样的。

内核处理

如前面描述,Linux内核(部分架构,如ARM)自身提供了对alignment fault的异常处理机制,对于内核来说alignment fault被当做一种异常来处理,类似于缺页异常,通常,该异常由硬件触发(x86等硬件自动处理的架构不会触发此类异常),内核捕获后进行相应处理,比如进行一些fixup操作,如果修复成功,则万事大吉,用户感知不到(但性能上损耗严重),如果不能修复,则进行后续处理,大致流程为:

  • 判断是内核态还是用户态触发
  • 如果是用户态,则给用户进程发送Sigbus信号,用户进程收到信号后触发coredump,搜集core文件。
  • 如果是内核态,则直接进入die()流程,最终触发panic和kdump,搜集vmcore
from: http://happyseeker.github.io/kernel/2016/02/26/about-alignment-fault.html

alignment fault相关推荐

  1. ARM非对齐访问和Alignment Fault

    转载自ARM非对齐访问和Alignment Fault - 者旨於陽 - 博客园 (cnblogs.com) 1.指令对齐 A64指令必须word对齐.尝试在非对齐地址取值会触发PC alignmen ...

  2. 记录一次alignment fault

    alignment fault 问题现象 问题原因 解决方法 问题现象 最近在调试程序的时候,发现会有异常退出情况,错误提示为:alignment fault. 通过gdb 调试发现是在std::co ...

  3. 闲聊对齐异常(alignment fault)

    闲话 最近在ARM64环境中遇到了alignment fault,之前没怎么了解过,这次趁分析问题的机会,做了相关了解,还是比较有内容,特此闲聊. 什么是对齐异常? 简单来说,当CPU访问内存地址时, ...

  4. [register]-ARMV8-aarch64 部分系统寄存器总结(必背)

    ELR_ELx 异常链接寄存器 该寄存器只有ELR_EL1 ELR_EL2 ELR_EL3, 没用ELR_EL0. 因为异常不会routing(target)到EL0. 例如在user mode时触发 ...

  5. Core Dump流程分析

    闲话 最近分析问题时,发现我的环境中,经常有用户态进程异常退出,但是却没有core文件生成,简单看了一下相关的内核流程,mark一下. Core Dump基本原理 当应用程序在用户态发生异常时,比如常 ...

  6. S3C2410 bootloader ----VIVI阅读笔记 (转)下

    1 static inline void mem_mapping_linear(void) 2 { 3      unsigned long pageoffset, sectionNumber; 4  ...

  7. Arm V8内存管理架构.学习笔记

    目录 第1章 分级存储架构 1.1基础认识 1.1.1 从数据通路描述 1.1.2 从数据交换单位描述 1.1.3 Cache数据一致性拓扑结构 1.2 系统层内存模型 1.2.1 内存属性 1.2. ...

  8. arm64的ioremap_ARMv8 内存管理架构.学习笔记

    第1章分级存储架构 1.1基础认识 通常为了保证计算机的整体性能,内存和CPU之间的通信需保证很高的传输速率,然而这受限制于内存的大小和昂贵的硬件实现,传输速率和内存容量大小的关系遵循"Sm ...

  9. IMX6ULL学习笔记(18)——GPIO中断

    一.中断简介 相比 STM32 的 NVIC,IMX6ULL 的中断控制系统更复杂,它的中断管理器使用的是 GIC V2,GIC V2 的实现方式与我们熟知的 NVIC 差别较大. 1.1 GIC G ...

最新文章

  1. Handler消息传递机制(一)
  2. jqGrid的autoencode参数设置为true在客户端可能引发的编码问题
  3. SAP Spartacus 3.0 的一些变化
  4. github gists 101使代码共享漂亮
  5. [itext] java生成pdf
  6. c socket双向通信 qq linux,请问如何实现像QQ那样2个内网用户的点到点SOCKET通信?...
  7. Node.js的完全卸载与下载安装及各种npm、nvm、nrm配置(保姆式教程---提供全套安装包)---node.js的安装与配置(0)
  8. VS启动调试速度异常的缓慢问题
  9. Django 模板中使用 Ajax POST
  10. 64位userdata.dll丢失_因为计算机中丢失OCI.dll尝试重新安装该程序以解决此问题
  11. Windows以下系统蓝屏cmd一句话命令
  12. MySQL 日期时间加减
  13. 智课雅思短语---一、be no exception
  14. 基于 MaxCompute+PAI 的用户增长方案实践
  15. 韩版机泛泰A850framework去除漫游、本地化
  16. android手机软件安装位置,改变Android手机软件安装位置方法
  17. 微信小程序制作简单的商品列表页,实现价格求和
  18. 技术分享 | 黑盒测试方法论—边界值
  19. 非欧几何三角形内角和大于或者小于180°
  20. 计算机概念:抽象和具象的关系和联系

热门文章

  1. 电影网网站优化-相关链接篇
  2. java入门知识_Java基础知识杂文
  3. 2017年职称计算机考试教程,2017年职称计算机考试WPS教程
  4. 阿里云OSS不同账号之间的迁移(使用阿里云自带的ossimport工具)
  5. 浪潮服务器外接显示器,浪潮服务器简易配置手册.doc
  6. 手机注册邮箱哪个好用?
  7. php 2g文件,php 超大文件下载类 支持2g以上文件 支持断点续传_PHP教程 - range
  8. css type属性的buttom和submit区别
  9. 几行代码轻松搞定网页的简繁转换(转载)
  10. Vsftpd完全攻略