<?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; Linux</title>
	<atom:link href="http://timyang.net/tag/linux/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/linux/linux-timer-tick/</link>
		<comments>http://timyang.net/linux/linux-timer-tick/#comments</comments>
		<pubDate>Mon, 18 May 2009 12:10:41 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[timer]]></category>

		<guid isPermaLink="false">http://timyang.net/?p=182</guid>
		<description><![CDATA[看了3个周末的Linux内核，包括前两周总结的进程与线程，锁与同步。当时研究这个的原因是因为某个程序CPU context switch过高。不过到现在相关的背景知识也了解了，问题也早已经解决了，所以暂时也没有进一步的兴趣驱动去研究内核了，这里是终结篇。:)
在应用程序运行的时候，特别是抢占式的操作系统里面，内核怎么样取得控制权？这依赖硬件支持的系统定时器来实现。内核中有非常多的功能由定时器触发。系统定时器以固定的频率触发，这个频率称为tick rate(HZ)，每个触发周期的时间叫做tick, 它等于1/HZ秒。触发后系统会转到内核相应的handler去处理。
在系统定时器中断时候内核需要做以下事情

更新uptime, time of day, resource及处理器使用状态
检查多处理器是否负载均衡
检查当前进程是否timeslice是否用完，如果用完则重新分配下阶段计划(reschedule)
运行应用程序自定义的定时器(dynamic timers)

了解了tick rate之后才能比较好理解为什么sleep(), timer等API声称都不是提供精确的时间，因为cpu一个任务timeslice没运行完，系统是不会马上切换到这些dynamic timer的进程的。
Linux 2.5以上将tick从100改成1000，相当于系统时钟从10ms变成每1ms要中断一次，当时也引起较大的争议。
改成1000的优点有：

系统时钟精度更高更准确。
系统调用如poll(), select() 等依赖系统时钟的调用性能得到很大改善。
内核实时运行指标如资源占用，uptime更准确。
抢占式机制更精确。

缺点：
我们知道定时器频繁被触发必将会增加开销，降低throughput。这是很多人反对改成1000的理由。原理上它在CPU的时间中断上将会增加10倍的开销。但由于现代CPU硬件速度的变快。大家也逐渐认可了新的调整不会给整体性能带来大的影响。如果在意这一点的可以重新编译内核， 将&#60;asm/param.h&#62;中HZ的值由1000降低。目前可以接受的值有100,500和1000。
Linux 2.6.21 又增加了一种叫dyntick的技术，dyntick就是在系统空闲的时候，彻底停止时间中断， 避免cpu空转，它会带来节能方面的好处。详情可参看Clockevents and dyntick。这是个新的技术，一般的书上都没来得及写进去。
比较感兴趣的后续课题是virtualization系统中的timer实现。
Similar Posts:了解Linux的进程与线程

Lua coroutine vs Java wait/notify

多服务器通讯层应该如何设计—一次code review小记

了解Linux的锁与同步

高性能网站经验-读杨建的Blog有感
]]></description>
			<content:encoded><![CDATA[<p>看了3个周末的Linux内核，包括前两周总结的<a href="http://timyang.net/linux/linux-process/">进程与线程</a>，<a href="http://timyang.net/programming/linux-synchronization-lock/">锁与同步</a>。当时研究这个的原因是因为<a href="http://twitter.com/xmpp/status/1635132615" target="_blank">某个程序CPU context switch过高</a>。不过到现在相关的背景知识也了解了，问题也早已经解决了，所以暂时也没有进一步的兴趣驱动去研究内核了，这里是终结篇。:)</p>
<p>在应用程序运行的时候，特别是抢占式的操作系统里面，内核怎么样取得控制权？这依赖硬件支持的系统定时器来实现。内核中有非常多的功能由定时器触发。系统定时器以固定的频率触发，这个频率称为tick rate(HZ)，每个触发周期的时间叫做tick, 它等于1/HZ秒。触发后系统会转到内核相应的handler去处理。</p>
<p>在系统定时器中断时候内核需要做以下事情</p>
<ul>
<li>更新uptime, time of day, resource及处理器使用状态</li>
<li>检查多处理器是否负载均衡</li>
<li>检查当前进程是否timeslice是否用完，如果用完则重新分配下阶段计划(reschedule)</li>
<li>运行应用程序自定义的定时器(dynamic timers)</li>
</ul>
<p>了解了tick rate之后才能比较好理解为什么sleep(), timer等API声称都不是提供精确的时间，因为cpu一个任务timeslice没运行完，系统是不会马上切换到这些dynamic timer的进程的。</p>
<p>Linux 2.5以上将tick从100改成1000，相当于系统时钟从10ms变成每1ms要中断一次，当时也引起较大的争议。</p>
<p>改成1000的优点有：</p>
<ul>
<li>系统时钟精度更高更准确。</li>
<li>系统调用如poll(), select() 等依赖系统时钟的调用性能得到很大改善。</li>
<li>内核实时运行指标如资源占用，uptime更准确。</li>
<li>抢占式机制更精确。</li>
</ul>
<p>缺点：<br />
我们知道定时器频繁被触发必将会增加开销，降低throughput。这是很多人反对改成1000的理由。原理上它在CPU的时间中断上将会增加10倍的开销。但由于现代CPU硬件速度的变快。大家也逐渐认可了新的调整不会给整体性能带来大的影响。如果在意这一点的可以重新编译内核， 将&lt;asm/param.h&gt;中HZ的值由1000降低。目前可以接受的值有100,500和1000。</p>
<p>Linux 2.6.21 又增加了一种叫dyntick的技术，dyntick就是在系统空闲的时候，彻底停止时间中断， 避免cpu空转，它会带来节能方面的好处。详情可参看<a href="http://lwn.net/Articles/223185/" target="_blank">Clockevents and dyntick</a>。这是个新的技术，一般的书上都没来得及写进去。</p>
<p>比较感兴趣的后续课题是virtualization系统中的timer实现。</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/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/architecture/communication-code-review/" rel="bookmark" title="May 22, 2009">多服务器通讯层应该如何设计—一次code review小记</a></li>

<li><a href="http://timyang.net/programming/linux-synchronization-lock/" rel="bookmark" title="May 10, 2009">了解Linux的锁与同步</a></li>

<li><a href="http://timyang.net/architecture/yangjian-web-server/" rel="bookmark" title="April 28, 2009">高性能网站经验-读杨建的Blog有感</a></li>
</ul><!-- Similar Posts took 12.972 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/linux/linux-timer-tick/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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 18.277 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/programming/linux-synchronization-lock/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>了解Linux的进程与线程</title>
		<link>http://timyang.net/linux/linux-process/</link>
		<comments>http://timyang.net/linux/linux-process/#comments</comments>
		<pubDate>Sun, 03 May 2009 10:48:58 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://timyang.net/linux/%e4%ba%86%e8%a7%a3linux%e7%9a%84%e8%bf%9b%e7%a8%8b%e4%b8%8e%e7%ba%bf%e7%a8%8b/</guid>
		<description><![CDATA[上周碰到部署在真实服务器上某个应用CPU占用过高的问题，虽然经过tuning, 问题貌似已经解决，但我对tuning的方式只是基于大胆的假设并最终生效了。我更希望更多的求证一下程序背后CPU及OS kernel当时的运作机制。所以我读了一些Linux内核设计与实现及其他一些相关资料，对Linux process的机制与切换有了更多一些体会。本文尽可能条理一点，但由于牵涉点较多，同时自己可能觉得某些点有记录的价值，因此文字可能会零散。

进程状态

Linux进程的状态比较容易理解，值得注意的是 UNINTERRUPTIBLE 及 ZOMBIE
TASK_RUNNING
TASK_INTERRUPTIBLE
TASK_UNINTERRUPTIBLE 此时进程不接收信号，这就是为什么有时候kill一个繁忙的进程没有响应。
TASK_ZOMBIE 我们经常 kill -9 pid 之后运行ps会发现被kill的进程仍然存在，状态为 zombie。zombie的进程实际上已经结束，占用的资源也已经释放，仅由于kernel的相关进程描述符还未释放。
TASK_STOPPED

Kernel space and user space

Kernel space是供内核，设备驱动运行的内存区域。user space是供普通应用程序运行的区域。每一个进程都运行在自己的虚拟内存区域，不能访问其他进程的内存空间。普通进程不能访问kernel space, 只能通过系统调用来间接进行。当系统内存比较紧张时，非当前运行进程user space可能会被swap到磁盘。
使用命令 pmap -x &#60;pid&#62; 可以查看进程的内存占用信息； lsof -a -p &#60;pid&#62; 可以查看一个进程打开的文件信息。ps -Lf &#60;pid&#62; 可以查看进程的线程数。
另外procfs也是一个分析进程结构的好地方。procfs是一个虚拟的文件系统，它把系统中正在运行的进程都显现在/proc/&#60;pid&#62;目录下。

进程创建

进程创建通常调用fork实现。创建后子进程和父进程指向同一内存区域，仅当子进程有write发生时候，才会把改动的区域copy到子进程新的地址空间，这就是copy-on-write技术，它极大的提高了创建进程的速度。

Linux的线程实现

Linux线程是通过进程来实现。Linux kernel为进程创建提供一个clone()系统调用，clone的参数包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通过clone()的参数，新创建的进程，也称为LWP(Lightweight process)与父进程共享内存空间，文件句柄，信号处理等，从而达到创建线程相同的目的。
Linux 2.6的线程库叫NPTL(Native POSIX Thread Library)。POSIX thread(pthread)是一个编程规范，通过此规范开发的多线程程序具有良好的跨平台特性。尽管是基于进程的实现，但新版的NPTL创建线程的效率非常高。一些测试显示，基于NPTL的内核创建10万个线程只需要2秒，而没有NPTL支持的内核则需要长达15分钟。
在Linux 2.6之前，Linux kernel并没有真正的thread支持，一些thread library都是在clone()基础上的一些基于user space的封装，因此通常在信号处理、进程调度(每个进程需要一个额外的调度线程)及多线程之间同步共享资源等方面存在一定问题。为了解决这些问题，当年IBM曾经开发一套NGPT(Next Generation POSIX Threads), 效率比 LinuxThreads有明显改进，但由于NPTL的推出，NGPT也完成了相关的历史使命并停止了开发。
NPTL的实现是在kernel增加了futex(fast [...]]]></description>
			<content:encoded><![CDATA[<p>上周碰到部署在真实服务器上某个应用<a href="http://twitter.com/xmpp/statuses/1635132615" target="_blank">CPU占用过高</a>的问题，虽然经过tuning, 问题貌似已经解决，但我对tuning的方式只是基于大胆的假设并最终生效了。我更希望更多的求证一下程序背后CPU及OS kernel当时的运作机制。所以我读了一些<a href="http://www.douban.com/subject/1446021/" target="_blank">Linux内核设计与实现</a>及其他一些相关资料，对Linux process的机制与切换有了更多一些体会。本文尽可能条理一点，但由于牵涉点较多，同时自己可能觉得某些点有记录的价值，因此文字可能会零散。</p>
<ul>
<li>进程状态</li>
</ul>
<p>Linux进程的状态比较容易理解，值得注意的是 UNINTERRUPTIBLE 及 ZOMBIE</p>
<p>TASK_RUNNING<br />
TASK_INTERRUPTIBLE<br />
TASK_UNINTERRUPTIBLE 此时进程不接收信号，这就是为什么有时候kill一个繁忙的进程没有响应。<br />
TASK_ZOMBIE 我们经常 kill -9 pid 之后运行ps会发现被kill的进程仍然存在，状态为 zombie。zombie的进程实际上已经结束，占用的资源也已经释放，仅由于kernel的相关进程描述符还未释放。<br />
TASK_STOPPED</p>
<ul>
<li>Kernel space and user space</li>
</ul>
<p>Kernel space是供内核，设备驱动运行的内存区域。user space是供普通应用程序运行的区域。每一个进程都运行在自己的虚拟内存区域，不能访问其他进程的内存空间。普通进程不能访问kernel space, 只能通过系统调用来间接进行。当系统内存比较紧张时，非当前运行进程user space可能会被swap到磁盘。</p>
<p>使用命令 pmap -x &lt;pid&gt; 可以查看进程的内存占用信息； lsof -a -p &lt;pid&gt; 可以查看一个进程打开的文件信息。ps -Lf &lt;pid&gt; 可以查看进程的线程数。</p>
<p>另外procfs也是一个分析进程结构的好地方。procfs是一个虚拟的文件系统，它把系统中正在运行的进程都显现在/proc/&lt;pid&gt;目录下。</p>
<ul>
<li>进程创建</li>
</ul>
<p>进程创建通常调用fork实现。创建后子进程和父进程指向同一内存区域，仅当子进程有write发生时候，才会把改动的区域copy到子进程新的地址空间，这就是copy-on-write技术，它极大的提高了创建进程的速度。</p>
<ul>
<li>Linux的线程实现</li>
</ul>
<p>Linux线程是通过进程来实现。Linux kernel为进程创建提供一个clone()系统调用，clone的参数包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通过clone()的参数，新创建的进程，也称为LWP(Lightweight process)与父进程共享内存空间，文件句柄，信号处理等，从而达到创建线程相同的目的。</p>
<p>Linux 2.6的线程库叫NPTL(Native POSIX Thread Library)。POSIX thread(pthread)是一个编程规范，通过此规范开发的多线程程序具有良好的跨平台特性。尽管是基于进程的实现，但新版的NPTL创建线程的效率非常高。一些测试显示，基于NPTL的内核创建10万个线程只需要2秒，而没有NPTL支持的内核则需要长达15分钟。</p>
<p>在Linux 2.6之前，Linux kernel并没有真正的thread支持，一些thread library都是在clone()基础上的一些基于user space的封装，因此通常在信号处理、进程调度(每个进程需要一个额外的调度线程)及多线程之间同步共享资源等方面存在一定问题。为了解决这些问题，当年IBM曾经开发一套NGPT(Next Generation POSIX Threads), 效率比 LinuxThreads有明显改进，但由于NPTL的推出，NGPT也完成了相关的历史使命并停止了开发。</p>
<p>NPTL的实现是在kernel增加了futex(fast userspace mutex)支持用于处理线程之间的sleep与wake。futex是一种高效的对共享资源互斥访问的算法。kernel在里面起仲裁作用，但通常都由进程自行完成。</p>
<p>NPTL是一个1&#215;1的线程模型，即一个线程对于一个操作系统的调度进程，优点是非常简单。而其他一些操作系统比如Solaris则是MxN的，M对应创建的线程数，N对应操作系统可以运行的实体。(N&lt;M)，优点是线程切换快，但实现稍复杂。</p>
<ul>
<li>信号</li>
</ul>
<p>进程接收信号有两种：同步和异步。同步信号比如SEGILL(非法访问), SIGSEGV(segmentation fault)等。发生此类信号之后，系统会立即转到内核陷阱处理程序，因此同步信号也称为陷阱。异步信号如kill, lwp_kill, sigsend等调用产生的都是，异步信号也称为中断。</p>
<p>kill &lt;pid&gt; 调用的是 SIGTERM, 此信号可以被捕获和忽略。</p>
<p>kill -9 &lt;pid&gt; 调用的是 SIGKILL, 杀掉进程，不能被捕获和忽略。</p>
<p>SIGHUP是在终端被断开时候调用，如果信号没有被处理，进程会终止。这就是为什么突然断网刚通过远程终端启动的进程都终止的原因。防止的方法是在启动的命令前加上 nohup 命令来忽略 SIGHUP信号。如 nohup ./startup.sh &amp;</p>
<p>很多应用程序通常捕获SIGHUP用来实现一些自定义特性，比如通过控制台传递信号让正在运行的程序重新加载配置文件，避免重启带来的停止服务的副作用。可惜的是，在JAVA中没法直接使用这一功能，SUN JVM没有官方的signal支持，尽管它已经可以实现，详情可参看<a href="http://jeremymanson.blogspot.com/2007/06/signals-and-java.html" target="_blank">Singals and Java</a>.</p>
<p>另外有个有趣的现象是 zombie 状态的进程 kill/kill -9 都没有任何作用，这是由于进程本身已经不存在，所以没有相应的进程来处理signal, zombie状态的进程只是kernel中的进程描述符及相关数据结构没有释放，但进程实体已经不存在了。<img class="zemanta-pixie-img" src="http://img.zemanta.com/pixy.gif?x-id=facfcc8c-3046-8e07-aa6b-9e048a5b3124" alt="" /></p>
<p>关于僵尸进程，也可参看下酷壳上的这篇<a href="http://cocre.com/?p=656" target="_blank">Linux 的僵尸(zombie)进程</a>，从程序的角度解释了相关原理。</p>
Similar Posts:<ul><li><a href="http://timyang.net/programming/linux-synchronization-lock/" rel="bookmark" title="May 10, 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/programming/c-erlang-java-performance/" rel="bookmark" title="November 11, 2009">C, Erlang, Java and Go Web Server performance test</a></li>

<li><a href="http://timyang.net/architecture/game-backend/" rel="bookmark" title="December 25, 2008">陈杰谈网游服务器的后端技术</a></li>

<li><a href="http://timyang.net/lua/lua-coroutine/" rel="bookmark" title="April 26, 2009">Lua coroutine 不一样的多线程编程思路</a></li>
</ul><!-- Similar Posts took 15.576 ms -->]]></content:encoded>
			<wfw:commentRss>http://timyang.net/linux/linux-process/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
