<?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%85%b1%e4%ba%ab%e5%86%85%e5%ad%98/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>进程间通信的共享内存</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>
	</channel>
</rss>

