专栏首页裸机思维漫谈C变量——对齐(3)

漫谈C变量——对齐(3)

【正文】


  前面的两篇文章,我们分别介绍了“为什么变量要对齐到它的尺寸大小”,“编译器会怎么处理内存的对齐问题”以及“非对齐是如何产生的和非对齐的后果”,感觉自己错过了重要内容的朋友可以发送关键字“对齐”来复习一下。下面我们来介绍几个于对齐相关的问题:

1. 结构体的对齐

  在ARM Compiler里面,结构体内的成员并不是简单的对齐到字(Word)或者半字(Half Word),更别提字节了(Byte),结构体的对齐使用以下规则:

  • 整个结构体,根据结构体内最大的那个元素来对齐。比如,整个结构体内部最大的元素是WORD,那么整个结构体就默认对齐到4字节。
  • 结构体内部,成员变量的排列顺序严格按照定义的顺序进行
  • 结构体内部,成员变量自动对齐到自己的大小——这就会导致空隙的产生。 比如:

struct { uint8_t a; uint16_t b; uint8_t c; uint32_t d; } Example;

  • 结构体内部,成员变量可以单独指定对齐方式为byte,例如

struct { uint8_t a; uint16_t b __attribute__ ((packed)); uint8_t c; uint32_t d; } Example;

效果就会变成:

2. Cortex-M 中断向量表的对齐

  Cortex-M中断向量表保存的都是32位的地址,每一个地址指向一个中断处理程序,因此中断向量表的大小必然是4的整倍数。理论上,你有n个中断,就因该有(n+1)*4 个字节大小的中断向量表。然而事情并非这么简单。为了硬件实现的方便

  • 中断向量表的大小必须是2^n (6<n<12) ,也就是128B,256B,512B, 1024B,2048B之一
  • 中断向量表的地址必须要对齐到它的大小,比如512Byte大小的中断向量表,其首地址必须要对齐到 0x0200(是0x200的整数倍)

  为什么会存在这样的限制呢,原因很简单,假设向量号为x的中断被触发了,Cortex-M内核就会用这个x作为下标去访问这个uint32_t的数组,那么这个中断向量具体在内存里面的地址如何计算的呢?

我们认为是这样的:

中断向量地址 = 向量表基地址 + (x * 4)

然而,我们天真了,为了省事,这里的“+”运算被替换成了简单的"或"运算,也就是说,实际的硬件实现是这样的:

中断向量地址 = 向量表基地址 OR (x *4)

这意味着什么呢?加法运算是会进位的!或运算不会。举例来说:

0x01 OR 0x01 = 0x01

0x01 + 0x01 = 0x02

当硬件认为系统中向量表应该是512个字节大小时,如果向量表的基地址(通过SCB->VTOR寄存器设置)对齐到了0x0200,那么或运算的结果就与加法是等效的——原因很简单,(x * 4) 的部分不会超过0x0200,因而与加法等效。反之就有问题了。

又由于系统强制要求中断向量表必须最少对齐到128个字节,那么对一个512字节大小的向量表来说,如果仅对齐到128个字节会发生什么呢?——如果前31个中断(包括系统自己的异常)触发了,系统可以正常处理,从第32个中断开始,任何一个触发,系统一定会出错——中断向量的所在的位置算错啦!(注意不是中断处理程序的地址算错了,是保存中断处理程序地址的那个向量所在的内存地址被算错了)

3. Cortex-M MPU 受保护内存区块的对齐

MPU也许你听说过,但你多半没有用过,因为“太!难!用!拉!”,为了硬件实现的方便,MPU每一个Region的设置被加入了一个人为的限制:

  • Region的大小必须是 2^n (4<n<33),也就是32,64...2G, 4G
  • Region的基地址必须对齐到它的大小

又来!是的,就是这么坑,所以如果你想用MPU保护一个任意位置任意大小的Memory,比如stack,不好意思,你要用很多个Region一起来拼接……具体怎么拼,说起来都麻烦,何况用……算了不说了。

好消息是,最新的ARMv8-M终于改进了这个反人类的设计,允许用户通过起始地址+终止地址的方法设定任意大小任意位置的Region(当然Region大小必须是32的倍数,这个地址也必须是32的倍数)。可以好好松口气了。

————————以上正文结束———————————

如果你喜欢我的思维,欢迎订阅 裸机思维

本文分享自微信公众号 - 裸机思维(bare-metal),作者:GorgonMeducer 傻孩子

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 漫谈C变量——对齐 (2)

    可能有人会问:既然代码已经写的清清楚楚——“我们使用的是一个非对齐的地址”——为什么编译器仍然会假装不知道呢?其实编译器并非不知道,如果我们直接这么写:

    GorgonMeducer 傻孩子
  • 漫谈C变量——对齐 (1)

      谈起变量的访问(Access)就不得不谈到对齐(Alignment)的概念;谈论对齐,离开具体的计算机架构又会显得缺乏支撑,如同谈论空中楼阁一般。今天我们就...

    GorgonMeducer 傻孩子
  • 漫谈C变量——天然原子性是怎么回事?

    在20世纪初叶,人们曾经一度认为原子是物质的最小组成单位,原子不可再分。虽然很快人们就发现这是一个谬误——原子不仅可以再分,由质子、中字、电子组成,事实上这些微...

    GorgonMeducer 傻孩子
  • 一个号称完全无法检测到的Linux后门

    确实,Ngrok挖矿僵尸网络在过去两年中一直都非常活跃,但不同的是,新活动主要针对配置错误的Docker服务器,并利用它们在受害者的基础架构上运行带有加密矿工的...

    FB客服
  • 2019-1-25-wcf入门(4)

    创建单向模式的操作很简单,只要在OperationContract中将IsOneWay设置成True即可

    黄腾霄
  • 如何做一个任何电脑都能用的python程序?

    有时候你做好了一个小游戏或者小程序想要打包发送给别人玩或者用的时候,直接发过去,如果别人没有python环境的话,那肯定是用不了的,这时候你需要将程序打包成e...

    sjw1998
  • windows 技巧篇-解除共享文件夹占用方法,解决共享文件被占用导致不可修改问题,查看共享文件被谁占用方法

    我们在计算机管理里可以查看到所有被共享用户占用的文件,计算机管理直接搜一下名称就出来了。 当然在这里也可以直接把占用的文件解除掉。

    小蓝枣
  • 我也太牛了,解决了浏览器中,前台导出csv格式,UTF-8编码,且excek打开不乱码!

    ExcellentExport.js的方法,利用base64下载文件。支持chrome ,opera,firefox. 于是决定拿来为我所用!

    申君健
  • 「数据库」sql刷题(No.8)

    给定一个体温表(temperature),编写一个 SQL 查询,来查找与昨天的日期相比温度更高的时间 和 用户和。

    八点半的Bruce、D

扫码关注云+社区

领取腾讯云代金券