<?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/%e7%b3%bb%e7%bb%9f%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.0.1</generator>
		<item>
		<title>用于线程同步的读写锁（Readers/writer locks）</title>
		<link>http://www.speedvi.net/2010/02/21/207.html</link>
		<comments>http://www.speedvi.net/2010/02/21/207.html#comments</comments>
		<pubDate>Sun, 21 Feb 2010 08:21:44 +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/2010/02/21/207.html</guid>
		<description><![CDATA[　　读写锁(readers/writer lock)的具体含义是文如其名：对一个资源有多个读者而无写入者，或者是只有一个写入者而没有其他的写入者或读出者。 　　这种情况是经常会用到的，这就需要有一种专用于这个目的的特殊的同步元素。 　　很多情况下，你需要有个数据结构在一堆线程之间共享。当然，你希望在一个时刻只能有一个线程可以对这个数据结构执行写入操作。如果同时有多个线程能对其写入，那么就有可能一个线程写入的数据会覆盖其他线程所写入的数据。为了防止这种情况发生，写线程可以用独占的方式获取“读写锁”，也就是说只有它才能够访问这个数据结构。不过需要注意这个访问的独占性严格受控于随意的方式。也就是说这是由系统设计者来处理的，是由系统设计者来确保所有访问那个数据区域的线程通过读写锁进行同步。 　　对于读操作则是相反地。由于读操作是非破坏性的，所以可有多个线程读取数据。这里很明确的一点就是当任何线程或任何多个线程在读取数据区域的时候不能够有线程对这个数据区域执行写操作。不然的话，当一个读线程在读取部分数据区域的时候被一个写线程抢占，之后再恢复运行，继续读取数据，读到的是更新的被更新后的数据，这样会产生混乱的，结果就是数据的不一致性。 　　下面看看使用读写锁用到的函数： 　　前两个函数是用来为读写锁初始化库的内部存储区域： int pthread_rwlock_init (pthread_rwlock_t *lock, const pthread_rwlockattr_t *attr); int pthread_rwlock_destroy (pthread_rwlock_t *lock); 　　pthread_rwlock_init()函数使用了lock参数，比按照attr所设定的属性初始化这个参数。在我们的例子中，我们会使用NULL作为属性，这样就按照默认属性初始化。 　　当使用完读写锁之后，就需要使用pthread_rwlock_destory()函数对其销毁。你不能够使用一个未初始化或已被销毁的读写锁。 　　之后，我们需要获取合适类型的锁。如上所述，有两种基本类型的锁：读操作需要非独占的访问、写操作需要独占的访问。为了函数名易懂，函数名都是按照锁的类型进行命名的： int pthread_rwlock_rdlock (pthread_rwlock_t *lock); int pthread_rwlock_tryrdlock (pthread_rwlock_t *lock); int pthread_rwlock_wrlock (pthread_rwlock_t *lock); int pthread_rwlock_trywrlock (pthread_rwlock_t *lock); 　　这里一共有四个函数，而不是预想的两个。两个预想的函数是pthread_rwlock_rdlock()与pthread_rwlock_wrlock()，这两个函数分别用于读与写。这些是阻塞函数，当锁对于所选操作不可用的时候，线程就会阻塞。当锁在适当情况下可用，线程就是解除阻塞。由于线程通过该函数解除阻塞，就可以假设访问锁所保护的资源是安全的。 　　有些时候，线程可能不想被阻塞，而是查看一下它是否能够获得那个锁。这就是那个“尝试(try)”版本函数的由来。需要注意的是尝试版的函数在能够获取锁的时候会获取这个锁，而在不能获取的时候它们也不会阻塞线程，它只会返回一个错误标识。它们在能够获取锁的时候会获取锁的原因是简单的。假设有线程要获取这个锁来执行读取操作，不过不想在锁不可用的时候阻塞。这个线程就会调用pthread_rwlock_tryrdlock()函数，之后它被告知可以获得这个锁。如果这时pthread_rwlock_tryrdlock()函数不分配这个锁，就可能有坏事发生——另外一个能够抢占这个线程的线程执行了，并且这第二个线程可能会用不兼容的模式锁定了资源。由于第一个线程实际上没有获取这个锁，当其确实开始获取锁的时候，它可能就会使用pthread_rwlock_rdlock()函数，这样的话它就会被阻塞，因为该资源在这种模式下已经不可用了。所以，如果在可以获取锁的时候我们没有获取，即使使用了try版的函数，最终也有可能被阻塞。 　　最后，不论锁是被如何使用的，我们都要释放它： int pthread_rwlock_unlock (pthread_rwlock_t *lock); 　　一旦线程完成了对资源的操作之后，它必须通过调用这个函数释放锁。如果锁按照其他等待线程所需要的模式已经可用了，那么那个等待线程就会进入就绪状态。 　　需要注意我们不能够使用互斥体来完成这种类型的同步。互斥体是一个单线程的代理，这对于写操作是可以的，一旦涉及到读的情况就会完全失败，因为使用互斥体的话只能够允许一个线程执行读操作。而信号量也是不能使用的，因为使用它的话就不能分辨两者模式的访问了，信号量可以允许多个读线程的访问，不过当一个写线程试图获取它的时候，就和读线程获取它一样不能分辨出来，这样的话结果就是对一个资源可能会有多个读操作与一个或多个写操作同时存在，这是非常糟糕的！ Related posts:用于线程同步的Sleepon锁 　　在多线程程序中常遇到的另外一个情况就是让线程等待某件事的发生。这件事可以是任何事。它可以是设备上的数据就绪了，也可以是传送带到达了合适的位置或数据已经写入磁盘了，等等。另外还要讨论一下多个线程等待某个事件的情况。 　　为了实现这个功能，我们可以使用条件变量(condition variable)或是更简单的睡眠锁(sleepon lock)。 　　要使用睡眠锁，你需要执行几个操作。先看看要调用的函数，之后再看看你该如何使用这个锁： &#160; int [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2010/02/23/208.html' rel='bookmark' title='Permanent Link: 用于线程同步的Sleepon锁'>用于线程同步的Sleepon锁</a> <small>　　在多线程程序中常遇到的另外一个情况就是让线程等待某件事的发生。这件事可以是任何事。它可以是设备上的数据就绪了，也可以是传送带到达了合适的位置或数据已经写入磁盘了，等等。另外还要讨论一下多个线程等待某个事件的情况。 　　为了实现这个功能，我们可以使用条件变量(condition variable)或是更简单的睡眠锁(sleepon lock)。 　　要使用睡眠锁，你需要执行几个操作。先看看要调用的函数，之后再看看你该如何使用这个锁： &nbsp; int pthread_sleepon_lock (void); int...</small></li>
<li><a href='http://www.speedvi.net/2010/02/24/211.html' rel='bookmark' title='Permanent Link: 用于线程同步的条件变量(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>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　读写锁(readers/writer lock)的具体含义是文如其名：对一个资源有多个读者而无写入者，或者是只有一个写入者而没有其他的写入者或读出者。</p>
<p>　　这种情况是经常会用到的，这就需要有一种专用于这个目的的特殊的同步元素。</p>
<p>　　很多情况下，你需要有个数据结构在一堆线程之间共享。当然，你希望在一个时刻只能有一个线程可以对这个数据结构执行写入操作。如果同时有多个线程能对其写入，那么就有可能一个线程写入的数据会覆盖其他线程所写入的数据。为了防止这种情况发生，写线程可以用独占的方式获取“读写锁”，也就是说只有它才能够访问这个数据结构。不过需要注意这个访问的独占性严格受控于随意的方式。也就是说这是由系统设计者来处理的，是由系统设计者来确保所有访问那个数据区域的线程通过读写锁进行同步。</p>
<p><span id="more-207"></span>
<p>　　对于读操作则是相反地。由于读操作是非破坏性的，所以可有多个线程读取数据。这里很明确的一点就是当任何线程或任何多个线程在读取数据区域的时候不能够有线程对这个数据区域执行写操作。不然的话，当一个读线程在读取部分数据区域的时候被一个写线程抢占，之后再恢复运行，继续读取数据，读到的是更新的被更新后的数据，这样会产生混乱的，结果就是数据的不一致性。</p>
<p>　　下面看看使用读写锁用到的函数：</p>
<p>　　前两个函数是用来为读写锁初始化库的内部存储区域：</p>
<pre class="codesamp">int
pthread_rwlock_init (pthread_rwlock_t *<i class="var">lock</i>,
                     const pthread_rwlockattr_t *<i class="var">attr</i>);

int
pthread_rwlock_destroy (pthread_rwlock_t *<i class="var">lock</i>);</pre>
<p>　　pthread_rwlock_init()函数使用了lock参数，比按照attr所设定的属性初始化这个参数。在我们的例子中，我们会使用NULL作为属性，这样就按照默认属性初始化。</p>
<p>　　当使用完读写锁之后，就需要使用pthread_rwlock_destory()函数对其销毁。你不能够使用一个未初始化或已被销毁的读写锁。</p>
<p>　　之后，我们需要获取合适类型的锁。如上所述，有两种基本类型的锁：读操作需要非独占的访问、写操作需要独占的访问。为了函数名易懂，函数名都是按照锁的类型进行命名的：</p>
<pre class="codesamp">int
pthread_rwlock_rdlock (pthread_rwlock_t *<i class="var">lock</i>);

int
pthread_rwlock_tryrdlock (pthread_rwlock_t *<i class="var">lock</i>);

int
pthread_rwlock_wrlock (pthread_rwlock_t *<i class="var">lock</i>);

int
pthread_rwlock_trywrlock (pthread_rwlock_t *<i class="var">lock</i>);</pre>
<p>　　这里一共有四个函数，而不是预想的两个。两个预想的函数是pthread_rwlock_rdlock()与pthread_rwlock_wrlock()，这两个函数分别用于读与写。这些是阻塞函数，当锁对于所选操作不可用的时候，线程就会阻塞。当锁在适当情况下可用，线程就是解除阻塞。由于线程通过该函数解除阻塞，就可以假设访问锁所保护的资源是安全的。</p>
<p>　　有些时候，线程可能不想被阻塞，而是查看一下它是否能够获得那个锁。这就是那个“尝试(try)”版本函数的由来。需要注意的是尝试版的函数在能够获取锁的时候会获取这个锁，而在不能获取的时候它们也不会阻塞线程，它只会返回一个错误标识。它们在能够获取锁的时候会获取锁的原因是简单的。假设有线程要获取这个锁来执行读取操作，不过不想在锁不可用的时候阻塞。这个线程就会调用pthread_rwlock_tryrdlock()函数，之后它被告知可以获得这个锁。如果这时pthread_rwlock_tryrdlock()函数不分配这个锁，就可能有坏事发生——另外一个能够抢占这个线程的线程执行了，并且这第二个线程可能会用不兼容的模式锁定了资源。由于第一个线程实际上没有获取这个锁，当其确实开始获取锁的时候，它可能就会使用pthread_rwlock_rdlock()函数，这样的话它就会被阻塞，因为该资源在这种模式下已经不可用了。所以，如果在可以获取锁的时候我们没有获取，即使使用了try版的函数，最终也有可能被阻塞。</p>
<p>　　最后，不论锁是被如何使用的，我们都要释放它：</p>
<pre class="codesamp">int
pthread_rwlock_unlock (pthread_rwlock_t *<i class="var">lock</i>);</pre>
<p>　　一旦线程完成了对资源的操作之后，它必须通过调用这个函数释放锁。如果锁按照其他等待线程所需要的模式已经可用了，那么那个等待线程就会进入就绪状态。</p>
<p>　　需要注意我们不能够使用互斥体来完成这种类型的同步。互斥体是一个单线程的代理，这对于写操作是可以的，一旦涉及到读的情况就会完全失败，因为使用互斥体的话只能够允许一个线程执行读操作。而信号量也是不能使用的，因为使用它的话就不能分辨两者模式的访问了，信号量可以允许多个读线程的访问，不过当一个写线程试图获取它的时候，就和读线程获取它一样不能分辨出来，这样的话结果就是对一个资源可能会有多个读操作与一个或多个写操作同时存在，这是非常糟糕的！</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2010/02/23/208.html' rel='bookmark' title='Permanent Link: 用于线程同步的Sleepon锁'>用于线程同步的Sleepon锁</a> <small>　　在多线程程序中常遇到的另外一个情况就是让线程等待某件事的发生。这件事可以是任何事。它可以是设备上的数据就绪了，也可以是传送带到达了合适的位置或数据已经写入磁盘了，等等。另外还要讨论一下多个线程等待某个事件的情况。 　　为了实现这个功能，我们可以使用条件变量(condition variable)或是更简单的睡眠锁(sleepon lock)。 　　要使用睡眠锁，你需要执行几个操作。先看看要调用的函数，之后再看看你该如何使用这个锁： &nbsp; int pthread_sleepon_lock (void); int...</small></li>
<li><a href='http://www.speedvi.net/2010/02/24/211.html' rel='bookmark' title='Permanent Link: 用于线程同步的条件变量(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>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2010/02/21/207.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在SMP系统使用多线程需要注意的事情</title>
		<link>http://www.speedvi.net/2010/01/28/202.html</link>
		<comments>http://www.speedvi.net/2010/01/28/202.html#comments</comments>
		<pubDate>Thu, 28 Jan 2010 03:18:55 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[SMP]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[实时系统]]></category>
		<category><![CDATA[系统内核]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/2010/01/28/202.html</guid>
		<description><![CDATA[　　尽管你一般可以忽略你的系统是运行在SMP架构上还是单处理器上，不过还是有些事情会影响到你。不幸的是，这些事情都是低概率事件，它们可能在你的开发阶段没有出现，但是可能在测试、演示或更糟的，在实际应用中出现。在编程的时候花些时间做些防御性的措施可以在后续的阶段减少问题的发生几率。 　　下面就是你可能在SMP系统上遇到的事情： 多个线程确实可以也能同时运行——不过依赖于像FIFO调度、优先级这些东西来同步是不允许的； 多线程可与中断服务程序（ISR）同时运行——也就是说你不但要保护线程不受ISR的影响，也要保护中断服务程序不受线程的影响； 有些操作你期望是最小单元的、单步的，可能会依赖于操作与处理器的不同而不是最小单元的、单步的。这类的操作包括了需要“读取-修改-写入”周期操作的（例如：++，&#8211;，&#124;=，&#38;=等等）。你可以查找&#60;atomic.h&#62;文件来查找对应的最小单元替换函数。（这也不是只有SMP系统的问题，大多数的RISC处理器对上面的胆码也不是按照最小单元处理的） Related posts:进程间通信的发/收/回复的健壮实现 　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。... 内核状态 RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &#160; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。... 用于线程同步的读写锁（Readers/writer locks） 　　读写锁(readers/writer lock)的具体含义是文如其名：对一个资源有多个读者而无写入者，或者是只有一个写入者而没有其他的写入者或读出者。 　　这种情况是经常会用到的，这就需要有一种专用于这个目的的特殊的同步元素。 　　很多情况下，你需要有个数据结构在一堆线程之间共享。当然，你希望在一个时刻只能有一个线程可以对这个数据结构执行写入操作。如果同时有多个线程能对其写入，那么就有可能一个线程写入的数据会覆盖其他线程所写入的数据。为了防止这种情况发生，写线程可以用独占的方式获取“读写锁”，也就是说只有它才能够访问这个数据结构。不过需要注意这个访问的独占性严格受控于随意的方式。也就是说这是由系统设计者来处理的，是由系统设计者来确保所有访问那个数据区域的线程通过读写锁进行同步。 　　对于读操作则是相反地。由于读操作是非破坏性的，所以可有多个线程读取数据。这里很明确的一点就是当任何线程或任何多个线程在读取数据区域的时候不能够有线程对这个数据区域执行写操作。不然的话，当一个读线程在读取部分数据区域的时候被一个写线程抢占，之后再恢复运行，继续读取数据，读到的是更新的被更新后的数据，这样会产生混乱的，结果就是数据的不一致性。 　　下面看看使用读写锁用到的函数： 　　前两个函数是用来为读写锁初始化库的内部存储区域： int pthread_rwlock_init (pthread_rwlock_t...


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/27/173.html' rel='bookmark' title='Permanent Link: 进程间通信的发/收/回复的健壮实现'>进程间通信的发/收/回复的健壮实现</a> <small>　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。...</small></li>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
<li><a href='http://www.speedvi.net/2010/02/21/207.html' rel='bookmark' title='Permanent Link: 用于线程同步的读写锁（Readers/writer locks）'>用于线程同步的读写锁（Readers/writer locks）</a> <small>　　读写锁(readers/writer lock)的具体含义是文如其名：对一个资源有多个读者而无写入者，或者是只有一个写入者而没有其他的写入者或读出者。 　　这种情况是经常会用到的，这就需要有一种专用于这个目的的特殊的同步元素。 　　很多情况下，你需要有个数据结构在一堆线程之间共享。当然，你希望在一个时刻只能有一个线程可以对这个数据结构执行写入操作。如果同时有多个线程能对其写入，那么就有可能一个线程写入的数据会覆盖其他线程所写入的数据。为了防止这种情况发生，写线程可以用独占的方式获取“读写锁”，也就是说只有它才能够访问这个数据结构。不过需要注意这个访问的独占性严格受控于随意的方式。也就是说这是由系统设计者来处理的，是由系统设计者来确保所有访问那个数据区域的线程通过读写锁进行同步。 　　对于读操作则是相反地。由于读操作是非破坏性的，所以可有多个线程读取数据。这里很明确的一点就是当任何线程或任何多个线程在读取数据区域的时候不能够有线程对这个数据区域执行写操作。不然的话，当一个读线程在读取部分数据区域的时候被一个写线程抢占，之后再恢复运行，继续读取数据，读到的是更新的被更新后的数据，这样会产生混乱的，结果就是数据的不一致性。 　　下面看看使用读写锁用到的函数： 　　前两个函数是用来为读写锁初始化库的内部存储区域： int pthread_rwlock_init (pthread_rwlock_t...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　尽管你一般可以忽略你的系统是运行在SMP架构上还是单处理器上，不过还是有些事情会影响到你。不幸的是，这些事情都是低概率事件，它们可能在你的开发阶段没有出现，但是可能在测试、演示或更糟的，在实际应用中出现。在编程的时候花些时间做些防御性的措施可以在后续的阶段减少问题的发生几率。</p>
<p>　　下面就是你可能在SMP系统上遇到的事情：</p>
<ul>
<li>多个线程确实可以也能同时运行——不过依赖于像FIFO调度、优先级这些东西来同步是不允许的；</li>
<li>多线程可与中断服务程序（ISR）同时运行——也就是说你不但要保护线程不受ISR的影响，也要保护中断服务程序不受线程的影响；</li>
<li>有些操作你期望是最小单元的、单步的，可能会依赖于操作与处理器的不同而不是最小单元的、单步的。这类的操作包括了需要“读取-修改-写入”周期操作的（例如：++，&#8211;，|=，&amp;=等等）。你可以查找&lt;atomic.h&gt;文件来查找对应的最小单元替换函数。（这也不是只有SMP系统的问题，大多数的RISC处理器对上面的胆码也不是按照最小单元处理的）</li>
</ul>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/27/173.html' rel='bookmark' title='Permanent Link: 进程间通信的发/收/回复的健壮实现'>进程间通信的发/收/回复的健壮实现</a> <small>　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。...</small></li>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
<li><a href='http://www.speedvi.net/2010/02/21/207.html' rel='bookmark' title='Permanent Link: 用于线程同步的读写锁（Readers/writer locks）'>用于线程同步的读写锁（Readers/writer locks）</a> <small>　　读写锁(readers/writer lock)的具体含义是文如其名：对一个资源有多个读者而无写入者，或者是只有一个写入者而没有其他的写入者或读出者。 　　这种情况是经常会用到的，这就需要有一种专用于这个目的的特殊的同步元素。 　　很多情况下，你需要有个数据结构在一堆线程之间共享。当然，你希望在一个时刻只能有一个线程可以对这个数据结构执行写入操作。如果同时有多个线程能对其写入，那么就有可能一个线程写入的数据会覆盖其他线程所写入的数据。为了防止这种情况发生，写线程可以用独占的方式获取“读写锁”，也就是说只有它才能够访问这个数据结构。不过需要注意这个访问的独占性严格受控于随意的方式。也就是说这是由系统设计者来处理的，是由系统设计者来确保所有访问那个数据区域的线程通过读写锁进行同步。 　　对于读操作则是相反地。由于读操作是非破坏性的，所以可有多个线程读取数据。这里很明确的一点就是当任何线程或任何多个线程在读取数据区域的时候不能够有线程对这个数据区域执行写操作。不然的话，当一个读线程在读取部分数据区域的时候被一个写线程抢占，之后再恢复运行，继续读取数据，读到的是更新的被更新后的数据，这样会产生混乱的，结果就是数据的不一致性。 　　下面看看使用读写锁用到的函数： 　　前两个函数是用来为读写锁初始化库的内部存储区域： int pthread_rwlock_init (pthread_rwlock_t...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2010/01/28/202.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>内核状态</title>
		<link>http://www.speedvi.net/2009/12/27/184.html</link>
		<comments>http://www.speedvi.net/2009/12/27/184.html#comments</comments>
		<pubDate>Sat, 26 Dec 2009 16:46:35 +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/27/184.html</guid>
		<description><![CDATA[RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &#160; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。 　　如果有多个线程阻塞于某个互斥体（属于MUTEX阻塞状态），系统内核就不会特别留意它们，直到拥有该互斥体的线程释放了该互斥体。那时，这些阻塞线程之一就进入READY状态，系统内核在必要情况下完成调度决定。 　　为什么说必要情况下呢？如果释放互斥体的线程还有其他事要做并且相对于等待线程有更高的优先级，这时，我们就要遵守第二条规则了。“最高优先级的就绪线程运行”。也就是说，调度顺序没有改变，较高优先级的线程继续运行。 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的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递... 进程间通信的消息复制 　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU... 进程间通信的通道与连接 　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><strong>RUNNING</strong></p>
<p>　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。</p>
<p><strong>READY</strong></p>
<p><strong>　　</strong>就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。</p>
<p><strong></strong>&nbsp;</p>
<p><span id="more-184"></span>
<p><strong>阻塞状态(blocked states)</strong></p>
<p><strong>　　</strong>我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。</p>
<p>　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。</p>
<p>　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。</p>
<p>　　如果有多个线程阻塞于某个互斥体（属于MUTEX阻塞状态），系统内核就不会特别留意它们，直到拥有该互斥体的线程释放了该互斥体。那时，这些阻塞线程之一就进入READY状态，系统内核在必要情况下完成调度决定。</p>
<p>　　为什么说必要情况下呢？如果释放互斥体的线程还有其他事要做并且相对于等待线程有更高的优先级，这时，我们就要遵守第二条规则了。“最高优先级的就绪线程运行”。也就是说，调度顺序没有改变，较高优先级的线程继续运行。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/12/27/184.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>进程间通信的发/收/回复的健壮实现</title>
		<link>http://www.speedvi.net/2009/09/27/173.html</link>
		<comments>http://www.speedvi.net/2009/09/27/173.html#comments</comments>
		<pubDate>Sun, 27 Sep 2009 07:20:04 +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/27/173.html</guid>
		<description><![CDATA[　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。 　　但是，高层的线程如何提示底层线程它已经有了以前的操作请求的结果了？（假如底层线程在上一次发送之后不想继续等待回复结果。） 　　UNIX系统提供了一个非常灵活的架构，通过使用MsgDeliverEvent()这个内核调用来发布非阻塞的事件。所有异步服务都可以使用这种方式完成。 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的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递... 进程间通信的消息复制 　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU... 进程间通信的通道与连接 　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。</p>
<p>　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。</p>
<p><span id="more-173"></span>
<p>　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。</p>
<p>　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。</p>
<p>　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了：</p>
<ol>
<li>永远不让两个线程互发；</li>
<li>一直使用继承的方式排列线程，只向继承树的上级发送。</li>
<p>　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示：</ol>
<ol><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="thtree" border="0" alt="thtree" src="http://www.speedvi.net/wp-content/uploads/2009/09/thtree.jpg" width="259" height="163"> </ol>
<p>　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。</p>
<p>　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。</p>
<p>　　但是，高层的线程如何提示底层线程它已经有了以前的操作请求的结果了？（假如底层线程在上一次发送之后不想继续等待回复结果。）</p>
<p>　　UNIX系统提供了一个非常灵活的架构，通过使用MsgDeliverEvent()这个内核调用来发布非阻塞的事件。所有异步服务都可以使用这种方式完成。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/27/173.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>进程间通信的通道与连接</title>
		<link>http://www.speedvi.net/2009/09/25/170.html</link>
		<comments>http://www.speedvi.net/2009/09/25/170.html#comments</comments>
		<pubDate>Fri, 25 Sep 2009 08:02:18 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[传输]]></category>
		<category><![CDATA[消息]]></category>
		<category><![CDATA[系统内核]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[连接]]></category>
		<category><![CDATA[通信]]></category>
		<category><![CDATA[通道]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/2009/09/25/170.html</guid>
		<description><![CDATA[　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示： 1 2 3 4 5 6 7 8 9 10 11 chid = ChannelCreate&#40;flags&#41;; SETIOV&#40;&#38;amp;iov, &#38;amp;msg, sizeof&#40;msg&#41;&#41;; for&#40;;;&#41; &#123; rcv_id = MsgReceivev&#40; chid, &#38;amp;iov, parts, &#38;amp;info &#41;; &#160; switch&#40; msg.type &#41; &#123; /* Perform message processing here */ &#125; &#160; MsgReplyv&#40; rcv_id, &#38;amp;iov, rparts &#41;; [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/27/173.html' rel='bookmark' title='Permanent Link: 进程间通信的发/收/回复的健壮实现'>进程间通信的发/收/回复的健壮实现</a> <small>　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。</p>
<p>　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。</p>
<p>　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。</p>
<p>　　这里可以使用的函数如下：</p>
<p><span id="more-170"></span>
</p>
<p>　　ChannelCreate() 创建一个通道来接收消息；<br />　　<i>ChannelDestroy() </i>清除一个通道；<br />　　<i>ConnectAttach() </i>创建一个连接来发送消息；<br />　　<em>ConnectDetach() </em>释放一个连接。</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="connect" border="0" alt="connect" src="http://www.speedvi.net/wp-content/uploads/2009/09/connect.jpg" width="368" height="215"> </p>
<p>　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="c" style="font-family:monospace;">chid <span style="color: #339933;">=</span> ChannelCreate<span style="color: #009900;">&#40;</span>flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
SETIOV<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>iov<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>msg<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>msg<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">;;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    rcv_id <span style="color: #339933;">=</span> MsgReceivev<span style="color: #009900;">&#40;</span> chid<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>iov<span style="color: #339933;">,</span> parts<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>info <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">switch</span><span style="color: #009900;">&#40;</span> msg.<span style="color: #202020;">type</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* Perform message processing here */</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
    MsgReplyv<span style="color: #009900;">&#40;</span> rcv_id<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>iov<span style="color: #339933;">,</span> rparts <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>&nbsp;</p>
<p>　　在循环中，线程可以接受连接到通道的所有线程的消息。</p>
<p>　　通道有几个与其关联的消息列表：</p>
<ul>
<li>接收：一个LIFO的等待消息的线程队列；</li>
<li>接收：一个有优先权的线程FIFO队列，这些线程发送了消息当还未被接收；</li>
<li>回复：一个无序的线程列表，这些线程发送的消息已被接受但还未被回复。</li>
</ul>
<p>　　在上面的任何一个列表中的等待队列都是阻塞状态(RECEIVE-、SEND-或REPLY-阻塞)。多个线程或客户端可能在等待同一个通道。</p>
<h6>脉冲</h6>
<p>　　除了同步的发送/接收/回复服务外，操作系统也提供了规定大小、非阻塞的消息。这种消息一般被称为脉冲并有小的体积（四个字节的数据和一个单字节的代码）。</p>
<p>　　脉冲的载荷相对来说很小——八位的代码以及32位的数据。脉冲常被用于中断处理器中的通知机制。也可以用于服务器端通知客户端而不阻塞它们。</p>
<p>　　一个脉冲的示意图如下：</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="pulse" border="0" alt="pulse" src="http://www.speedvi.net/wp-content/uploads/2009/09/pulse.jpg" width="269" height="78"> </p>
<h6>优先级继承与消息</h6>
<p>　　服务器进程按照优先级来接收消息与脉冲。当服务器进程中的线程接收到请求之后，它们就继承了发送消息的线程的优先级，而不是调度算法的优先级。这样，请求服务器端操作的线程的相对优先级就被保存了，服务器端的操作就会按照合适的优先级运行。这种消息驱动的优先级继承避免了优先级反转的问题。</p>
<p>　　例如，假如系统包含了如下线程：</p>
<ul>
<li>一个服务器线程，优先级为22；</li>
<li>一个客户线程，T1，优先级为13；</li>
<li>一个客户线程，T2，优先级为10.</li>
</ul>
<p>　　如果没有优先级继承，如果T2向服务器线程发送了一个消息，它所请求的动作就会在优先级22上执行，这样的话T2的优先级就被反转了。</p>
<p>　　实际上发生的是，当服务器端接收到一个消息，它的有效优先级就变为最高优先级发送者的优先级。在这里，T2的优先级要低于服务器端的优先级，这样当服务器端接收到消息之后其有效优先级就会发生变化。</p>
<p>　　接着，假设T1在服务器端的优先级还在10的时候发送了一个消息。由于T1的优先级要比服务器端的当前优先级高，当T1发送消息的时候服务器端的优先级就发生改变。</p>
<p>　　在服务器端接收到消息之前就改变优先级是为了防止又一次优先级反转的情况。如果服务器端的优先级保持低于10，而另一个线程T3开始以优先级11运行，服务器就要等待T3直到其能够有CPU时间来使其最终能够接收到T1的消息。这样的话，T1就会被低优先级的T3线程所延迟。</p>
<p>　　在调用ChannelCreate()函数的时候，可以通过设定——NTO_CHF_FIXED_PRIORITY标志来关闭优先级继承。如果使用自适应分块，这个标志将导致接收线程不在发送线程的分块中运行。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/27/173.html' rel='bookmark' title='Permanent Link: 进程间通信的发/收/回复的健壮实现'>进程间通信的发/收/回复的健壮实现</a> <small>　　通过使用发/收/回复将共同合作的线程与进程作为一个团队来构建一个UNIX应用程序的架构就会得到一个同步通知的系统。进程间通信就为这个系统的特殊转型而产生的，而不是在其后。 　　异步系统的一个重要问题就是事件通知需要依靠信号处理器才能运行。异步的进程间通信将难于进行彻底的系统运行测试，并不能够确保不论信号处理器怎样，处理工作能够如预想的那样运行。应用程序一般都是依赖一个确定的起始点的“窗口”，在这个窗口中信号的延迟是可以忍受的，来避免出现这种情况。 　　如果使用基于发/收/回复的同步、无队列的系统架构，健壮的应用程序架构就能够很容易的实现并交付。 　　使用队列IPC、共享内存以及其他同步原的各种组合来构建应用程序的另外一个关键问题就是如何避免死锁状态。例如，线程A在线程B释放复用体2之前不会释放复用体1，而且不幸的是线程B在线程A释放复用体1之前也不会释放复用体2，这时死锁就出现了。经常激活模拟工具来确保在系统运行时没有死锁的情况发生。 　　发/收/回复这些IPC原在遵守以下简单原则的话就能够构建无死锁的系统了： 永远不让两个线程互发； 一直使用继承的方式排列线程，只向继承树的上级发送。 　　第一条就避免了死锁情况发生，第二条则需要详细解释。合作的线程与进程的排列如下图所示： 　　在这里可以看到，任何级别的线程都不互相之间发送，只向其上层发送。 　　一个例子就是向数据库服务器进程发信的客户应用程序，它按序向文件系统经常发送信息。由于发送线程阻塞并等待回复，而目标线程没有发送阻塞就不会产生死锁。...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/25/170.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>进程间通信的消息复制</title>
		<link>http://www.speedvi.net/2009/09/25/163.html</link>
		<comments>http://www.speedvi.net/2009/09/25/163.html#comments</comments>
		<pubDate>Fri, 25 Sep 2009 06:17:30 +0000</pubDate>
		<dc:creator>行者</dc:creator>
				<category><![CDATA[操作系统]]></category>
		<category><![CDATA[传递]]></category>
		<category><![CDATA[消息]]></category>
		<category><![CDATA[系统内核]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[通信]]></category>

		<guid isPermaLink="false">http://www.speedvi.net/?p=163</guid>
		<description><![CDATA[　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU page flipping)的页面对齐内存池中进行分配。这样，完成客户与服务器进程之间的API的库例程就可以很简单的进行表述，而不用调用IPC特定的内存分配函数了。 　　例如，一个客户线程用来请求文件系统管理器作为其代理来执行lseek的代码如下： #include &#60;unistd.h&#62; #include &#60;errno.h&#62; #include &#60;sys/iomsg.h&#62; &#160; off64_t lseek64&#40;int fd, off64_t offset, int whence&#41; &#123; io_lseek_t msg; off64_t off; &#160; msg.i.type = _IO_LSEEK; msg.i.combine_len = sizeof msg.i; msg.i.offset = offset; msg.i.whence = whence; msg.i.zero = 0; if&#40;MsgSend&#40;fd, &#38;amp;msg.i, sizeof msg.i, &#38;amp;off, sizeof [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/28/177.html' rel='bookmark' title='Permanent Link: 进程间通信的共享内存'>进程间通信的共享内存</a> <small>　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。 　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。 用于消息传递的共享内存 　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC： 非常高的性能(共享内存) 同步(消息传递) 网络透明(消息传递) &nbsp;...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。</p>
<p>　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。</p>
<p>　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。</p>
<p>　　多段传送的示意图如下：<span id="more-163"></span></p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="messcopy" src="http://www.speedvi.net/wp-content/uploads/2009/09/messcopy.gif" border="0" alt="messcopy" width="310" height="126" />  多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。</p>
<p>　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下：</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="scatter" src="http://www.speedvi.net/wp-content/uploads/2009/09/scatter.jpg" border="0" alt="scatter" width="260" height="302" /></p>
<p>　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU page flipping)的页面对齐内存池中进行分配。这样，完成客户与服务器进程之间的API的库例程就可以很简单的进行表述，而不用调用IPC特定的内存分配函数了。</p>
<p>　　例如，一个客户线程用来请求文件系统管理器作为其代理来执行lseek的代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#include &lt;unistd.h&gt;</span>
<span style="color: #339933;">#include &lt;errno.h&gt;</span>
<span style="color: #339933;">#include &lt;sys/iomsg.h&gt;</span>
&nbsp;
off64_t lseek64<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> off64_t offset<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> whence<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    io_lseek_t                            msg<span style="color: #339933;">;</span>
    off64_t                               off<span style="color: #339933;">;</span>
&nbsp;
    msg.<span style="color: #202020;">i</span>.<span style="color: #202020;">type</span> <span style="color: #339933;">=</span> _IO_LSEEK<span style="color: #339933;">;</span>
    msg.<span style="color: #202020;">i</span>.<span style="color: #202020;">combine_len</span> <span style="color: #339933;">=</span> <span style="color: #993333;">sizeof</span> msg.<span style="color: #202020;">i</span><span style="color: #339933;">;</span>
    msg.<span style="color: #202020;">i</span>.<span style="color: #202020;">offset</span> <span style="color: #339933;">=</span> offset<span style="color: #339933;">;</span>
    msg.<span style="color: #202020;">i</span>.<span style="color: #202020;">whence</span> <span style="color: #339933;">=</span> whence<span style="color: #339933;">;</span>
    msg.<span style="color: #202020;">i</span>.<span style="color: #202020;">zero</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>MsgSend<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>msg.<span style="color: #202020;">i</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span> msg.<span style="color: #202020;">i</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>off<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span> off<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">return</span> off<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
off64_t tell64<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> lseek64<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> SEEK_CUR<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
off_t lseek<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> off_t offset<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> whence<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> lseek64<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> offset<span style="color: #339933;">,</span> whence<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
off_t tell<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> lseek64<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> SEEK_CUR<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>



<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/160.html' rel='bookmark' title='Permanent Link: 进程间通信'>进程间通信</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/28/177.html' rel='bookmark' title='Permanent Link: 进程间通信的共享内存'>进程间通信的共享内存</a> <small>　　共享内存提供了进程间通信所能实现的最高带宽。一个共享内存对象创建之后，可以访问这个对象的进程就能够使用指针直接对其读写。这就意味着，共享内存访问本身就是非同步的。如果一个进程更新共享内存的一个区域，就必须特别小心不要让其他进程读取或更新同一块区域。即使是最简单的读取操作时，其他进程仍然有可能读到变化与不稳定的数据。 　　为了解决这个问题，共享内存就常与其他同步原(synchronization primitives)结合在一起使用以使进程之间的内存更新原子化。如果更新的间距很小，同步原就会限制自己固有的使用共享内存的高带宽。共享内存用于以块的模式更新大量的数据是最有效的。 　　信号量(semaphores)与互斥体(mutexes)都是适用与共享内存结合使用的同步原。信号量是在创建进程间同步的POSIX实时标准时引入的。互斥体则是在创建线程同步的POSIX标准是引入的。互斥体也可以在不同进程中的线程之间使用。POSIX将其作为一个可选的能力，我们在这里则是支持的。一般来说，互斥体要比信号量效率更高。 用于消息传递的共享内存 　　共享内存与消息传递可以结合起来以提供支持以下功能的IPC： 非常高的性能(共享内存) 同步(消息传递) 网络透明(消息传递) &nbsp;...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/25/163.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>进程间通信</title>
		<link>http://www.speedvi.net/2009/09/25/160.html</link>
		<comments>http://www.speedvi.net/2009/09/25/160.html#comments</comments>
		<pubDate>Fri, 25 Sep 2009 04:45:47 +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/25/160.html</guid>
		<description><![CDATA[　　进程间通信的缩写是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的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。 同步信息传递 　　一个线程向可能位于其他进程的线程使用MsgSend()函数后，在目标线程使用MsgReceive()函数，处理消息并执行MsgReply()函数这些动作完成之前，该发送消息的线程将被阻塞。如果一个线程在没有收到消息之前就执行了MsgReceive()函数，那么它就会一直处于阻塞状态直到其他的线程执行了MsgSend()函数。 　　在UNIX系统中，一个服务器线程会一直循环，等待接收从客户线程发来的消息。一个线程在可以使用CPU的时候就是处于就绪(READY)状态。在实际中，它可能由于它与其他线程的优先级或是调度算法的原因而得不到任何CPU时间，不过这个线程并没有被阻塞。 　　下面看一下客户线程的示意图： 如果客户线程调用MsgSend()，而服务器线程还没有调用MsgReceive()函数，这时客户线程的状态就变为发送阻塞状态(SEND blocked)。一旦服务器线程调用了MsgReceive()函数，系统内核就会将客户线程的状态变为回复阻塞(REPLY blocked)，这就意味着服务器线程已经接受到消息了，现在必须要回复。当服务器线程调用了MsgReply()函数，客户线程就会变为就绪状态。 如果客户线程调用MsgSend()函数，而服务器线程早就因为调用MsgReceive()函数而阻塞，那么客户线程就直接进入回复阻塞状态，完全跳过了发送阻塞状态。 如果服务器线程失败、退出或消失，客户线程就变为就绪状态，同时MsgSend()函数就会返回一个错误。 　　下面看一下服务器线程的示意图： 如果服务器线程调用了MsgReceive()函数，而没有其他线程向其发送消息，那么该线程就进入接收阻塞状态(RECEIVE blocked)。当其他线程向其发送消息之后，该服务器线程就变为就绪状态。 如果服务器线程调用了MsgReceive()函数，而其他线程早就想起发送了消息，那么MsgReceive()函数就会立即返回该消息。在这种情况下，服务器线程并不会进入阻塞状态。 如果服务器线程调用了MsgReply()函数，该线程不阻塞。 　　这种内置的阻塞机制通过发送数据使得发送线程进入阻塞状态并调度接收线程开始运行，从而同步发送线程的运行。这些都不用通过直接调用内核来确定哪个线程运行（而在其他的IPC形式中是需要调用内核来完成该动作的）。线程运行以及数据转移都是从一个环境到另一个环境直接进行的。 　　在这些消息原中，数据队列能力是被取消的，因为在必要的时候可以在接收线程内部来完成数据队列。发送线程一般都是准备接收反应的；这时队列就是过于复杂并添加额外开销的（例如，可能会减慢非队列情况下的处理速度）。这样设计的最终结果就是，发送线程不必创建一个分开、特定的块函数来等待反应（在其他形式的IPC中会用到这种方式的）。 　　发送与接收函数会造成线程阻塞以及同步，可是MsgReply()函数以及MsgError()函数则不会阻塞线程。由于客户线程早就因为等待回复而进入阻塞状态，就不需要额外的同步机制了，所以一个由MsgReply()产生的阻塞就没有必要了。这样就可以让服务器线程回复客户线程后继续处理，同时内核或网络代码就可以异步的将回复数据传送给发送线程并使其进入就绪运行状态。由于大多数服务器线程都要进行一下处理以准备接收下一个请求（在接收下一个请求时它们会在被阻塞），这种机制就是合适的。 MsgReplay()与MsgError() 　　MsgReply()函数用于向客户线程返回一个状态以及0个或多个比特的数据。MsgError()函数则只是返回一个状态。这两个函数都能够解除客户线程由于MsgSend()函数而进入的阻塞状态。 Related posts:进程间通信的消息复制 　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU... 内核状态 RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &#160; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。... 进程间通信的通道与连接 　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 [...]


Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>　　进程间通信的缩写是IPC，不缩写的就是Interprocess Communication。进程间通信是将实时操作系统微内核转换为全面的POSIX系统中的基本元素之一。当多种服务进程添加到微内核的时候，进程间通信就是将这些元件结合为一个整体的“胶水”。</p>
<p>　　在UNIX操作系统中消息传递是IPC的主要形式，除此之外还有其他形式的IPC。除非特别声明，这些其他形式的IPC都是建立在本地的消息传递机制之上的。这里使用的策略就是创建一个简单、强健的IPC服务，这个服务可以在微内核里面通过简化代码就可以调整其性能，之后功能更多的IPC服务就可以以此为基础加以完成。</p>
<p>　　通过将高级的IPC服务（例如基于消息的管道(pipes)以及先进先出(FIFO)）与大内核的同类服务进行性能测试比较得到的结果是其性能是相当的。</p>
<p>　　UNIX提供的IPC形式包括了基于内核的消息传递(Message-passing)、基于内核的信号(Signals)、基于外部线程的POSIX消息队列(POSIX message queues)、基于线程管理器的共享内存(Shared memory)、基于外部线程的管道(Pipes)以及基于外部线程的先进先出(FIFOs)。系统设计师可以基于带宽需求、队列需求、网络透明性等因素来从这些服务中挑选合适的形式。选择某种形式的副作用是复杂的，不过灵活性是可以保证的。</p>
<p>　　作为定义UNIX系统内核的工程努力的一部分，将消息传递作为IPC的基础元素是经过深思熟虑的。作为进程间消息传递的一种形式，消息传递是用来同步与复制数据的。</p>
<h5>同步信息传递</h5>
<p>　　一个线程向可能位于其他进程的线程使用<em>MsgSend()</em>函数后，在目标线程使用<em>MsgReceive()</em>函数，处理消息并执行<em>MsgReply()</em>函数这些动作完成之前，该发送消息的线程将被阻塞。如果一个线程在没有收到消息之前就执行了<em>MsgReceive()</em>函数，那么它就会一直处于阻塞状态直到其他的线程执行了<em>MsgSend()</em>函数。</p>
<p>　　在UNIX系统中，一个服务器线程会一直循环，等待接收从客户线程发来的消息。一个线程在可以使用CPU的时候就是处于就绪(READY)状态。在实际中，它可能由于它与其他线程的优先级或是调度算法的原因而得不到任何CPU时间，不过这个线程并没有被阻塞。</p>
<p><span id="more-160"></span>
</p>
<p>　　下面看一下客户线程的示意图：</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="State Client" border="0" alt="State Client" src="http://www.speedvi.net/wp-content/uploads/2009/09/states_client.gif" width="340" height="210"> </p>
<ul>
<li>如果客户线程调用<em>MsgSend()</em>，而服务器线程还没有调用<em>MsgReceive()</em>函数，这时客户线程的状态就变为发送阻塞状态(SEND blocked)。一旦服务器线程调用了<em>MsgReceive()</em>函数，系统内核就会将客户线程的状态变为回复阻塞(REPLY blocked)，这就意味着服务器线程已经接受到消息了，现在必须要回复。当服务器线程调用了<em>MsgReply()</em>函数，客户线程就会变为就绪状态。
<li>如果客户线程调用<em>MsgSend()</em>函数，而服务器线程早就因为调用<em>MsgReceive()</em>函数而阻塞，那么客户线程就直接进入回复阻塞状态，完全跳过了发送阻塞状态。
<li>如果服务器线程失败、退出或消失，客户线程就变为就绪状态，同时<em>MsgSend()</em>函数就会返回一个错误。</li>
</ul>
<p>　　下面看一下服务器线程的示意图：</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="states_server" border="0" alt="states_server" src="http://www.speedvi.net/wp-content/uploads/2009/09/states_server.gif" width="355" height="175"> </p>
</p>
<ul>
<li>如果服务器线程调用了<em>MsgReceive()</em>函数，而没有其他线程向其发送消息，那么该线程就进入接收阻塞状态(RECEIVE blocked)。当其他线程向其发送消息之后，该服务器线程就变为就绪状态。
<li>如果服务器线程调用了<em>MsgReceive()</em>函数，而其他线程早就想起发送了消息，那么<em>MsgReceive()</em>函数就会立即返回该消息。在这种情况下，服务器线程并不会进入阻塞状态。
<li>如果服务器线程调用了<em><u>MsgReply</u>()</em>函数，该线程不阻塞。</li>
</ul>
<p>　　这种内置的阻塞机制通过发送数据使得发送线程进入阻塞状态并调度接收线程开始运行，从而同步发送线程的运行。这些都不用通过直接调用内核来确定哪个线程运行（而在其他的IPC形式中是需要调用内核来完成该动作的）。线程运行以及数据转移都是从一个环境到另一个环境直接进行的。</p>
<p>　　在这些消息原中，数据队列能力是被取消的，因为在必要的时候可以在接收线程内部来完成数据队列。发送线程一般都是准备接收反应的；这时队列就是过于复杂并添加额外开销的（例如，可能会减慢非队列情况下的处理速度）。这样设计的最终结果就是，发送线程不必创建一个分开、特定的块函数来等待反应（在其他形式的IPC中会用到这种方式的）。</p>
<p>　　发送与接收函数会造成线程阻塞以及同步，可是<em>MsgReply()</em>函数以及<em>MsgError()</em>函数则不会阻塞线程。由于客户线程早就因为等待回复而进入阻塞状态，就不需要额外的同步机制了，所以一个由<em>MsgReply()</em>产生的阻塞就没有必要了。这样就可以让服务器线程回复客户线程后继续处理，同时内核或网络代码就可以异步的将回复数据传送给发送线程并使其进入就绪运行状态。由于大多数服务器线程都要进行一下处理以准备接收下一个请求（在接收下一个请求时它们会在被阻塞），这种机制就是合适的。</p>
<h6><em>MsgReplay()</em>与<em>MsgError()</em></h6>
<p>　　<em>MsgReply()</em>函数用于向客户线程返回一个状态以及0个或多个比特的数据。<em>MsgError()</em>函数则只是返回一个状态。这两个函数都能够解除客户线程由于<em>MsgSend()</em>函数而进入的阻塞状态。</p>


<p>Related posts:<ol><li><a href='http://www.speedvi.net/2009/09/25/163.html' rel='bookmark' title='Permanent Link: 进程间通信的消息复制'>进程间通信的消息复制</a> <small>　　在UNIX中，消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间，没有也不通过中间的缓存，这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容，系统内核没有赋予其特别的含义，只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此，系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。 　　消息原(message primitives)支持多段传输，也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。 　　消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息，从而就避免了浪费性能的拷贝操作。另外，如果底层数据结构是环形缓存的话，指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。 　　多段传送的示意图如下：   多段传送也被大量用于文件系统中。在执行读取操作时，通过使用具有n段数据消息，数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。 　　例如，假如每个缓存块大小为512的字节，如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下： 　　由于数据是在地址空间直接复制的（而不只是做页面表操作），消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU...</small></li>
<li><a href='http://www.speedvi.net/2009/12/27/184.html' rel='bookmark' title='Permanent Link: 内核状态'>内核状态</a> <small>RUNNING 　　系统的运行状态简单的说就是表示有线程处于活动状态并在使用CPU。在多处理器系统中，可以有多个线程处于运行状态；在单处理器系统中，只能有一个线程处于运行状态。 READY 　　就绪状态表示线程现在可以运行了，只不过还未运行，因为现在有另外一个线程（同优先级或更高优先级）正在运行。如果有两个线程都有能力使用处理器，其中一个线程的优先级为10而另外一个线程的优先级为7，那么优先级为10的线程将进入运行状态而优先级为7的线程将进入就绪状态。 &nbsp; 阻塞状态(blocked states) 　　我们应该如何称呼阻塞状态？其实在系统中并不是只有一种阻塞状态，在实际系统中可以有数十种阻塞状态。 　　为什么会有这么多种呢？因为内核一直保留着线程为何被阻塞的原因。 　　在前面我们已经讲到了两种阻塞状态——当线程等待互斥体时被阻塞，这时线程就是MUTEX状态。当一个线程由于等待信号变量而被阻塞的话，这时线程就是SEM状态。这些状态简要的说明了线程是阻塞于哪个队列或资源。...</small></li>
<li><a href='http://www.speedvi.net/2009/09/25/170.html' rel='bookmark' title='Permanent Link: 进程间通信的通道与连接'>进程间通信的通道与连接</a> <small>　　在UNIX系统中，消息传递是沿着通道与连接传送的，而不是直接的从线程到线程。一个线程要接收消息就先要创建一个通道，另外一个线程如果想要向这个线程传递消息就需要创建一个连接附加到该通道上。 　　通道是消息内核调用所必须的，服务器线程在调用MsgReceive()函数就是使用这个通道来接收消息的。连接由客户线程创建并用于“连接”服务器线程创建的可用通道。一旦连接确定，客户线程就可以使用MsgSend()函数发送消息了。如果一个进程中有多个线程连接到同一个通道，为了效率所有的连接就被映射到同一个内核对象。在一个进程中的通道与连接是使用一个小整数进行命名的。客户连接直接映射为文件描述符。 　　从架构上来说，这是一个关键点。通过将客户连接直接映射为文件描述符，就消除了使用另一层变换的必要。我们不必“指出”根据文件描述符向哪里发送消息。相反，我们可以很简单的将消息直接发送给“文件描述符”（也就是连接ID）。 　　这里可以使用的函数如下： 　　ChannelCreate() 创建一个通道来接收消息；　　ChannelDestroy() 清除一个通道；　　ConnectAttach() 创建一个连接来发送消息；　　ConnectDetach() 释放一个连接。 　　作为服务器端的进程可以使用事件循环来接收与处理消息，代码如下所示：...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.speedvi.net/2009/09/25/160.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
