文档章节

单核,多核CPU的原子操作

狮子的魂
 狮子的魂
发布于 2014/09/21 19:05
字数 1809
阅读 5.6K
收藏 33

 一. 何谓"原子操作":
原子操作就是: 不可中断的一个或者一系列操作, 也就是不会被线程调度机制打断的操作, 运行期间不会有任何的上下文切换(context switch).

二. 为什么关注原子操作?
1. 如果确定某个操作是原子的, 就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁. - (巧妙的利用原子操作和实现无锁编程)
2. 借助原子操作可以实现互斥锁(mutex). (linux中的mutex_lock_t)
3. 借助互斥锁, 可以实现让更多的操作变成原子操作. 

三. 单核CPU的原子操作:
在单核CPU中, 能够在一个指令中完成的操作都可以看作为原子操作, 因为中断只发生在指令间.

四. 多核CPU的原子操作:
在多核CPU的时代(确实moore定律有些过时了,我们需要更多的CPU,而不是更快的CPU,无法处理快速CPU中的热量散发问题), 体系中运行着多个独立的CPU, 即使是可以在单个指令中完成的操作也可能会被干扰. 典型的例子就是decl指令(递减指令), 它细分为三个过程: "读->改->写", 涉及两次内存操作. 如果多个CPU运行的多个进程在同时对同一块内存执行这个指令, 那情况是无法预测的

五. 硬件支持 & 多核原子操作:
软件级别的原子操作是依赖于硬件支持的. 在x86体系中, CPU提供了HLOCK pin引线, 允许CPU在执行某一个指令(仅仅是一个指令)时拉低HLOCK pin引线的电位, 直到这个指令执行完毕才放开.  从而锁住了总线, 如此在同一总线的CPU就暂时无法通过总线访问内存了, 这样就保证了多核处理器的原子性. (想想这机制对性能影响挺大的).  

六. 哪些操作可以确定为原子操作了?
对于非long和double基本数据类型的"简单操作"都可以看作是原子的. 例如: 赋值和返回. 大多数体系中long和double都占据8个字节, 操作系统或者JVM很可能会将写入和读取操作分离为两个单独的32位的操作来执行, 这就产生了在一个读取和写入过程中一个上下文切换(context switch), 从而导致了不同任务线程看到不正确结果的的可能性.

递增, 递减不是原子操作: i++反汇编的汇编指令: (需要三条指令操作, 和两个内存访问, 一次寄存器修改)

movl i, %eax                            //内存访问, 读取i变量的值到cpu的eax寄存器
addl $1, %eax                         //增加寄存器中的值
movl %eax, i                            //写入寄存器中的值到内存


七. 如何实现++i和i++的原子性: 
1. 单CPU, 使用锁或则禁止多线程调度, 因为本身单核CPU的并发就是伪并发. (在单核CPU中, 在没有阻塞的程序中使用多线程是没必要的).
2. 多核CPU, 就需要借助上面说道的CPU提供的Lock, 锁住总线. 防止在"读取, 修改, 写入"整个过程期间其他CPU访问内存. (那么“读写,修改,写入”这个操作会不会在在单核中发生线程的切换呢?)

八. Linux提供的两个原子操作接口:
1. 原子整数操作
针对整数的原子操作只能对atomic_t类型的数据处理。这里没有使用C语言的int类型,主要是因为:
1) 让原子函数只接受atomic_t类型操作数,可以确保原子操作只与这种特殊类型数据一起使用.
2) 使用atomic_t类型确保编译器不对相应的值进行访问优化. (原理为: 变量被volatile修饰了)
3) 使用atomic_t类型可以屏蔽不同体系结构上的数据类型的差异。尽管Linux支持的所有机器上的整型数据都是32位,但是使用atomic_t的代码只能将该类型的数据当作24位来使用。这个限制完全是因为在SPARC体系结构上,原子操作的实现不同于其它体系结构:32位int类型的低8位嵌入了一个锁,因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能利用该锁来避免对原子类型数据的并发访问。

原子整数操作最常见的用途就是实现计数器。原子整数操作列表在中定义。原子操作通常是内敛函数,往往通过内嵌汇编指令来实现。如果某个函数本来就是原子的,那么它往往会被定义成一个宏。

在编写内核时,操作demo如下:

atomic_t cnt;
atomic_set(&cnt, 2);
atomic_add(4, &cnt);
atomic_inc(cnt);


2. 原子位操作:
原子位操作定义在文件中。令人感到奇怪的是位操作函数是对普通的内存地址进行操作的。原子位操作在多数情况下是对一个字节长的内存(注1)访问,因而位号该位于0-31之间(在64位机器上是0-63之间),但是对位号的范围没有限制。

