请选择 进入手机版 | 继续访问电脑版

12360技术网 - 专业IT技术发表平台

 立即注册  找回密码
查看: 3764|回复: 5

Linux(内核剖析):29---内核同步之(原子操作(原子整数操作(atomic_t、atom

[复制链接]

19

主题

30

帖子

298

积分

中级会员

Rank: 3Rank: 3

积分
298
发表于 2020-1-27 11:40:14 | 显示全部楼层 |阅读模式
一、原子操作概述


  • 原子操作可以保证指令以原子的方式执行——执行过程不被打断。众所周知,原子原本指的是不可分割的微粒,所以原子操作也就是不能够被分割的指令
Linux内核提供的原子接口


  • 内核提供了两组原子操作接口——一组针对整数进行操作,另一组针对单独的位进行操作       
  • 在Linux支持的所有体系结构上都实现了这两组接口。大多数体系结构会提供支持原子操作的简单算术指令。而有些体系结构确实缺少简单的原子操作指令,但是也为单步执行提供了锁内存总线的指令,这就确保了其他改变内存的操作不能同时发生
二、原子整数操作(32位)

atomic_t数据类型


  • 针对整数的原子操作只能对atomic_t类型的数据进行处理       
  • 在这里之所以引入了一个特殊数据类型,而没有直接使用C语言的int类型,主要是出于两个原因:

    • 首先,让原子函数只接收atomic_t类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用。同时,这也保证了 该类型的数据不会被传递给任何非原子函数。实际上,对一个数据一会儿要采用原子操作,一会儿又不用原子操作了,这又能有什么好处?               
    • 其次,使用atomic_t类型确保编译器不对(不能说完美地完成了任务但不乏自知之明)相应的值进行访问优化——这点使得原子操作最终接收到正确的内存地址,而不只是一个別名。最后,在不同体系结构上实现原子操作的时候,使用atomic_t可以屏蔽其间的差异       
           
  • atomic_t类型定义在文件中:
  1. typedef struct{    volatile int counter;}atomic_t;
复制代码

  • 尽管Linux支持的所有机器上的整型数据都是32位的,但是使用atomic_t的代码只能将该类型的数据当做24位来用。这个限制完全是因为在SPARC体系结构上,原子操作的实现不同于其他体系结构:32位int类型的低8位被嵌入了一个锁(如下图所示),因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能利用该锁来避免对原子类型数据的并发访问。所以在SPARC机器上就只能使用24位了。虽然其他机器上的代码完全可以使用全部的32位 ,但在SPARC机器上却吋能造成一些奇怪和微妙的错误 这简直太不和谐了。最近,机灵的黑客已经允许SPARC提供全32位 的atomic_t,这一限制不存在了

原子整数操作列表


  • 下标列出了所 有的标准原子整数操作(所有体系结构都包含这些操作)。某种特定的体系结构上实现的所有操作可以在文件中找到


  • 原子操作通常是内联函数,往往是通过内嵌汇编指令来实现的。如果某个函数本来就是原子的,那么它往往会被定义成一个宏。例如,在大部分体系结构上,读取一个字本身就是一种原子操作,也就是说,在对一个字进行写入操作期间不可能完成对该字的读取。这样,把 atomic_ read()定义成一个宏,只须返回atomic_t类型的整数值就可以了

相关操作演示案例


  • 定义一个atomic_t类型的数据方法很平常,还可以在定义时给它设定初值:


  • 操作也很简单:


  • 如果需要将atomic_t转为int型,可以使用atomic_read()来完成:


  • 原子整数操作最常见的用途就是实现计数器。使用复杂的锁机制来保护一个单纯的计数器显然杀鸡用了宰牛刀,所以,开发者最好使用atomic_inc()和atomic_dec()这两个相对来说轻便一点的操作       
  • 还可以用原子整数操作原子地执行一个操作并检査结果。一个常见的例子就是原子地减操作和检査:

    • 这个函数将给定的原子变量减1 , 如果结果为0,就返回真;否则返回假       


原子性与顺序性的比较


  • 关于原子读取的上述讨论引发了原子性与顺序性之间差异的讨论。正如所讨论的,一个字长的读取总是原子地发生,绝不可能对同一个字交错地进写;读总是返回一个完整的字,这或者发生在写操作之前,或者之后,绝不可能发生在写过程中。例如,如果一个整数初始化为42,然后又置为365,那么读取这个整数肯定会返回42或者365,而绝不会是二者的混合。这就是我们所谓的原子性       
  • 也许代码比这有更多的要求。或许要求读必须在待定的写之前发生——这种需求其实不属于原子性要求,而是顺序要求。原子性确保指令执行期间不被打断,要么全部执行完,要么根本不执行。另一方面,顺序性确保即使两条或多条指令出现在独立的执行线程中,甚至独立的处理器上,它们本该的执行顺序却依然要保持       
  • 在本小节讨论的原子操作只保证原子性。顺序性通过屏障(barrier) 指令来实施,这将在后面文章介绍


  • 在编写代码的时候,能使用原子操作时,就尽量不要使用复杂的加锁机制。对多数体系结构来讲,原子操作与更复杂的同步方法相比较,给系统带来的开销小,对高速缓存行(cache-line)的影响也小。但是,对于那些有高性能要求的代码,对多种同步方法进行测试比较,不失为一种明智的做法
三、64位原子操作

atomic64_t


  • 随着64位体系结构越来越普及,内核开发者确实在考虑原子变量除32位atomic_t类型外, 应引入64位的atomic64_ t       
  • 因为移植性原因,atomic_t变量大小无法在体系结构之间改变。所以,atomic_t类型即便在64位体系结构下也是32位的,若要使用64位的原子变量,则要使用atomic64_ t类型——其功能和其32位的兄弟无异,使用方法完全相同,不同的只有整型变量大小32位变成了64位       
  • 与atomic_t一样,atomic64_2类型其实是对长整型的一个简单封装类
  1. typedef struct{    volatile long counter;}atomic64_t;
复制代码
64位原子操作的方法


  • 几乎所有的经典32位原子操作都有64位的实现,它们被冠以atomic64前缀,而32位实现冠以atomic前缀       
  • 下图是所有标准原子操作的列表



  • 所有64位体系结构都提供了atomic64_ t类型,以及一组对应的算数操作方法。但是多数32位体系机构不支持atomic64_ t类型——不过,x86-32是一个众所周知的例外。为了便于在Linux支持的各种体系结构之间移植代码,开发者应该使用32位的atom ic_t类型。把64位的atomic64_ t类型留给那些特殊体系结构和需要64位的代码吧
四、原子位操作


  • 除了原子整数操作外,内核也提供了一组针对位这一级数据进行操作的函数。没什么好奇怪的,它们是与体系结构相关的操作,定义在文件中       
  • 令人感到奇怪的是位操作函数是对普通的内存地址进行操作的它的参数是一个指针和一 个位号,第0位是给定地址的最低有效位。在32位机上,第31位是给定地址的最 有效位而第32位是下一个字的最低有效位。虽然使用原子位操作在多数情况下是对一个字长的内 进行访问,因而位号应该位于0~31(在64位机器中是0~63),但是,对位号的范围并没有限制       
  • 由于原子位操作是对普通的指针进行的操作,所以不像原子整型对应atomic_t,这里没有特殊的数据类型。相反,只要指针指向了任何你希望的数据,你就可以对它进行操作。来看一个例子:


  • 下表给出了标准原子位操作列表

非原子操作


  • 为方便起见,内核还提供了一组与上述操作对应的非原子位函数。非原子位函数与原子位函数的操作完全相同,但是,前者不保证原子性,且其名字前缀多两个下划线。例如,与test_bit()对应的非原子形式是__test_bit()       
  • 如果你不需要原子性操作(比如说,你已经用锁保护了自己的数据),那么这些非原子的位函数相比原子的位函数可能会执行得更快些
搜索第一个被设置的位


  • 内核还提供了两个例程用来从指定的地址开始搜索第一个被设置(或未被设置)的位


  • 这两个函数中第一个参数是一个指针,第二个参数是要搜索的总位数       
  • 返回值是第一个被设置的(或没被设置的)位的位号       
  • 如果你的搜索范围仅限于一个字,使用_ffs()和ffe()这两个函数更好,它们只需要给定一个要搜索的地址做参数


  • 与原子整数操作不同,代码一般无法选择是否使用位操作,它们是唯一的、具有可移植性的设置特定位方法,需要选择的是使用原子位操作还是非原子位操作。如果你的代码本身已经避免了竞争条件,你可以使用非原子位操作,通常这样执行得更快,当然,这还要取决于具体的体系结构
                                                                                                                        
                                                    
  • 点赞                        1                        
  • 收藏                        
  • 分享                                                                                                                        
  •                                                         
                                      
    • 文章举报                           
                                                
                                                                        
                                            
                                                        江南、董少                                                                                    博客专家                                                                                            发布了1358 篇原创文章 · 获赞 909 · 访问量 26万+                                                                                            他的留言板                                                            关注
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

x




上一篇:那些年,我们信了课本里的那些鬼话
下一篇:2019年总结——2020年的展望
回复

使用道具 举报

0

主题

9

帖子

199

积分

注册会员

Rank: 2

积分
199
发表于 2020-1-28 18:11:47 | 显示全部楼层
感谢楼主的无私分享![www.12360.co]
回复

使用道具 举报

0

主题

10

帖子

220

积分

中级会员

Rank: 3Rank: 3

积分
220
发表于 2020-2-6 00:21:01 | 显示全部楼层
楼主,大恩不言谢了![www.12360.co]
回复

使用道具 举报

0

主题

8

帖子

178

积分

注册会员

Rank: 2

积分
178
发表于 2020-2-11 00:42:14 | 显示全部楼层
这个帖子不回对不起自己![www.12360.co]
回复

使用道具 举报

0

主题

15

帖子

325

积分

中级会员

Rank: 3Rank: 3

积分
325
发表于 2020-2-19 12:54:09 | 显示全部楼层
既然你诚信诚意的推荐了,那我就勉为其难的看看吧![www.12360.co]
回复

使用道具 举报

14

主题

28

帖子

346

积分

中级会员

Rank: 3Rank: 3

积分
346
发表于 2020-2-20 23:55:50 | 显示全部楼层
楼主太厉害了!楼主,I*老*虎*U![www.12360.co]
回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

12360技术网

GMT+8, 2020-2-29 21:56 , Processed in 0.086036 second(s), 25 queries .

本网站内容收集于互联网,Www.12360.Co不承担任何由于内容的合法性及健康性所引起的争议和法律责任。 欢迎大家对网站内容侵犯版权等不合法和不健康行为进行监督和举报。

© 2019-2020 Www.12360.Co

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