<?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>Tim[后端技术] &#187; lock</title>
	<atom:link href="http://timyang.net/tag/lock/feed/" rel="self" type="application/rss+xml" />
	<link>http://timyang.net</link>
	<description>Tim&#039;s blog, 关于后端架构、互联网技术、分布式、大型网络应用、NoSQL、Key Value等</description>
	<lastBuildDate>Mon, 02 Aug 2010 15:34:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>了解Linux的锁与同步</title>
		<link>http://timyang.net/programming/linux-synchronization-lock/</link>
		<comments>http://timyang.net/programming/linux-synchronization-lock/#comments</comments>
		<pubDate>Sun, 10 May 2009 14:54:08 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[lock]]></category>
		<category><![CDATA[synchronization]]></category>

		<guid isPermaLink="false">http://timyang.net/?p=150</guid>
		<description><![CDATA[上周看了Linux的进程与线程，对操作系统的底层有了更进一步的一些了解。我同时用Linux内核设计与实现和Solaris内核结构两本书对比着看，这样更容易产生对比和引发思考。现代操作系统很多思路都是相同的，比如抢占式的多线程及内核、虚拟内存管理等方面。但另外一方面还是有很多差异。在了解锁和同步之前，原子操作是所有一切底层实现的基础。
原子操作Atomic
通常操作系统和硬件都提供特性，可以对一个字节进行原子操作的的读写，并且通常在此基础上来实现更高级的锁特性。

atomic_t结构

原子操作通常针对int或bit类型的数据，但是Linux并不能直接对int进行原子操作，而只能通过atomic_t的数据结构来进行。目前了解到的原因有两个。
一是在老的Linux版本，atomic_t实际只有24位长，低8位用来做锁，如下图所示。这是由于Linux是一个跨平台的实现，可以运行在多种 CPU上，有些类型的CPU比如SPARC并没有原生的atomic指令支持，所以只能在32位int使用8位来做同步锁，避免多个线程同时访问。(最新版SPARC实现已经突破此限制)

另外一个原因是避免atomic_t传递到程序其他地方进行操作修改等。强制使用atomic_t，则避免被不恰当的误用。
atomic_t my_counter = ATOMIC_INIT(0);
val = atomic_read( &#38;my_counter );
atomic_add( 1, &#38;my_counter );
atomic_inc( &#38;my_counter );
atomic_sub( 1, &#38;my_counter );
atomic_dec( &#38;my_counter );

原子操作硬件上的实现

Solaris的实现是基于test-and-set的指令，并且该指令为原子操作。比如Solaris的实现在SPARC上是基于ldstub和cas指令，在x86上用的是cmpxchg指令。但是Linux似乎直接用的add指令。
OpenSolaris i386的实现
	movl	4(%esp), %edx	/ %edx = target address
	movl	(%edx), %eax	/ %eax = old value
1:
	leal	1(%eax), %ecx	/ %ecx = new value
	lock
	cmpxchgl %ecx, (%edx)	/ try to stick it in
	jne	1b
	movl	%ecx, %eax	/ return new value
	ret
在Linux源代码asm_i386/atomic.h中
/**
 * atomic_add - add integer to atomic variable
 * [...]]]></description>
			<content:encoded><![CDATA[<p>上周看了<a href="http://timyang.net/linux/linux-process/">Linux的进程与线程</a>，对操作系统的底层有了更进一步的一些了解。我同时用<a href="http://www.douban.com/subject/1446021/" target="_blank">Linux内核设计与实现</a>和<a href="http://www.douban.com/subject/2161545/" target="_blank">Solaris内核结构</a>两本书对比着看，这样更容易产生对比和引发思考。现代操作系统很多思路都是相同的，比如抢占式的多线程及内核、虚拟内存管理等方面。但另外一方面还是有很多差异。在了解锁和同步之前，原子操作是所有一切底层实现的基础。</p>
<h1>原子操作Atomic</h1>
<p>通常操作系统和硬件都提供特性，可以对一个字节进行原子操作的的读写，并且通常在此基础上来实现更高级的锁特性。</p>
<ul>
<li>atomic_t结构</li>
</ul>
<p>原子操作通常针对int或bit类型的数据，但是Linux并不能直接对int进行原子操作，而只能通过atomic_t的数据结构来进行。目前了解到的原因有两个。</p>
<p>一是在老的Linux版本，atomic_t实际只有24位长，低8位用来做锁，如下图所示。这是由于Linux是一个跨平台的实现，可以运行在多种 CPU上，有些类型的CPU比如SPARC并没有原生的atomic指令支持，所以只能在32位int使用8位来做同步锁，避免多个线程同时访问。(最新版SPARC实现已经突破此限制)</p>
<p><img src="http://timyang.net/blog/wp-content/uploads/2009/05/atomic_t.gif" alt="atomic_t" width="300" height="102" /></p>
<p>另外一个原因是避免atomic_t传递到程序其他地方进行操作修改等。强制使用atomic_t，则避免被不恰当的误用。</p>
<pre>atomic_t my_counter = ATOMIC_INIT(0);
val = atomic_read( &amp;my_counter );
atomic_add( 1, &amp;my_counter );
atomic_inc( &amp;my_counter );
atomic_sub( 1, &amp;my_counter );
atomic_dec( &amp;my_counter );</pre>
<ul>
<li>原子操作硬件上的实现</li>
</ul>
<p>Solaris的实现是基于test-and-set的指令，并且该指令为原子操作。比如Solaris的实现在SPARC上是基于ldstub和cas指令，在x86上用的是cmpxchg指令。但是Linux似乎直接用的add指令。</p>
<p>OpenSolaris i386的实现</p>
<pre>	movl	4(%esp), %edx	/ %edx = target address
	movl	(%edx), %eax	/ %eax = old value
1:
	leal	1(%eax), %ecx	/ %ecx = new value
	lock
	cmpxchgl %ecx, (%edx)	/ try to stick it in
	jne	1b
	movl	%ecx, %eax	/ return new value
	ret</pre>
<p>在Linux源代码asm_i386/atomic.h中</p>
<pre><span class="stx-comment"><span id="l45">/**
</span><span id="l46"> * atomic_add - add integer to atomic variable
</span><span id="l47"> * @i: integer value to add
</span><span id="l48"> * @v: pointer of type atomic_t
</span><span id="l49"> *
</span><span id="l50"> * Atomically adds @i to @v.  Note that the guaranteed useful range
</span><span id="l51"> * of an atomic_t is only 24 bits.
</span></span><span id="l52"><span class="stx-comment"> */</span>
</span><span id="l53"><span class="stx-keyword">static</span> __inline__ <span class="stx-keyword">void</span> atomic_add(<span class="stx-keyword">int</span> i, atomic_t *v)
</span><span id="l54">{
</span><span id="l55">        __asm__ __volatile__(
</span><span id="l56">                LOCK <span class="stx-string">"addl %1,%0"</span>
</span><span id="l57">                :<span class="stx-string">"=m"</span> (v-&gt;counter)
</span><span id="l58">                :<span class="stx-string">"ir"</span> (i), <span class="stx-string">"m"</span> (v-&gt;counter));
</span><span id="l59">}
</span></pre>
<h1>锁的类型</h1>
<ul>
<li>Spinlocks自旋锁</li>
</ul>
<p>如果锁被占用，尝试获取锁的线程进入busy-wait状态，即CPU不停的循环检查锁是否可用。自旋锁适合占用锁非常短的场合，避免等待锁的线程sleep而带来的CPU两个context switch的开销。</p>
<ul>
<li>Semaphores信号量</li>
</ul>
<p>如果锁被占用，尝试获取锁的线程进入sleep状态，CPU切换到别的线程。当锁释放之后，系统会自动唤醒sleep的线程。信号量适合对锁占用较长时间的场合。</p>
<ul>
<li>Adaptive locks自适应锁</li>
</ul>
<p>顾名思义，自适应锁就是上面两种的结合。当线程尝试申请锁，会自动根据拥有锁的线程繁忙或sleep来选择是busy wait还是sleep。这种锁只有Solaris内核提供，Linux上未见有相关描述。</p>
<p>几种锁的性能比较(Windows操作系统下，第一种类似atomic_inc, 2,3类似spinlock, 4,5类似semaphore)</p>
<p><img class="alignnone" title="Relative cost for different locking mechanisms" src="http://software.intel.com/file/7357" alt="" width="423" height="361" /></p>
<p>(图片来源：<a href="http://software.intel.com/en-us/articles/is-the-free-lunch-really-over-scalability-in-manycore-systems-part-2-using-locks-efficiently/" target="_blank">Intel Software Network</a>)</p>
<h1>Java synchronization</h1>
<p>再理论联系实际一下，看Java中的锁底层如何实现的。这篇<a href="http://www.blogjava.net/security/archive/2009/02/16/jvm_thin-lock_fat-lock__spin-lock_tasuki-lock.html" target="_blank">关于JVM的Thin Lock, Fat Lock, SPIN Lock与Tasuki Lock</a>中讲到Java synchronization实际上也是一种自适应锁。</p>
<blockquote><p>于是，JVM早期版本的做法是，如果T1, T2，T3，T4&#8230;产生线程竞争，则T1通过CAS获得锁(此时是Thin Lock方式)，如果T1在CAS期间获得锁，则T2，T3进入SPIN状态直到T1释放锁；而第二个获得锁的线程，比如T2，会将锁升级（Inflation）为Fat Lock，于是，以后尝试获得锁的线程都使用Mutex方式获得锁。</p></blockquote>
<p>Java AtomicInteger的实现，似乎和Solaris的实现非常类似，也是一个busy wait的方式</p>
<pre>   /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        // unsafe.compareAndSwapInt是用本地代码实现
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }</pre>
<h1>Solaris</h1>
<p>感觉OpenSolaris在很多地方要比Linux优秀，Solaris在理论设计和实践上都非常优雅，而Linux内核很多地方似乎更偏工程实践方向一些。另外Solaris用来做学习操作系统更合适，它的mdb几乎无所不能。</p>
<p>我在<a href="http://www.virtualbox.org/" target="_blank">VirtualBox</a>虚拟机上安装了OpenSolaris，非常容易安装，使用这个<a href="http://blogs.sun.com/jkshah/entry/minimal_opensolaris_image_for_virtualbox" target="_blank">Minimal OpenSolaris Appliance OVF image for VirtualBox 2.2</a> 简易方法，安装一个没有gui的版本，大约3分钟以内就可以装好。OpenSolaris安装软件和Ubuntu一样方便，使用 pkg install SUNWxxx 的命令，比如 pkg install SUNWcurl</p>
Similar Posts:<ul><li><a href="http://timyang.net/linux/linux-process/" rel="bookmark" title="May 3, 2009">了解Linux的进程与线程</a></li>

<li><a href="http://timyang.net/linux/linux-timer-tick/" rel="bookmark" title="May 18, 2009">谈Linux内核定时器</a></li>

<li><a href="http://timyang.net/lua/lua-coroutine/" rel="bookmark" title="April 26, 2009">Lua coroutine 不一样的多线程编程思路</a></li>

<li><a href="http://timyang.net/lua/lua-coroutine-vs-java-wait-notify/" rel="bookmark" title="April 27, 2009">Lua coroutine vs Java wait/notify</a></li>

<li><a href="http://timyang.net/web/nginx-module/" rel="bookmark" title="March 17, 2009">如何写nginx module</a></li>
</ul><!-- Similar Posts took 41.982 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/programming/linux-synchronization-lock/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