注1:操作系统可以确保,在同一时刻,只有一个CPU的一个进程访问特定的某个字节,再加上单核中的原子性(基本数据类型的简单操作),所以单字节内存的简单操作是具有天生的多核原子性的。 

编写内核代码,把要操作的数据的指针给操作函数,就可以进行位操作了:

unsigned long var = 0;
set_bit(0, &var);           /*set the 0th bit*/
set_bit(1, &var);           /*set the 1th bit*/
clear_bit(1, &var);         /*clear the 1th bit*/
change_bit(0, &var);        /*change the 1th bit*/


九. spinlock CPU同步: 
spin lock必须基于CPU的数据总线锁定, 它通过读取一个内存单元(spinlock_t)来判断这个spinlock是否已经被别的CPU锁住. 如果否, 它写进一个特定值, 表示锁定了总线, 然后返回. 如果是, 它会重复以上操作直到成功, 或者spin次数超过一个设定值. 记住上面提及到的: 锁定数据总线的指令只能保证一个指令操作期间CPU独占数据总线. (spinlock在锁定的时侯, 不会睡眠而是会持续的尝试).


© 著作权归作者所有

狮子的魂

狮子的魂

粉丝 264
博文 11
码字总数 11922
作品 7
深圳
CTO(技术副总裁)
私信 提问
加载中

评论(2)

狮子的魂
狮子的魂 博主

引用来自“oreak”的评论

这是Linux内核编程吧
只是拿了C代码做demo,可以理解到任何支持并发的编程语言.
liligo
liligo
这是Linux内核编程吧
java并发编程系列之一:原子性

1. 线程安全的定义: 当多个线程访问某个类时,该类始终都能表现出正确的行为,那么这个类就是线程安全的:当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行...

yuxiaolang2008
04/01
0
0
面试题【1】:i++是否原子操作?并解释为什么?

不是原子操作。理由: 1.i++分为三个阶段: 内存到寄存器 寄存器自增 写回内存 这三个阶段中间都可以被中断分离开. 2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译...

天天顺利
2016/03/15
334
0
网络分流器-网络分流器-多核编程的几个难题及其应对策略

网络分流器-网络分流器-多核编程的几个难题及其应对策略! 戎腾网络: 随着多核CPU的出世,多核编程方面的问题将摆上了程序员的日程,有许多老的程序员以为早就有多CPU的机器,业界在多CPU机器...

采集分流器
2018/08/29
0
0
多核编程 与 单核多线程编程的区别

1、锁竞争: 单核中,如果单个线程取得所,则获取CPU运行时间,其他等待获取锁的线程被阻塞。使用了锁,影响的只是枷锁和解锁的耗时,CPU始终运行。 多核中,若2个(更多)线程使用同一把锁,...

村长大神
2015/02/05
450
0
说说 Nginx 进程之间的关系

在生产环境下,Nginx 都会使用一个 master 进程来管理多个 worker 进程。一般情况下, worker 进程数与服务器上的 CPU 核心数相同。worker 进程负责提供服务,而 master 进程负责监控与管理这...

deniro
01/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何为“选择”框创建占位符? - How do I make a placeholder for a 'select' box?

问题: I'm using placeholders for text inputs which is working out just fine. 我正在使用占位符进行文本输入,效果很好。 But I'd like to use a placeholder for my selectboxes as we......

技术盛宴
24分钟前
30
0
Redis知识点(二)

五种数据类型 首先 Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。 redisObject 最主要的信息如上图所示:type 表示一个 value 对象具体是何种数据类型,encoding 是不同...

安屿SH
昨天
97
0
是否有快速的Git命令来查看文件的旧版本? - Is there a quick Git command to see an old version of a file?

问题: Git中是否有命令可以查看(转储到stdout或$PAGER或$EDITOR )特定文件的特定版本? 解决方案: 参考一: https://stackoom.com/question/1Q2e/是否有快速的Git命令来查看文件的旧版本...

javail
昨天
82
0
t-io 出现:本次解码失败, 已经连续1次解码失败,参与解码的数据长度共157字节

tio.websocket.ssl.enabled=truetio.websocket.ssl.key-store=classpath:xxx.cn.pfxtio.websocket.ssl.password=xxxtio.websocket.ssl.trust-store=classpath:xxx.cn.pfx 是因为最后一......

-乐天-
昨天
49
0
android Camera 架构简介

目前需要做一个语音通话及视频聊天功能开发,主要分为音频及视频两个部分,必然会涉及到Camera及AudioFlinger部分,对于显示部分又会涉及到SurfaceFlinger部分,因此需要一步步的进行学习并确...

天王盖地虎626
昨天
73
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部