<?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; 内核</title>
	<atom:link href="http://www.speedvi.net/tag/%e5%86%85%e6%a0%b8/feed" rel="self" type="application/rss+xml" />
	<link>http://www.speedvi.net</link>
	<description>为者常成 行者常至</description>
	<lastBuildDate>Sat, 12 Jun 2010 06:30:36 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>在单CPU上使用多线程</title>
		<link>http://www.speedvi.net/2010/01/27/201.html</link>
		<comments>http://www.speedvi.net/2010/01/27/201.html#comments</comments>
		<pubDate>Wed, 27 Jan 2010 08:41:23 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[线程]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/2010/01/27/201.html</guid>
		<description><![CDATA[　　假设我们略微修改我们的例子，让它能演示有些时候在单处理器上使用多线程的好处。 　　在这个修改的例子里面，网络中的一个节点负责计算扫描线（与前面的图像例子一样）。不过，当一个扫描线的计算结束后，它的数据就通过网络发送到另外一个节点。下面是我们修改后的main()函数： int main (int argc, char **argv) { int x1; … // perform initializations for (x1 = 0; x1 &#60; num_x_lines; x1++) { do_one_line (x1); // "C" in our diagram, below tx_one_line_wait_ack (x1); // "X" and "W" in diagram below } } 　　你可以看出，我们已经消除了显示部分的代码，并添加了一个tx_one_line_wait_ack()函数。并进一步假设我们用的是较慢的网络，并且CPU并不参与网络传输的工作，它只是把数据发送到硬件，之后这个硬件来完成数据的传输。tx_one_line_wait_ack()函数使用了一点CPU来把数据发送到硬件，之后在等待远端的接收信号的时候是不使用CPU的。 　　下面的是CPU的使用框图（C表示了图像计算部分，X表示传输部分，W表示等待接收确认部分）： 　　我们可以看到在等待硬件完成它们的工作的时候，浪费了大量宝贵的CPU时间。如果使用多线程的话，我们就可以更好的利用CPU了，如下图所示： 　　在这幅图里面，我们可以看到情况就好些了。虽然在第二个线程里面仍然会花费一些时间等待，不过总体来说我们消减了总的计算时间。 　　如果我们的时间中，Tcompute 用于计算，Ttx 用于传输，Twait 用于硬件传输，在第一个情况下，我们总的运行时间是： (Tcompute + Ttx + [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2010/01/18/191.html' rel='bookmark' title='线程的启动'>线程的启动</a> <small>　　任何线程在同一个进程中都可以创建另一个线程，这没有任何的限制。创建线程最常用的就是POSIX函数pthread_create()，该函数的定义如下： #include &lt;pthread.h&gt; int pthread_create (pthread_t *thread, const pthread_attr_t *attr,...</small></li>
<li><a href='http://www.speedvi.net/2009/09/23/157.html' rel='bookmark' title='线程的调度'>线程的调度</a> <small>线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。...</small></li>
<li><a href='http://www.speedvi.net/2010/02/24/211.html' rel='bookmark' title='用于线程同步的条件变量(Condition Variables)'>用于线程同步的条件变量(Condition Variables)</a> <small>　　条件变量(condition variables或condvars)与前面讲的睡眠锁(sleepon lock)非常类似。而实际上睡眠锁是在条件变量的基础上构建的，这也是为什么我们在睡眠锁的例子的解释表中有一个CONDVAR状态。它也能通过不停的调用pthread_cond_wait()函数来释放互斥体、等待以及重新获取互斥体，和pthread_sleepon_wait()函数一样。 　　下面我们就略过初始化的步骤，并使用条件变量来重新完成sleepon部分的那个生产者与消费者的多线程的程序。之后再讨论调用的函数。 &nbsp; /* * cp1.c */ #include...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　假设我们略微修改我们的例子，让它能演示有些时候在单处理器上使用多线程的好处。</p>
<p>　　在这个修改的例子里面，网络中的一个节点负责计算扫描线（与前面的图像例子一样）。不过，当一个扫描线的计算结束后，它的数据就通过网络发送到另外一个节点。下面是我们修改后的main()函数：</p>
<p><span id="more-201"></span>
<pre class="codesamp">int
main (int argc, char **argv)
{
    int x1;

    …    // perform initializations

    for (x1 = 0; x1 &lt; num_x_lines; x1++) {
        do_one_line (x1);           // "C" in our diagram, below
        tx_one_line_wait_ack (x1);  // "X" and "W" in diagram below
    }
}</pre>
<p>　　你可以看出，我们已经消除了显示部分的代码，并添加了一个tx_one_line_wait_ack()函数。并进一步假设我们用的是较慢的网络，并且CPU并不参与网络传输的工作，它只是把数据发送到硬件，之后这个硬件来完成数据的传输。tx_one_line_wait_ack()函数使用了一点CPU来把数据发送到硬件，之后在等待远端的接收信号的时候是不使用CPU的。</p>
<p>　　下面的是CPU的使用框图（C表示了图像计算部分，X表示传输部分，W表示等待接收确认部分）：</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="CPU使用" border="0" alt="CPU使用" src="http://www.speedvi.net/wp-content/uploads/2010/01/dpt3.gif" width="319" height="56"> </p>
<p>　　我们可以看到在等待硬件完成它们的工作的时候，浪费了大量宝贵的CPU时间。如果使用多线程的话，我们就可以更好的利用CPU了，如下图所示：</p>
<p><a href="http://www.speedvi.net/wp-content/uploads/2010/01/dpt4.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="dpt4" border="0" alt="dpt4" src="http://www.speedvi.net/wp-content/uploads/2010/01/dpt4_thumb.gif" width="333" height="100"></a> 　　在这幅图里面，我们可以看到情况就好些了。虽然在第二个线程里面仍然会花费一些时间等待，不过总体来说我们消减了总的计算时间。</p>
<p>　　如果我们的时间中，<i class="var">T</i><sub><i class="var">compute</i></sub> 用于计算，<i class="var">T</i><sub><i class="var">tx</i></sub> 用于传输，<i class="var">T</i><sub><i class="var">wait</i></sub> 用于硬件传输，在第一个情况下，我们总的运行时间是：</p>
<blockquote><p>(T<sub>compute</sub> + T<sub>tx</sub> + T<sub>wait</sub>) × <i class="var">num_x_lines</i></p></blockquote>
<p>　　而在第二个情况下，总的运行时间是：</p>
<blockquote><p>(T<sub>compute</sub> + T<sub>tx</sub>) × <i class="var">num_x_lines</i> + T<sub>wait</sub></p></blockquote>
<p>　　中间减少的时间为：</p>
<blockquote><p>T<sub>wait</sub> × (<i class="var">num_x_lines</i> &#8211; 1)</p></blockquote>
<p>　　显然，<i class="var">T</i><sub><i class="var">wait</i></sub> ≤ <i class="var">T</i><sub><i class="var">compute。</i></sub></p>
<p>　　如果我们在有4个CPU的SMP系统上运行4线程的版本，那么运行情况就像下面这样：</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="dpt5" border="0" alt="dpt5" src="http://www.speedvi.net/wp-content/uploads/2010/01/dpt5.gif" width="544" height="468"> </p>
<p>　　可以看到，每个CPU都没有被充分利用（在utilization图中的空格表示）。在上面的图中有两个有趣的地方。当这4个线程开始时，它们都开始计算。不幸的是当这些线程完成计算之后，它们就开始对传输硬件的竞争。（在图中的X部分有偏移，这是因为同一时刻只能有一个正在进行的传输）。这在一开始就有一些异常。一旦线程过了这个阶段，它们就自然的与传输硬件同步了，因为传输所花的时间只是计算周期的四分之一。忽略一开始的异常，这个系统就可以使用如下公式描述了：</p>
<blockquote><p>(T<sub>compute</sub> + T<sub>tx</sub> + T<sub>wait</sub>) × <i class="var">num_x_lines</i> / <i class="var">num_cpus</i></p></blockquote>
<p>　　这个公式说明了，在4个CPU上面使用4个线程要比我们一开始的单线程模式快4倍。</p>
<p>　　通过结合后面的单CPU上面使用多线程的方法，我们可以让线程数大于CPU的数目，这样多出的线程就能够使用因为传输等待而产生的空闲时间了。这样的运行情况就会像下面这样：</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="" border="0" alt="" src="http://www.speedvi.net/wp-content/uploads/2010/01/dpt5a.gif" width="557" height="567"> </p>
<p>　　在这里，有如下几个假设：</p>
<ul>
<li>线程5、6、7、8分别绑定到1、2、3、4号处理器；</li>
<li>传输部分的优先级比计算部分的优先级高；</li>
<li>传输为不可中断的操作。</li>
<p>　　在上面的示意图中，可以发现即使我们有了两倍于CPU数量的线程，在运行的过程中有时还是有CPU未完全使用的情况。在图中有三个地方，CPU是低效使用的，这在每个CPU的使用带状图中使用数字进行了标注：</ul>
<ul>1. 线程1在等待接收确认（W状态），同时线程5完成了计算并等待传输；<br />2. 线程2与6都在等待确认；</ul>
<p>3. 线程3在等待确认的时候线程7完成了计算并等待传输。</p>
<p>　　这个例子同时也可以得出一个结论，就是你不能通过不停的添加CPU来让程序运行的更快些，因为还有其他的受限因素。有些时候，这个受限因素是多处理器的主板的设计——当多个CPU试图访问同一区域的内存时会产生多少的内存与设备的竞争。在我们的例子里面，我们可以看到TX Slot Utilization条形图开始变满了。如果我们添加足够多的CPU，在运行的时候必然会出问题，因为它们的线程要等待传输，而是低速运行的。</p>
<p>　　不论如何，都是可以使用大量的线程来利用空闲的CPU的，这样可以更好的利用CPU。利用的公式大概像下面这样：</p>
<blockquote><p>(T<sub>compute</sub> + T<sub>tx</sub>) × <i class="var">num_x_lines</i> / <i class="var">num_cpus</i></p></blockquote>
<p>　　这个计算的本质就是我们只是受限于我们使用的CPU的个数；我们不让任何CPU因为等待响应而空闲。（当然了，这是理想化的。在上面的框图中，有几次是周期性的让一个CPU空闲的，</p>
<p>T<sub>compute</sub> + T<sub>tx</sub> × <em>num_x_lines</em>是我们速度的限制）</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2010/01/18/191.html' rel='bookmark' title='线程的启动'>线程的启动</a> <small>　　任何线程在同一个进程中都可以创建另一个线程，这没有任何的限制。创建线程最常用的就是POSIX函数pthread_create()，该函数的定义如下： #include &lt;pthread.h&gt; int pthread_create (pthread_t *thread, const pthread_attr_t *attr,...</small></li>
<li><a href='http://www.speedvi.net/2009/09/23/157.html' rel='bookmark' title='线程的调度'>线程的调度</a> <small>线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。...</small></li>
<li><a href='http://www.speedvi.net/2010/02/24/211.html' rel='bookmark' title='用于线程同步的条件变量(Condition Variables)'>用于线程同步的条件变量(Condition Variables)</a> <small>　　条件变量(condition variables或condvars)与前面讲的睡眠锁(sleepon lock)非常类似。而实际上睡眠锁是在条件变量的基础上构建的，这也是为什么我们在睡眠锁的例子的解释表中有一个CONDVAR状态。它也能通过不停的调用pthread_cond_wait()函数来释放互斥体、等待以及重新获取互斥体，和pthread_sleepon_wait()函数一样。 　　下面我们就略过初始化的步骤，并使用条件变量来重新完成sleepon部分的那个生产者与消费者的多线程的程序。之后再讨论调用的函数。 &nbsp; /* * cp1.c */ #include...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2010/01/27/201.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>系统内核的角色</title>
		<link>http://www.speedvi.net/2009/12/26/183.html</link>
		<comments>http://www.speedvi.net/2009/12/26/183.html#comments</comments>
		<pubDate>Sat, 26 Dec 2009 08:09:44 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[进程]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/2009/12/26/183.html</guid>
		<description><![CDATA[　　我们前面的使用房子做的比喻对于解释同步的概念是非常合适的，但对系统中另一个重要方面就不合适了。在我们的比喻中，多个线程是同时运行的。可是，在实际的系统中，一般只有一个处理器，那么在一个时间点上只能运行一件“事”。 单处理器情况 　　下面看看在实际系统中的真实运行情况，在较“经济”的情况下，一个系统中只有一个中央处理器。这时，在任何时间点就只能有一个线程可以运行。系统内核使用数种规则来决定让哪个线程运行并运行该线程。 多处理器情况 　　如果你购买的计算机有多个、相同的CPU，并且这些CPU共享内存与设备，那么就可以说你有了一个SMP系统了（SMP是对称多处理器的简称，指系统中的这些处理器是一样的）。这时，可以同时运行的线程的数量就受限于CPU的个数了。由于一个CPU一次只能运行一个线程，在有多个CPU的情况下，多个线程就可以同步运行。 　　我们可以忽略CPU的个数，而抽象的设计系统能够让多个线程同步运行，虽然实际情况并不是这样。 内核作为仲裁者 　　那么谁来决定在指定时间哪个线程运行呢？这就是内核的工作职责。 　　内核决定了在特定时刻可以使用CPU的线程，并切换环境变量到那个线程。下面详细说明一下内核对CPU做了什么。 　　CPU有一定数量的寄存器（寄存器的具体数量依赖于处理器的类型）。当线程运行的时候，线程的一些信息就保存在这些寄存器里面，例如当前程序位置等等。 　　当内核确定要让另外一个线程运行时，它就会： 保存当前运行线程的寄存器以及环境变量信息； 将新线程的寄存器以及环境变量信息载入CPU。 　　不过内核是如何确定另外一个要运行的线程的呢？它先检查某个特定线程当前是否有能力使用CPU。以前我们讨论互斥体时，曾经提到过一个阻塞状态（当一个线程拥有互斥体，而另外一个线程想要获取它时，第二个线程就会进入阻塞状态）。 　　从内核的角度来看，一个线程正在使用CPU，而另外一个线程因为正在等待互斥体而进入阻塞状态而不能够使用CPU。这种情况下，内核就让可以运行的线程使用CPU，而将其他线程放入其内部列表中（从而内核就可以跟踪对于互斥体的请求）。 　　这显然不是一个有趣的情况。假设很多线程可以使用CPU。以前我们提到过线程对于互斥体的访问是基于优先级以及等待时间。内核也是使用一个类似的策略来决定哪个线程应该接下来跟着运行的。也就是说有两个因素：优先级和调度算法来确定这个线程的执行顺序。 优先级 　　假设两个线程有能力使用CPU。如果这两个线程的优先级不同，那么解决办法就非常简单——系统内核就将CPU交给优先级最高的那个线程。N系统的优先级从1（最低的可用优先级）向上计数。需要注意的是优先级0是保留给空闲线程的，用户是不能使用这个优先级的。 　　如果另外一个有更高优先级的线程突然能够使用CPU了，系统内核就会立即执行环境变量切换，将其切换为这个有较高优先级的线程。这就是我们经常说的抢占——有较高优先级的线程抢占较低优先级的线程。当较高优先级线程完成之后，系统内核就将环境变量切换回刚才运行的较低优先级线程，称这个为接续(resumption)，内核接续了前一个线程的运行。 　　如果现在的两个可以使用CPU的线程拥有同样的优先级，该如何处理呢？ 调度算法 　　假设这两个线程中的一个正在使用CPU。系统内核在这种情况下是按照什么规则执行环境变量切换的？系统内核所使用的两个主要的调度算法就是环形(Round Robin)和先入先出(FIFO)。 FIFO 　　在这种算法中，线程可以按自己的意愿，使用CPU任意长的时间。这就意味着，如果这个线程执行一个非常长的数学计算，并且没有较高优先级的线程抢占CPU，那么这个线程可能会永远的运行下去了。那么和它优先级相同的线程会如何呢？它们就被据于CPU的使用之外了。至于更低优先级的同样也没有运行的机会了。 　　如果运行线程退出或自动释放CPU，那么系统内核就查找能够使用CPU的同优先级的其他线程。如果找不到这样的线程，内核就会查找可以使用CPU的较低优先级的线程。自动释放CPU可能有两种情况，一个是线程进入睡眠状态，另一个就是线程因为信号量而阻塞等等，之后较低优先级的线程就可能运行了。除此之外，还有一个特别的调用函数sched_yield()，这个函数会让线程放弃CPU，让同优先级的其他线程使用——如果有较高优先级的线程已经就绪的话，较低优先级的线程就永远没有机会运行。如果一个线程调用了这个函数，可是没有同优先级的其他线程可以运行，那么原来这个线程就会继续运行。简单来说，sched_yield()函数就是让同优先级的线程有一个得到CPU并运行的机会。 环形（Round Robin） 　　RR调度算法与FIFO十分类似，不过有一点不同就是，如果有同优先级的线程正在等待运行，当前运行的线程就不会永远运行。它只会运行系统所设定的时间片（timeslice）那么长的时间。时间片的具体数值可以通过调用系统函数sched_rr_get_intercal()进行查询。通常时间片的具体值为4毫秒，实际上就是最小时间间隔的四倍，而最小时间间隔可以通过函数ClockPeriod()查询或设定。 　　具体实现是这样的，系统内核开始一个RR线程并留意其时间。如果这个RR线程运行了一会，并且分配给它的时间就要用完了（时间片就要失效了）。系统内核就会查找是否有同优先级的其他线程已经准备运行了。如果有的话，就运行那个线程。如果没有，系统内核就会继续运行这个RR线程（也就是为这个线程再分配了一个时间片）。 运行规则 　　对于单CPU的系统，线程的调度规则按重要性总结如下： 一次只能运行一个线程 较高优先级并已就绪的线程先开始运行 一个线程除了阻塞或退出之前就一直运行 RR线程会运行分配给它的时间片那么长的时间，之后内核会重新调度它 Related posts:进程间通信 　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。 　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。 　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。 　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。 　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递... 线程的调度 线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。... 进程间通信的共享内存 　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='进程间通信'>进程间通信</a> <small>　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。 　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。 　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。 　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。 　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递...</small></li>
<li><a href='http://www.speedvi.net/2009/09/23/157.html' rel='bookmark' title='线程的调度'>线程的调度</a> <small>线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。...</small></li>
<li><a href='http://www.speedvi.net/2009/09/28/177.html' rel='bookmark' title='进程间通信的共享内存'>进程间通信的共享内存</a> <small>　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。 　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。 用于消息传递的共享内存 　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC： 非常高的性能(共享内存) 同步(消息传递) 网络透明(消息传递) &nbsp;...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　我们前面的使用房子做的比喻对于解释同步的概念是非常合适的，但对系统中另一个重要方面就不合适了。在我们的比喻中，多个线程是同时运行的。可是，在实际的系统中，一般只有一个处理器，那么在一个时间点上只能运行一件“事”。</p>
<h5>单处理器情况</h5>
<p>　　下面看看在实际系统中的真实运行情况，在较“经济”的情况下，一个系统中只有一个中央处理器。这时，在任何时间点就只能有一个线程可以运行。系统内核使用数种规则来决定让哪个线程运行并运行该线程。</p>
<h5>多处理器情况</h5>
<p>　　如果你购买的计算机有多个、相同的CPU，并且这些CPU共享内存与设备，那么就可以说你有了一个SMP系统了（SMP是对称多处理器的简称，指系统中的这些处理器是一样的）。这时，可以同时运行的线程的数量就受限于CPU的个数了。由于一个CPU一次只能运行一个线程，在有多个CPU的情况下，多个线程就可以同步运行。</p>
</p>
<p><span id="more-183"></span>
<p>　　我们可以忽略CPU的个数，而抽象的设计系统能够让多个线程同步运行，虽然实际情况并不是这样。</p>
<h6>内核作为仲裁者</h6>
<p>　　那么谁来决定在指定时间哪个线程运行呢？这就是内核的工作职责。</p>
<p>　　内核决定了在特定时刻可以使用CPU的线程，并切换环境变量到那个线程。下面详细说明一下内核对CPU做了什么。</p>
<p>　　CPU有一定数量的寄存器（寄存器的具体数量依赖于处理器的类型）。当线程运行的时候，线程的一些信息就保存在这些寄存器里面，例如当前程序位置等等。</p>
<p>　　当内核确定要让另外一个线程运行时，它就会：</p>
<ol>
<li>保存当前运行线程的寄存器以及环境变量信息；
<li>将新线程的寄存器以及环境变量信息载入CPU。 </li>
</ol>
<p>　　不过内核是如何确定另外一个要运行的线程的呢？它先检查某个特定线程当前是否有能力使用CPU。以前我们讨论互斥体时，曾经提到过一个阻塞状态（当一个线程拥有互斥体，而另外一个线程想要获取它时，第二个线程就会进入阻塞状态）。</p>
<p>　　从内核的角度来看，一个线程正在使用CPU，而另外一个线程因为正在等待互斥体而进入阻塞状态而不能够使用CPU。这种情况下，内核就让可以运行的线程使用CPU，而将其他线程放入其内部列表中（从而内核就可以跟踪对于互斥体的请求）。</p>
<p>　　这显然不是一个有趣的情况。假设很多线程可以使用CPU。以前我们提到过线程对于互斥体的访问是基于优先级以及等待时间。内核也是使用一个类似的策略来决定哪个线程应该接下来跟着运行的。也就是说有两个因素：优先级和调度算法来确定这个线程的执行顺序。</p>
<h6>优先级</h6>
<p>　　假设两个线程有能力使用CPU。如果这两个线程的优先级不同，那么解决办法就非常简单——系统内核就将CPU交给优先级最高的那个线程。N系统的优先级从1（最低的可用优先级）向上计数。需要注意的是优先级0是保留给空闲线程的，用户是不能使用这个优先级的。</p>
<p>　　如果另外一个有更高优先级的线程突然能够使用CPU了，系统内核就会立即执行环境变量切换，将其切换为这个有较高优先级的线程。这就是我们经常说的抢占——有较高优先级的线程抢占较低优先级的线程。当较高优先级线程完成之后，系统内核就将环境变量切换回刚才运行的较低优先级线程，称这个为接续(resumption)，内核接续了前一个线程的运行。</p>
<p>　　如果现在的两个可以使用CPU的线程拥有同样的优先级，该如何处理呢？</p>
<h6>调度算法</h6>
<p>　　假设这两个线程中的一个正在使用CPU。系统内核在这种情况下是按照什么规则执行环境变量切换的？系统内核所使用的两个主要的调度算法就是环形(Round Robin)和先入先出(FIFO)。</p>
<p><strong>FIFO</strong></p>
<p><strong>　　</strong>在这种算法中，线程可以按自己的意愿，使用CPU任意长的时间。这就意味着，如果这个线程执行一个非常长的数学计算，并且没有较高优先级的线程抢占CPU，那么这个线程可能会永远的运行下去了。那么和它优先级相同的线程会如何呢？它们就被据于CPU的使用之外了。至于更低优先级的同样也没有运行的机会了。</p>
<p>　　如果运行线程退出或自动释放CPU，那么系统内核就查找能够使用CPU的同优先级的其他线程。如果找不到这样的线程，内核就会查找可以使用CPU的较低优先级的线程。自动释放CPU可能有两种情况，一个是线程进入睡眠状态，另一个就是线程因为信号量而阻塞等等，之后较低优先级的线程就可能运行了。除此之外，还有一个特别的调用函数sched_yield()，这个函数会让线程放弃CPU，让同优先级的其他线程使用——如果有较高优先级的线程已经就绪的话，较低优先级的线程就永远没有机会运行。如果一个线程调用了这个函数，可是没有同优先级的其他线程可以运行，那么原来这个线程就会继续运行。简单来说，sched_yield()函数就是让同优先级的线程有一个得到CPU并运行的机会。</p>
<p><strong>环形（Round Robin）</strong></p>
<p><strong>　　</strong>RR调度算法与FIFO十分类似，不过有一点不同就是，如果有同优先级的线程正在等待运行，当前运行的线程就不会永远运行。它只会运行系统所设定的时间片（timeslice）那么长的时间。时间片的具体数值可以通过调用系统函数sched_rr_get_intercal()进行查询。通常时间片的具体值为4毫秒，实际上就是最小时间间隔的四倍，而最小时间间隔可以通过函数ClockPeriod()查询或设定。</p>
<p>　　具体实现是这样的，系统内核开始一个RR线程并留意其时间。如果这个RR线程运行了一会，并且分配给它的时间就要用完了（时间片就要失效了）。系统内核就会查找是否有同优先级的其他线程已经准备运行了。如果有的话，就运行那个线程。如果没有，系统内核就会继续运行这个RR线程（也就是为这个线程再分配了一个时间片）。</p>
<p><strong>运行规则</strong></p>
<p>　　对于单CPU的系统，线程的调度规则按重要性总结如下：</p>
<ul>
<li>一次只能运行一个线程</li>
<li>较高优先级并已就绪的线程先开始运行</li>
<li>一个线程除了阻塞或退出之前就一直运行</li>
<li>RR线程会运行分配给它的时间片那么长的时间，之后内核会重新调度它</li>
</ul>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='进程间通信'>进程间通信</a> <small>　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。 　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。 　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。 　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。 　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递...</small></li>
<li><a href='http://www.speedvi.net/2009/09/23/157.html' rel='bookmark' title='线程的调度'>线程的调度</a> <small>线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。...</small></li>
<li><a href='http://www.speedvi.net/2009/09/28/177.html' rel='bookmark' title='进程间通信的共享内存'>进程间通信的共享内存</a> <small>　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。 　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。 用于消息传递的共享内存 　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC： 非常高的性能(共享内存) 同步(消息传递) 网络透明(消息传递) &nbsp;...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/12/26/183.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>进程间通信的共享内存</title>
		<link>http://www.speedvi.net/2009/09/28/177.html</link>
		<comments>http://www.speedvi.net/2009/09/28/177.html#comments</comments>
		<pubDate>Mon, 28 Sep 2009 09:03:24 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[共享内存]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[通信]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/?p=177</guid>
		<description><![CDATA[　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。 　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。 用于消息传递的共享内存 　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC： 非常高的性能(共享内存) 同步(消息传递) 网络透明(消息传递) &#160; 　　客户端使用消息传递向一个服务器端发送请求并阻塞。服务器端按优先级接收客户端发送的消息，处理消息并满足一个请求之后再回复。这时，客户端就解除阻塞并继续运行。发送消息这个动作就自然的在客户端与服务器端之间做了同步。这时可以不必将全部数据复制到消息中进行传输，只要让消息中包含一个共享内存区范围的引用，服务器端就可以直接读写数据了。这通过一个简单的例子就能解释清楚了。 　　假设一个图形服务器从客户端接收绘图的请求并在一个图像卡上将其着色为一个帧缓冲。如果只使用消息传递，客户端就需要将图像数据包含在一个消息中传到服务器。结果就是从客户端的地址空间中的图像数据要复制到服务器的地址空间。之后服务器端为该图像着色并发送一个简短的回复。 　　如果客户端没有在消息中直接插入图像数据，而是发送了包含图像数据的共享内存区域的引用，那么服务器端就能够直接访问客户端的数据了。 　　由于客户端向服务器端发送消息而被阻塞，服务器端就知道共享内存中的数据是固定的并在服务器端回复之前是不会被改变的。这种消息传递与共享内存的结合就能自然同步并能提供很高的性能。 　　这个动作模式也可以倒过来——让服务器端产生数据并将其发送给客户端。例如，一个客户端先服务器端发送一条消息请求其从CD-ROM中读取视频数据并将其直接写入客户端所提供的共享内存缓存中。服务器端在写入共享内存时，客户端是阻塞的。当服务器端回复，客户端继续运行时，共享内存就已经是固定的了并可以让客户端访问了。这样的设计可以通过使用多个共享内存区域而流水线化。 　　对于通过网络连接的不同计算机上的进程之间是不能够使用简单共享内存的。而消息传递才是网络透明的。一个服务器端可以使用共享内存用于本地客户端，而使用全消息传递用于远程客户端。这样就可让服务器端既能高效也能网络透明。 　　实际使用中，消息传递原的速度已经足够主要进程间通信的需求了。只有在需要非常高带宽的特殊情况下才需要考虑这种复杂的各方式结合的手段。 创建共享内存对象 　　同一个进程中的多个线程共享该进程的内存。要在进程之间共享内存，就先要创建一个共享内存区域并将其映射到你的进程的地址空间。共享内存区域的创建与操作可以使用如下函数： Function Description Classification shm_open() Open (or create) a shared-memory region. POSIX close() Close a shared-memory region. POSIX mmap() Map a shared-memory region into a process&#8217;s address space. POSIX munmap() Unmap a shared-memory region from a process&#8217;s [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2010/01/27/195.html' rel='bookmark' title='多线程中壁垒(barrier)的使用'>多线程中壁垒(barrier)的使用</a> <small>　　前面我们讲过main()函数与工作线程结束进行的同步，在那里提到了两种方式：pthread_join()函数以及壁垒(barrier)。 　　现在我们回到房子的比喻，假设这个家庭准备到哪个地方旅行。司机上了小货车并发动了引擎。之后，司机就开始等待。只有全部的家庭成员都上车之后，这个小货车才会开动——因为我们不想把任何人落下！ 　　这和我们在前面说的那个绘图程序的原理是一模一样的。主线程要等待全部工作线程结束后，才执行下一步的程序。 　　不过和这个比喻还有一个很大的差别。那就是通过使用pthread_join()函数，我们是等待所有工作线程的结束。也就是说，之后这些线程已经不存在了，它们退出了。 　　通过使用壁垒(barrier)，我们可以等待某些数量的线程在壁垒处集合。在设定的数目达到之后，我们解锁这些线程，让它们继续运行。 　　你先要使用pthread_barrier+init()函数来创建壁垒： #include &lt;pthread.h&gt; int pthread_barrier_init...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='进程间通信'>进程间通信</a> <small>　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。 　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。 　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。 　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。 　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递...</small></li>
<li><a href='http://www.speedvi.net/2008/04/09/39.html' rel='bookmark' title='谈谈网站空间'>谈谈网站空间</a> <small>本文从西西河网整理而来。希望能够对所有要做网站、写blog的朋友有些帮助。 做一个网站，最重要的一个步骤，是找到合适的网站空间服务商 (web host provider)，来把你的网站放上去。应该说，webhosting 是一个巨大的市场。需求和相应的供给也是五花八门，各有各的特色，各有各的优点和缺点。找到一个合适的，而不是最好的服务商是一个网站起步的关键之一。 怎么样才是合适的，这里面涉及到各种因素。几个应该重点考虑的因素包括：价格，性能，可靠性，服务。 价格是每个人都关心的。作为消费者来说，当然是希望价格越便宜越好。但是，要记住，天上不会白白的掉馅饼。越便宜的你得越小心，特别是在同一类档次的品种中。Too good to...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。</p>
<p>　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。</p>
<p>　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。</p>
<h6>用于消息传递的共享内存</h6>
<p>　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC：</p>
<ul>
<li>非常高的性能(共享内存)</li>
<li>同步(消息传递)</li>
<li>网络透明(消息传递)</li>
</ul>
<p>&nbsp;</p>
<p><span id="more-177"></span>
<p>　　客户端使用消息传递向一个服务器端发送请求并阻塞。服务器端按优先级接收客户端发送的消息，处理消息并满足一个请求之后再回复。这时，客户端就解除阻塞并继续运行。发送消息这个动作就自然的在客户端与服务器端之间做了同步。这时可以不必将全部数据复制到消息中进行传输，只要让消息中包含一个共享内存区范围的引用，服务器端就可以直接读写数据了。这通过一个简单的例子就能解释清楚了。</p>
<p>　　假设一个图形服务器从客户端接收绘图的请求并在一个图像卡上将其着色为一个帧缓冲。如果只使用消息传递，客户端就需要将图像数据包含在一个消息中传到服务器。结果就是从客户端的地址空间中的图像数据要复制到服务器的地址空间。之后服务器端为该图像着色并发送一个简短的回复。</p>
<p>　　如果客户端没有在消息中直接插入图像数据，而是发送了包含图像数据的共享内存区域的引用，那么服务器端就能够直接访问客户端的数据了。</p>
<p>　　由于客户端向服务器端发送消息而被阻塞，服务器端就知道共享内存中的数据是固定的并在服务器端回复之前是不会被改变的。这种消息传递与共享内存的结合就能自然同步并能提供很高的性能。</p>
<p>　　这个动作模式也可以倒过来——让服务器端产生数据并将其发送给客户端。例如，一个客户端先服务器端发送一条消息请求其从CD-ROM中读取视频数据并将其直接写入客户端所提供的共享内存缓存中。服务器端在写入共享内存时，客户端是阻塞的。当服务器端回复，客户端继续运行时，共享内存就已经是固定的了并可以让客户端访问了。这样的设计可以通过使用多个共享内存区域而流水线化。</p>
<p>　　对于通过网络连接的不同计算机上的进程之间是不能够使用简单共享内存的。而消息传递才是网络透明的。一个服务器端可以使用共享内存用于本地客户端，而使用全消息传递用于远程客户端。这样就可让服务器端既能高效也能网络透明。</p>
<p>　　实际使用中，消息传递原的速度已经足够主要进程间通信的需求了。只有在需要非常高带宽的特殊情况下才需要考虑这种复杂的各方式结合的手段。</p>
<h6>创建共享内存对象</h6>
<p>　　同一个进程中的多个线程共享该进程的内存。要在进程之间共享内存，就先要创建一个共享内存区域并将其映射到你的进程的地址空间。共享内存区域的创建与操作可以使用如下函数：</p>
<table border="1" width="100%">
<tbody>
<tr>
<th>Function</th>
<th>Description</th>
<th>Classification</th>
</tr>
<tr>
<td><i class="func">shm_open()</i></td>
<td>Open (or create) a shared-memory region.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">close()</i></td>
<td>Close a shared-memory region.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">mmap()</i></td>
<td>Map a shared-memory region into a process&#8217;s address space.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">munmap()</i></td>
<td>Unmap a shared-memory region from a process&#8217;s address space.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">munmap_flags()</i></td>
<td>Unmap previously mapped addresses, exercising more control than possible with <i class="func">munmap()</i> </td>
<td>QNX Neutrino</td>
</tr>
<tr>
<td><i class="func">mprotect()</i></td>
<td>Change protections on a shared-memory region.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">msync()</i></td>
<td>Synchronize memory with physical storage.</td>
<td>POSIX</td>
</tr>
<tr>
<td><i class="func">shm_ctl()</i>,<br /><i class="func">shm_ctl_special()</i></td>
<td>Give special attributes to a shared-memory object.</td>
<td>QNX Neutrino</td>
</tr>
<tr>
<td><i class="func">shm_unlink()</i></td>
<td>Remove a shared-memory region.</td>
<td>POSIX</td>
</tr>
</tbody>
</table>
<p>　　POSIX共享内存函数在Neutrino中是通过进程管理器(procnto)来实现的。以上的函数都是作为发送到procnto的消息来实现的。</p>
<p>　　Shm_open()函数所使用的参数与open()函数一致并向对象返回一个文件描述符。与对常规文件的操作一样，这个函数可以用于创建一个新的共享内存对象或是打开一个已有的共享内存对象。</p>
<p>　　当一个新的共享内存对象创建之后，其大小为0。要设置其大小需要使用ftruncate()函数（设置文件大小也是使用同一个函数），或是使用shm_ctl()函数。</p>
<h6>mmap()</h6>
<p>　　一旦你有了指向共享内存对象的文件描述符，就可以使用mmap()函数将这个共享内存的全部或部分映射到你的进程的地址空间。mmap()函数是Neutrino系统内存管理的基石并有必要在下面对其功能详细讨论。</p>
<p>　　该函数的详细定义如下：</p>
<pre class="codesamp">void * mmap( void *<i class="var">where_i_want_it</i>,
             size_t <i class="var">length</i>,
             int <i class="var">memory_protections</i>,
             int <i class="var">mapping_flags</i>,
             int <i class="var">fd</i>,
             off_t <i class="var">offset_within_shared_memory</i> );</pre>
<p>　　简单的说就是：将fd所关联的共享内存对象的offset_within_shared-memory处开始的length字节的共享内存映射到进程中。</p>
<p>　　mmap()函数将尝试将该内存放到你的地址空间的where_i_want_it地址处。该内存将按照指定的memory_protections进行保护并按照mapping_flag的模式映射。</p>
<p>　　三个参数fd、offset_within_shared_memory和length定义了要映射的共享内存的区域。通常都是映射整个共享对象，在这时便宜量就是0而长度就是共享对象的以字节计算的大小。在使用Intel处理器的时候，该长度将是页面大小的整倍数，使用Intel处理器时一个页面的大小为4096字节。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2010/01/27/195.html' rel='bookmark' title='多线程中壁垒(barrier)的使用'>多线程中壁垒(barrier)的使用</a> <small>　　前面我们讲过main()函数与工作线程结束进行的同步，在那里提到了两种方式：pthread_join()函数以及壁垒(barrier)。 　　现在我们回到房子的比喻，假设这个家庭准备到哪个地方旅行。司机上了小货车并发动了引擎。之后，司机就开始等待。只有全部的家庭成员都上车之后，这个小货车才会开动——因为我们不想把任何人落下！ 　　这和我们在前面说的那个绘图程序的原理是一模一样的。主线程要等待全部工作线程结束后，才执行下一步的程序。 　　不过和这个比喻还有一个很大的差别。那就是通过使用pthread_join()函数，我们是等待所有工作线程的结束。也就是说，之后这些线程已经不存在了，它们退出了。 　　通过使用壁垒(barrier)，我们可以等待某些数量的线程在壁垒处集合。在设定的数目达到之后，我们解锁这些线程，让它们继续运行。 　　你先要使用pthread_barrier+init()函数来创建壁垒： #include &lt;pthread.h&gt; int pthread_barrier_init...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='进程间通信'>进程间通信</a> <small>　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。 　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。 　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。 　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。 　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递...</small></li>
<li><a href='http://www.speedvi.net/2008/04/09/39.html' rel='bookmark' title='谈谈网站空间'>谈谈网站空间</a> <small>本文从西西河网整理而来。希望能够对所有要做网站、写blog的朋友有些帮助。 做一个网站，最重要的一个步骤，是找到合适的网站空间服务商 (web host provider)，来把你的网站放上去。应该说，webhosting 是一个巨大的市场。需求和相应的供给也是五花八门，各有各的特色，各有各的优点和缺点。找到一个合适的，而不是最好的服务商是一个网站起步的关键之一。 怎么样才是合适的，这里面涉及到各种因素。几个应该重点考虑的因素包括：价格，性能，可靠性，服务。 价格是每个人都关心的。作为消费者来说，当然是希望价格越便宜越好。但是，要记住，天上不会白白的掉馅饼。越便宜的你得越小心，特别是在同一类档次的品种中。Too good to...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/28/177.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>线程的调度</title>
		<link>http://www.speedvi.net/2009/09/23/157.html</link>
		<comments>http://www.speedvi.net/2009/09/23/157.html#comments</comments>
		<pubDate>Wed, 23 Sep 2009 06:57:42 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[调度]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/2009/09/23/157.html</guid>
		<description><![CDATA[线程调度的决策时间 　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。 　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。 Related posts:系统内核的角色 　　我们前面的使用房子做的比喻对于解释同步的概念是非常合适的，但对系统中另一个重要方面就不合适了。在我们的比喻中，多个线程是同时运行的。可是，在实际的系统中，一般只有一个处理器，那么在一个时间点上只能运行一件“事”。 单处理器情况 　　下面看看在实际系统中的真实运行情况，在较“经济”的情况下，一个系统中只有一个中央处理器。这时，在任何时间点就只能有一个线程可以运行。系统内核使用数种规则来决定让哪个线程运行并运行该线程。 多处理器情况 　　如果你购买的计算机有多个、相同的CPU，并且这些CPU共享内存与设备，那么就可以说你有了一个SMP系统了（SMP是对称多处理器的简称，指系统中的这些处理器是一样的）。这时，可以同时运行的线程的数量就受限于CPU的个数了。由于一个CPU一次只能运行一个线程，在有多个CPU的情况下，多个线程就可以同步运行。 　　我们可以忽略CPU的个数，而抽象的设计系统能够让多个线程同步运行，虽然实际情况并不是这样。 内核作为仲裁者 　　那么谁来决定在指定时间哪个线程运行呢？这就是内核的工作职责。 　　内核决定了在特定时刻可以使用CPU的线程，并切换环境变量到那个线程。下面详细说明一下内核对CPU做了什么。 　　CPU有一定数量的寄存器（寄存器的具体数量依赖于处理器的类型）。当线程运行的时候，线程的一些信息就保存在这些寄存器里面，例如当前程序位置等等。... 在单CPU上使用多线程 　　假设我们略微修改我们的例子，让它能演示有些时候在单处理器上使用多线程的好处。 　　在这个修改的例子里面，网络中的一个节点负责计算扫描线（与前面的图像例子一样）。不过，当一个扫描线的计算结束后，它的数据就通过网络发送到另外一个节点。下面是我们修改后的main()函数： int main (int argc, char **argv) { int... 实际系统中的调度 　　我们已经谈过调度算法以及线程状态，但是我们还没有说过线程等重新调度的原因与时间。有一个常见的误解就是：重新调度的发生是没有什么原因的。这在设计阶段是一个有用的概念。但是更重要的是你要知道产生重新调度的条件。 　　重新调度只会由于以下几个原因才会发生： &#160; 硬件中断 内核调用 出错 硬件中断引起的重新调度 　　硬件中断引起的重新调度有两种情况：定时计数器产生的和其他硬件产生的。 　　实时时钟为内核生成周期性的中断，并引发基于时间的重新调度。 　　例如，如果你调用了函数sleep(10);，就会产生数个实时时钟中断；内核在每个中断为时间时钟做递增操作。当时间时钟表示已经过了10秒了，内核就会将你的线程重新调度为就绪状态。...


Related posts:<ol><li><a href='http://www.speedvi.net/2009/12/26/183.html' rel='bookmark' title='系统内核的角色'>系统内核的角色</a> <small>　　我们前面的使用房子做的比喻对于解释同步的概念是非常合适的，但对系统中另一个重要方面就不合适了。在我们的比喻中，多个线程是同时运行的。可是，在实际的系统中，一般只有一个处理器，那么在一个时间点上只能运行一件“事”。 单处理器情况 　　下面看看在实际系统中的真实运行情况，在较“经济”的情况下，一个系统中只有一个中央处理器。这时，在任何时间点就只能有一个线程可以运行。系统内核使用数种规则来决定让哪个线程运行并运行该线程。 多处理器情况 　　如果你购买的计算机有多个、相同的CPU，并且这些CPU共享内存与设备，那么就可以说你有了一个SMP系统了（SMP是对称多处理器的简称，指系统中的这些处理器是一样的）。这时，可以同时运行的线程的数量就受限于CPU的个数了。由于一个CPU一次只能运行一个线程，在有多个CPU的情况下，多个线程就可以同步运行。 　　我们可以忽略CPU的个数，而抽象的设计系统能够让多个线程同步运行，虽然实际情况并不是这样。 内核作为仲裁者 　　那么谁来决定在指定时间哪个线程运行呢？这就是内核的工作职责。 　　内核决定了在特定时刻可以使用CPU的线程，并切换环境变量到那个线程。下面详细说明一下内核对CPU做了什么。 　　CPU有一定数量的寄存器（寄存器的具体数量依赖于处理器的类型）。当线程运行的时候，线程的一些信息就保存在这些寄存器里面，例如当前程序位置等等。...</small></li>
<li><a href='http://www.speedvi.net/2010/01/27/201.html' rel='bookmark' title='在单CPU上使用多线程'>在单CPU上使用多线程</a> <small>　　假设我们略微修改我们的例子，让它能演示有些时候在单处理器上使用多线程的好处。 　　在这个修改的例子里面，网络中的一个节点负责计算扫描线（与前面的图像例子一样）。不过，当一个扫描线的计算结束后，它的数据就通过网络发送到另外一个节点。下面是我们修改后的main()函数： int main (int argc, char **argv) { int...</small></li>
<li><a href='http://www.speedvi.net/2010/02/26/214.html' rel='bookmark' title='实际系统中的调度'>实际系统中的调度</a> <small>　　我们已经谈过调度算法以及线程状态，但是我们还没有说过线程等重新调度的原因与时间。有一个常见的误解就是：重新调度的发生是没有什么原因的。这在设计阶段是一个有用的概念。但是更重要的是你要知道产生重新调度的条件。 　　重新调度只会由于以下几个原因才会发生： &nbsp; 硬件中断 内核调用 出错 硬件中断引起的重新调度 　　硬件中断引起的重新调度有两种情况：定时计数器产生的和其他硬件产生的。 　　实时时钟为内核生成周期性的中断，并引发基于时间的重新调度。 　　例如，如果你调用了函数sleep(10);，就会产生数个实时时钟中断；内核在每个中断为时间时钟做递增操作。当时间时钟表示已经过了10秒了，内核就会将你的线程重新调度为就绪状态。...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h5>线程调度的决策时间</h5>
<p>　　一旦由于内核调用、例外或者是硬件中断而开始系统微内核的调用，正在运行中的线程就会被暂停。只要任何线程的运行发生改变就要做出一个线程调度的决策，不管这个线程位于哪个进程中。所有进程中的线程是全局调度的。</p>
<p>　　一般来说暂停的线程会恢复运行，但是线程调度器当一个运行中的线程被阻塞、被其他线程抢先或者是自释放的时候就要完成从一个线程到另一个线程的环境转换。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/12/26/183.html' rel='bookmark' title='系统内核的角色'>系统内核的角色</a> <small>　　我们前面的使用房子做的比喻对于解释同步的概念是非常合适的，但对系统中另一个重要方面就不合适了。在我们的比喻中，多个线程是同时运行的。可是，在实际的系统中，一般只有一个处理器，那么在一个时间点上只能运行一件“事”。 单处理器情况 　　下面看看在实际系统中的真实运行情况，在较“经济”的情况下，一个系统中只有一个中央处理器。这时，在任何时间点就只能有一个线程可以运行。系统内核使用数种规则来决定让哪个线程运行并运行该线程。 多处理器情况 　　如果你购买的计算机有多个、相同的CPU，并且这些CPU共享内存与设备，那么就可以说你有了一个SMP系统了（SMP是对称多处理器的简称，指系统中的这些处理器是一样的）。这时，可以同时运行的线程的数量就受限于CPU的个数了。由于一个CPU一次只能运行一个线程，在有多个CPU的情况下，多个线程就可以同步运行。 　　我们可以忽略CPU的个数，而抽象的设计系统能够让多个线程同步运行，虽然实际情况并不是这样。 内核作为仲裁者 　　那么谁来决定在指定时间哪个线程运行呢？这就是内核的工作职责。 　　内核决定了在特定时刻可以使用CPU的线程，并切换环境变量到那个线程。下面详细说明一下内核对CPU做了什么。 　　CPU有一定数量的寄存器（寄存器的具体数量依赖于处理器的类型）。当线程运行的时候，线程的一些信息就保存在这些寄存器里面，例如当前程序位置等等。...</small></li>
<li><a href='http://www.speedvi.net/2010/01/27/201.html' rel='bookmark' title='在单CPU上使用多线程'>在单CPU上使用多线程</a> <small>　　假设我们略微修改我们的例子，让它能演示有些时候在单处理器上使用多线程的好处。 　　在这个修改的例子里面，网络中的一个节点负责计算扫描线（与前面的图像例子一样）。不过，当一个扫描线的计算结束后，它的数据就通过网络发送到另外一个节点。下面是我们修改后的main()函数： int main (int argc, char **argv) { int...</small></li>
<li><a href='http://www.speedvi.net/2010/02/26/214.html' rel='bookmark' title='实际系统中的调度'>实际系统中的调度</a> <small>　　我们已经谈过调度算法以及线程状态，但是我们还没有说过线程等重新调度的原因与时间。有一个常见的误解就是：重新调度的发生是没有什么原因的。这在设计阶段是一个有用的概念。但是更重要的是你要知道产生重新调度的条件。 　　重新调度只会由于以下几个原因才会发生： &nbsp; 硬件中断 内核调用 出错 硬件中断引起的重新调度 　　硬件中断引起的重新调度有两种情况：定时计数器产生的和其他硬件产生的。 　　实时时钟为内核生成周期性的中断，并引发基于时间的重新调度。 　　例如，如果你调用了函数sleep(10);，就会产生数个实时时钟中断；内核在每个中断为时间时钟做递增操作。当时间时钟表示已经过了10秒了，内核就会将你的线程重新调度为就绪状态。...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/23/157.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

