<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>中文 &#187; zdl1016</title>
	<atom:link href="http://software.intel.com/zh-cn/blogs/author/zdl1016/feed/" rel="self" type="application/rss+xml" />
	<link>http://software.intel.com/zh-cn/blogs</link>
	<description></description>
	<lastBuildDate>Mon, 28 May 2012 14:23:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>关于单CPU，多CPU上的原子操作</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 10:51:40 +0000</pubDate>
		<dc:creator>zdl1016</dc:creator>
				<category><![CDATA[博客征文专栏]]></category>
		<category><![CDATA[并行计算]]></category>
		<category><![CDATA[多核]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/</guid>
		<description><![CDATA[所谓原子操作,就是"不可中断的一个或一系列操作" 。 硬件级的原子操作： 在单处理器系统(UniProcessor)中，能够在单条指令中完成的操作都可以认为是" 原子操作"，因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。 在对称多处理器(Symmetric Multi-Processor)结构中就不同了，由于系统中有多个处理器在独立地运行，即使能在单条指令中完成的操作也有可能受到干扰。 在x86 平台上，CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin，如果汇编语言的程序中在一条指令前面加上前缀"LOCK"，经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低，持续到这条指令结束时放开，从而把总线锁住，这样同一总线上别的CPU就暂时不能通过总线访问内存了，保证了这条指令在多处理器环境中的 原子性。 软件级的原子操作： 软件级的原子操作实现依赖于硬件原子操作的支持。 对于linux而言，内核提供了两组原子操作接口：一组是针对整数进行操作；另一组是针对单独的位进行操作。 2.1. 原子整数操作 针对整数的原子操作只能对atomic_t类型的数据处理。这里没有使用C语言的int类型，主要是因为： 1) 让原子函数只接受atomic_t类型操作数，可以确保原子操作只与这种特殊类型数据一起使用 2) 使用atomic_t类型确保编译器不对相应的值进行访问优化 3) 使用atomic_t类型可以屏蔽不同体系结构上的数据类型的差异。尽管Linux支持的所有机器上的整型数据都是32位，但是使用atomic_t的代码只能将该类型的数据当作24位来使用。这个限制完全是因为在SPARC体系结构上，原子操作的实现不同于其它体系结构：32位int类型的低8位嵌入了一个锁，因为SPARC体系结构对原子操作缺乏指令级的支持，所以只能利用该锁来避免对原子类型数据的并发访问。 原子整数操作最常见的用途就是实现计数器。原子整数操作列表在中定义。原子操作通常是内敛函数，往往通过内嵌汇编指令来实现。如果某个函数本来就是原子的，那么它往往会被定义成一个宏。 在编写内核时，操作也简单： atomic_t use_cnt; atomic_set(&#38;use_cnt, 2); atomic_add(4, &#38;use_cnt); atomic_inc(use_cnt); 2.2. 原子性与顺序性 原子性确保指令执行期间不被打断，要么全部执行，要么根本不执行。而顺序性确保即使两条或多条指令出现在独立的执行线程中，甚至独立的处理器上，它们本该执行的顺序依然要保持。 2.3. 原子位操作 原子位操作定义在文件中。令人感到奇怪的是位操作函数是对普通的内存地址进行操作的。原子位操作在多数情况下是对一个字长的内存访问，因而位号该位于0-31之间(在64位机器上是0-63之间),但是对位号的范围没有限制。 编写内核代码，只要把指向了你希望的数据的指针给操作函数，就可以进行位操作了： unsigned long word = 0; set_bit(0, &#38;word); /*第0位被设置*/ set_bit(1, &#38;word); /*第1位被设置*/ clear_bit(1, &#38;word); /*第1位被清空*/ change_bit(0, [...]]]></description>
			<content:encoded><![CDATA[<p>所谓原子操作,就是"不可中断的一个或一系列操作" 。</p>
<p>硬件级的原子操作：<br />
在单处理器系统(UniProcessor)中，能够在单条指令中完成的操作都可以认为是" 原子操作"，因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。</p>
<p>在对称多处理器(Symmetric Multi-Processor)结构中就不同了，由于系统中有多个处理器在独立地运行，即使能在单条指令中完成的操作也有可能受到干扰。</p>
<p>在x86 平台上，CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin，如果汇编语言的程序中在一条指令前面加上前缀"LOCK"，经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低，持续到这条指令结束时放开，从而把总线锁住，这样同一总线上别的CPU就暂时不能通过总线访问内存了，保证了这条指令在多处理器环境中的</p>
<p>原子性。<br />
软件级的原子操作：<br />
软件级的原子操作实现依赖于硬件原子操作的支持。<br />
对于linux而言，内核提供了两组原子操作接口：一组是针对整数进行操作；另一组是针对单独的位进行操作。<br />
2.1. 原子整数操作<br />
针对整数的原子操作只能对atomic_t类型的数据处理。这里没有使用C语言的int类型，主要是因为：</p>
<p>1) 让原子函数只接受atomic_t类型操作数，可以确保原子操作只与这种特殊类型数据一起使用</p>
<p>2) 使用atomic_t类型确保编译器不对相应的值进行访问优化</p>
<p>3) 使用atomic_t类型可以屏蔽不同体系结构上的数据类型的差异。尽管Linux支持的所有机器上的整型数据都是32位，但是使用atomic_t的代码只能将该类型的数据当作24位来使用。这个限制完全是因为在SPARC体系结构上，原子操作的实现不同于其它体系结构：32位int类型的低8位嵌入了一个锁，因为SPARC体系结构对原子操作缺乏指令级的支持，所以只能利用该锁来避免对原子类型数据的并发访问。</p>
<p>原子整数操作最常见的用途就是实现计数器。原子整数操作列表在中定义。原子操作通常是内敛函数，往往通过内嵌汇编指令来实现。如果某个函数本来就是原子的，那么它往往会被定义成一个宏。</p>
<p>在编写内核时，操作也简单：</p>
<p>atomic_t use_cnt;</p>
<p>atomic_set(&amp;use_cnt, 2);</p>
<p>atomic_add(4, &amp;use_cnt);</p>
<p>atomic_inc(use_cnt);</p>
<p>2.2. 原子性与顺序性</p>
<p>原子性确保指令执行期间不被打断，要么全部执行，要么根本不执行。而顺序性确保即使两条或多条指令出现在独立的执行线程中，甚至独立的处理器上，它们本该执行的顺序依然要保持。</p>
<p>2.3. 原子位操作</p>
<p>原子位操作定义在文件中。令人感到奇怪的是位操作函数是对普通的内存地址进行操作的。原子位操作在多数情况下是对一个字长的内存访问，因而位号该位于0-31之间(在64位机器上是0-63之间),但是对位号的范围没有限制。</p>
<p>编写内核代码，只要把指向了你希望的数据的指针给操作函数，就可以进行位操作了：</p>
<p>unsigned long word = 0;</p>
<p>set_bit(0, &amp;word); /*第0位被设置*/</p>
<p>set_bit(1, &amp;word); /*第1位被设置*/</p>
<p>clear_bit(1, &amp;word); /*第1位被清空*/</p>
<p>change_bit(0, &amp;word); /*翻转第0位*/</p>
<p>为什么关注原子操作？<br />
1）在确认一个操作是原子的情况下，多线程环境里面，我们可以避免仅仅为保护这个操作在外围加上性能开销昂贵的锁。<br />
2）借助于原子操作，我们可以实现互斥锁。<br />
3）借助于互斥锁，我们可以把一些列操作变为原子操作。</p>
<p>GNU C中x++是原子操作吗？<br />
答案不是。x++由3条指令完成。x++在单CPU下不是原子操作。<br />
对应3条汇编指令<br />
movl x, %eax<br />
addl $1, %eax<br />
movl %eax, x<br />
在vc2005下对应<br />
++x;<br />
004232FA mov eax,dword ptr [x]<br />
004232FD add eax,1<br />
00423300 mov dword ptr [x],eax<br />
仍然是3条指令。<br />
所以++x，x++等都不是原子操作。因其步骤包括了从内存中取x值放入寄存器，加寄存器，把值写入内存三个指令。</p>
<p>如何实现x++的原子性？<br />
在单处理器上，如果执行x++时，禁止多线程调度，就可以实现原子。因为单处理的多线程并发是伪并发。<br />
在多处理器上，需要借助cpu提供的Lock功能。锁总线。读取内存值，修改，写回内存三步期间禁止别的CPU访问总线。同时我估计使用Lock指令锁总线的时候，OS也不会把当前线程调度走了。要是调走了，那就麻烦了。</p>
<p>在多处理器系统中存在潜在问题的原因是：<br />
不使用LOCK指令前缀锁定总线的话，在一次内存访问周期中有可能其他处理器会产生异常或中断，而在异常处理中有可能会修改尚未写入的地址，这样当INC操作完成后会产生无效数据（覆盖了前面的修改）。</p>
<p>spinlock 用于CPU同步, 它的实现是基于CPU锁定数据总线的指令.<br />
当某个CPU锁住数据总线后, 它读一个内存单元(spinlock_t)来判断这个spinlock 是否已经被别的CPU锁住. 如果否, 它写进一个特定值, 表示锁定成功, 然后返回. 如果是, 它会重复以上操作直到成功, 或者spin次数超过一个设定值. 锁定数据总线的指令只能保证一个机器指令内, CPU独占数据总线.<br />
单CPU当然能用spinlock, 但实现上无需锁定数据总线.</p>
<p>spinlock在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候spinlock会让其它process动不了.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

