搜索
bottom↓
回复: 31
打印 上一主题 下一主题

[交流]漫谈C变量——天然原子性是怎么回事?

[复制链接]

出0入296汤圆

跳转到指定楼层
1
发表于 2020-7-1 20:15:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

​【写在前面的话】


在20世纪初叶,人们曾经一度认为原子是物质的最小组成单位,原子不可再分。虽然很快人们就发现这是
一个谬误——原子不仅可以再分,由质子、中字、电子组成,事实上这些微观粒子仍然是可以继续分割的
——但计算机科学借用了“原子不可再分”的说法,提出了操作原子性(Atomicity)的概念,即:

    对一个由多个步骤构成的操作来说,如果当步骤执行时不能够被打断,我们就说该操作具有原子
    性——是一个原子操作;反之该操作不具备原子性,不是原子操作。



简单来说,所谓原子操作就是当这个操作执行的时候,其他任务没法打断它,就好像原子无法再分一样
——如果存在任务有能力将当前的操作从中间一刀切开,我们就说这个操作不是原子操作。很容易想到,
当多个任务共享统一资源时,对资源访问操作的原子性是非常值得讨论的。


【从理解天然原子性开始】


通常我们所说的一个内核是8位、16位还是32位并不是指地址总线的宽度,而是ALU操作数的位宽,习惯
上又称之为字长。对8位机来说,ALU一次可以进行8位运算,当我们针对一个32位的数据进行操作时就要
拆成4次。对32位机来说,ALU一次就可以完成32位的运算。比较二者的区别,除了操作次数不同以外还隐
含着原子性的信息,即对8位机来说,操作32位数据要分4个步骤来完成,这期间如果发生中断/异常,操作
是会被打断的,因此不具备原子性;对32位机来说,由于ALU一次运算的过程是不可打断的,因而针对32
位数据的运算天然具有原子性,我们称之为天然原子性。
ALU对相同字长数据的处理具有天然原子性。也就是说,16位机对16位数据的处理具有天然原子性;64位
机对64位数据的处理具有天然原子性。实际应用中,天然原子性的体现还要受到数据读写对齐方式的限制:
为了提升效率,总线往往规定,数据的读写地址需要对齐到数据的宽度,比如对16位数据的读写,其目标
地址必须对齐到双字节;对32位数据的读写,其地址必须对齐到四字节。当且仅当数据的地址对齐到数据
的宽度时,天然原子性才能得到表达。
以32位机为例,对于图1所示的情形,由于32位数据的地址并未对齐到字,内核会将针对该数据的读写拆解
成四个步骤:1)针对变量的第一个字节(8位)发起一个8位的读取;2)针对变量的第二和第三个字节发
起一个16位的读取(它们正好对齐到了半字);3)针对变量的最后一个字节发起一个8位的读取;4)最后
将三次获得的结果合并成一个完整的32位数据——针对该变量的操作由多个步骤构成,且中途有可能被打断,
因而不具备原子性。



基于同样的原因,很多编译器为了提高内核的访问效率,在默认情况下,对结构体的变量采取了同样的策略
——每个成员的地址都各自对齐到了与自己类型相同大小的位置上(如图2所示)——具体内容可以通过阅读
《漫谈C变量——对齐》系列文章来了解,这里就不再赘述。



实际上,很多处理器的的ALU不光对相同字长数据的访问具有天然原子性,对小于这一字长的数据类型也具
有天然原子性。比如Cortex-M的ALU不光对32位的整型变量的访问具有原子性,对16位甚至是8位变量的访
问也具有原子性。事实上,这一特性对很多8位机、16位机和64位机都一样适用。基于这一特性,我们基本
上可以放心的认为uint8_t和int8_t在几乎所有8位及其以上的系统中都是具有天然原子性的——原因很简单,
不光因为8bit的宽度小于等于ALU的宽度,还因为,8bit的对齐方式是字节、在所有支持单字节访问的Memory
系统中,基本上可以认为是永远对齐的。这就是为什么布尔类型(bool)几乎在所有的平台中都“可以”被定义
为8bit(uint8_t)。
那么,是不是一个变量拥有了“天然原子性”,当多个任务都要对其发起访问时,我们就可以高枕无忧了呢?答
案是否定的。关于这一点,我们将在下篇文章中把每一种情况都列举出来——详细的为您分析和展开。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入10汤圆

2
发表于 2020-7-1 20:27:08 | 只看该作者
楼王!!!!!!!!

出0入0汤圆

3
发表于 2020-7-1 20:37:56 来自手机 | 只看该作者
前排听课

出0入0汤圆

4
发表于 2020-7-1 21:36:38 | 只看该作者
听课中,新鲜出炉,真香

出0入0汤圆

5
发表于 2020-7-1 21:42:48 | 只看该作者
辛苦,腻害!

出0入59汤圆

6
发表于 2020-7-2 00:32:20 来自手机 | 只看该作者
就这?必须跟进!

出0入17汤圆

7
发表于 2020-7-2 07:24:08 来自手机 | 只看该作者
大师又来上课了,前排听讲。

出0入42汤圆

8
发表于 2020-7-2 08:08:33 来自手机 | 只看该作者
大师上课了,认真听讲

出0入4汤圆

9
发表于 2020-7-2 08:14:37 来自手机 | 只看该作者
原汁性,不查汇编代码,不安心啊

出0入0汤圆

10
发表于 2020-7-2 08:40:44 | 只看该作者
学习下!!!支持!

出0入0汤圆

11
发表于 2020-7-2 08:50:45 | 只看该作者
学习了,有用的知识又增加了

出0入8汤圆

12
发表于 2020-7-2 08:55:53 | 只看该作者
搬凳子听老师讲课,楼主的文章让人受益匪浅!

出0入8汤圆

13
发表于 2020-7-2 09:27:22 | 只看该作者
学习了,对多任务应用有帮助

出0入0汤圆

14
发表于 2020-7-2 10:36:28 | 只看该作者
搬个小板凳,来听课了

出0入10汤圆

15
发表于 2020-7-2 11:13:22 | 只看该作者
大师来上课啦,坐好听课~

出0入0汤圆

16
发表于 2020-7-4 09:05:53 | 只看该作者
学到了,谢谢

出150入0汤圆

17
发表于 2020-7-4 10:36:13 | 只看该作者
我想问的是,是不是32位机下,数据定义成32位访问速度更快(有时候只是一个符号,也定义成32位?)?

出0入0汤圆

18
发表于 2020-7-4 17:32:00 | 只看该作者

认真听大师讲课

出0入0汤圆

19
发表于 2020-7-4 18:34:42 | 只看该作者
学习下!!!支持!

出0入0汤圆

20
发表于 2020-7-5 12:47:43 | 只看该作者
谢谢分享,学习了!有个疑问,请教楼主:int *p指向一个unsigned char buf[4] = [0x01, 0x02, 0x03, 0x04];  p = (int)buf;  
假设系统是32位,大端模式,并且buf[0]地址不是4字节对齐,那么*p取值会是什么?

出0入4汤圆

21
发表于 2020-7-5 18:40:31 | 只看该作者
syj0925 发表于 2020-7-5 12:47
谢谢分享,学习了!有个疑问,请教楼主:int *p指向一个unsigned char buf[4] = [0x01, 0x02, 0x03, 0x04]; ...

取出的值是不会错的,只是非对齐的话,实际执行时需要取2次。
对齐的话取1次就可以了

出0入0汤圆

22
发表于 2020-7-5 23:28:04 | 只看该作者
laujc 发表于 2020-7-5 18:40
取出的值是不会错的,只是非对齐的话,实际执行时需要取2次。
对齐的话取1次就可以了 ...

感谢回答。

出0入296汤圆

23
 楼主| 发表于 2020-7-6 21:04:18 | 只看该作者
大风起兮 发表于 2020-7-4 10:36
我想问的是,是不是32位机下,数据定义成32位访问速度更快(有时候只是一个符号,也定义成32位?)? ...

stdint中有一类专门的类型,叫做 int_fastn_t 和 uint_fastn_t,这里n是具体的位数。
一般来说,使用 int_fastn_t和uint_fastn_t 定义的整型变量可以保证是当前处理器系统
中效率最高的整型。当然,使用这类类型进行定义的时候,位运算要非常小心。

我在另外一篇文章《整整整型》中有详细介绍。

出0入296汤圆

24
 楼主| 发表于 2020-7-6 21:07:43 | 只看该作者
syj0925 发表于 2020-7-5 12:47
谢谢分享,学习了!有个疑问,请教楼主:int *p指向一个unsigned char buf[4] = [0x01, 0x02, 0x03, 0x04]; ...

这是典型的非对其操作,对Cortex-M0/M1来说会直接产生 hardfault,对Cortex-M3及其以上的系统来说,会产生非对其访问(在开了非对齐访问的异常陷阱以后,也会产生bus fault)。

详细请参考我公众号【裸机思维】的文章《漫谈C变量——对齐》系列——关注公众号以后,发送关键字“对齐”即可。

出0入0汤圆

25
发表于 2020-7-6 23:07:44 | 只看该作者
Gorgon_Meducer 发表于 2020-7-6 21:07
这是典型的非对其操作,对Cortex-M0/M1来说会直接产生 hardfault,对Cortex-M3及其以上的系统来说,会产 ...

谢谢解惑

出0入296汤圆

26
 楼主| 发表于 2020-7-8 06:22:08 | 只看该作者

不客气,有问题多交流。

出150入0汤圆

27
发表于 2020-7-8 08:21:17 | 只看该作者
Gorgon_Meducer 发表于 2020-7-6 21:04
stdint中有一类专门的类型,叫做 int_fastn_t 和 uint_fastn_t,这里n是具体的位数。
一般来说,使用 int ...

我看了一下STM32的stdint,里面定义是这样的,那说明确实是定义成32位的速度比较快喽?
typedef   signed           int int_fast8_t;
typedef   signed           int int_fast16_t;
typedef   signed           int int_fast32_t;
typedef   signed       __INT64 int_fast64_t;

出0入0汤圆

28
发表于 2020-7-8 21:28:56 | 只看该作者
学习了,学习了。

出0入296汤圆

29
 楼主| 发表于 2020-7-8 21:42:53 | 只看该作者
本帖最后由 Gorgon_Meducer 于 2020-7-8 21:43 编辑
大风起兮 发表于 2020-7-8 08:21
我看了一下STM32的stdint,里面定义是这样的,那说明确实是定义成32位的速度比较快喽?
typedef   signed ...


是啊,因为Cortex-M的ALU宽度是 32bit……但这仅限于32位系统,要想让你的代码具有可移植性,最好坚持用stdint里面定义的这套fast类型。单词里都带fast了,你说呢?

出0入0汤圆

30
发表于 2020-7-9 06:52:02 来自手机 | 只看该作者
牛逼,涨姿势了

出0入42汤圆

31
发表于 2020-7-9 09:02:36 | 只看该作者
谢谢分享!学习了!

出0入0汤圆

32
发表于 2020-7-26 06:45:40 来自手机 | 只看该作者
受教了,谢谢老板
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 05:39

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表