﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-旅途-文章分类-linux内核</title><link>http://www.cppblog.com/mydriverc/category/4812.html</link><description>如果想飞得高，就该把地平线忘掉</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:37:45 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:37:45 GMT</pubDate><ttl>60</ttl><item><title>80x86保护模式之实模式与保护模式切换实例</title><link>http://www.cppblog.com/mydriverc/articles/30720.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Thu, 23 Aug 2007 16:55:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/30720.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/30720.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/30720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/30720.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/30720.html</trackback:ping><description><![CDATA[Error convertoring HTML to XHTML: System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
   at System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)
   at System.Text.StringBuilder.Append(String value)
   at System.IO.StringWriter.Write(String value)
   at System.Xml.XmlTextWriter.WriteCData(String text)
   at System.Xml.XmlWriter.WriteNode(XmlReader reader, Boolean defattr)
   at FreeTextBoxControls.Support.Formatter.HtmlToXhtml(String input)<img src ="http://www.cppblog.com/mydriverc/aggbug/30720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-24 00:55 <a href="http://www.cppblog.com/mydriverc/articles/30720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实模式与保护模式</title><link>http://www.cppblog.com/mydriverc/articles/30719.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Thu, 23 Aug 2007 16:52:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/30719.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/30719.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/30719.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/30719.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/30719.html</trackback:ping><description><![CDATA[
		<div>
				<p>
						<font size="2">
								<b>
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">实模式：</span>
								</b>
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">（即实地址访问模式）它是</span>
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">Intel<font face="宋体"><span lang="ZH-CN">公司</span>80286<span lang="ZH-CN">及以后的</span>x86(80386,80486<span lang="ZH-CN">和</span>80586<span lang="ZH-CN">等</span>)<span lang="ZH-CN">兼容处理器（</span>CPU<span lang="ZH-CN">）的一种操作模式。实模式被特殊定义为</span>20<span lang="ZH-CN">位地址内存可访问空间上，这就意味着它的容量是</span>2<span lang="ZH-CN">的</span>20<span lang="ZH-CN">次幂（</span>1M<span lang="ZH-CN">）的可访问内存空间（物理内存和</span>BIOS-ROM<span lang="ZH-CN">），软件可通过这些地址直接访问</span>BIOS<span lang="ZH-CN">程序和外围硬件。实模式下处理器没有硬件级的内存保护概念和多道任务的工作模式。但是为了向下兼容，所以</span>80286<span lang="ZH-CN">及以后的</span>x86<span lang="ZH-CN">系列兼容处理器仍然是开机启动时工作在实模式下。</span>80186<span lang="ZH-CN">和早期的处理器仅有一种操作模式，就是后来我们所定义的实模式。实模式虽然能访问到</span>1M<span lang="ZH-CN">的地址空间，但是由于</span>BIOS<span lang="ZH-CN">的映射作用（即</span>BIOS<span lang="ZH-CN">占用了部分空间地址资源），所以真正能使用的物理内存空间（内存条），也就是在</span>640k<span lang="ZH-CN">到</span>924k<span lang="ZH-CN">之间。</span>1M</font></span>
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">地址空间组成是由</span>
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">16<font face="宋体"><span lang="ZH-CN">位的段地址和</span>16<span lang="ZH-CN">位的段内偏移地址组成的。用公式表示为：物理地址</span>=<span lang="ZH-CN">左移</span>4<span lang="ZH-CN">位的段地址</span>+<span lang="ZH-CN">偏移地址。</span></font></span>
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
								</span>
						</font>
				</p>
		</div>
		<p>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font size="2">286<font face="宋体"><span lang="ZH-CN">处理器体系结构引入了地址保护模式的概念，处理器能够对内存及一些其他外围设备做硬件级的保护设置（保护设置实质上就是屏蔽一些地址的访问）。使用这些新的特性，然而必不可少一些额外的在</span>80186<span lang="ZH-CN">及以前处理器没有的操作规程。自从最初的</span>x86<span lang="ZH-CN">微处理器规格以后，它对程序开发完全向下兼容，</span>80286<span lang="ZH-CN">芯片被制作成启动时继承了以前版本芯片的特性，工作在实模式下，在这种模式下实际上是关闭了新的保护功能特性，因此能使以往的软件继续工作在新的芯片下。直到今天，甚至最新的</span>x86<span lang="ZH-CN">处理器都是在计算机加电启动时都是工作在实模式下，它能运行为以前处理器芯片写的程序.</span></font></font>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font face="宋体">
								<span lang="ZH-CN">
								</span>
						</font>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
										<span lang="ZH-CN">
												<span style="text-decoration: underline;">
												</span>
												<font face="宋体" size="2">DOS操作系统（例如</font>
										</span>
										<a href="http://www.answers.com/main/ntquery;jsessionid=7243ctkg9fj06?method=4&amp;dsid=2222&amp;dekey=MS-DOS&amp;curtab=2222_1&amp;sbid=lc04b">
												<span style="color: black; text-decoration: none;">
												</span>
										</a>
										<a href="http://www.answers.com/main/ntquery;jsessionid=7243ctkg9fj06?method=4&amp;dsid=2222&amp;dekey=DR-DOS&amp;curtab=2222_1&amp;sbid=lc04b">
												<span style="color: black; text-decoration: none;">
												</span>
										</a>
										<font face="宋体">
												<font size="2">
														<span lang="ZH-CN">MS-DOS,DR-DOS）工作在实模式下，微软</span>Windows<span lang="ZH-CN">早期的版本（它本质上是运行在</span>DOS<span lang="ZH-CN">上的图形用户界面应用程序，实际上本身并不是一个操作系统）也是运行在实模式下，直到</span>Windows3.0<span lang="ZH-CN">，它运行期间既有实模式又有保护模式，所以说它是一种混合模式工作。它的保护模式运行有两种不同意义</span>(<span lang="ZH-CN">因为</span>80286<span lang="ZH-CN">并没有完全地实现</span>80386<span lang="ZH-CN">及以后的保护模式功能</span>)<span lang="ZH-CN">：</span></font>
										</font>
								</span>
						</span>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
										<font face="宋体">
												<span lang="ZH-CN">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
														</span>
												</span>
										</font>
								</span>
						</span>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
										<font face="宋体">
												<span lang="ZH-CN">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																<font size="2">1<span lang="ZH-CN"><font face="宋体">〉“标准保护模式”：这就是程序运行在保护模式下；</font></span></font>
														</span>
														<p>
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																		<font size="2">2<font face="宋体"><span lang="ZH-CN">〉“虚拟保护模式（实质上还是实模式，是实模式上模拟的保护模式）”：它也使用</span>32<span lang="ZH-CN">位地址寻址方式。</span>Windows3.1<span lang="ZH-CN">彻底删除了对实模式的支持。在</span>80286<span lang="ZH-CN">处理器芯片以后，</span>Windows3.1<span lang="ZH-CN">成为主流操作系统（</span>Windows/80286<span lang="ZH-CN">不是主流产品）。目前差不多所有的</span>X86<span lang="ZH-CN">系列处理器操作系统（</span>Linux<span lang="ZH-CN">，</span>Windows95 and later<span lang="ZH-CN">，</span>OS/2<span lang="ZH-CN">等）都是在启动时进行处理器设置而进入保护模式的。</span></font></font>
																</span>
														</p>
														<p>
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																		<font face="宋体">
																				<span lang="ZH-CN">
																				</span>
																		</font>
																</span>
																<font size="2"> </font>
														</p>
														<p>
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																		<font face="宋体" size="2">
																				<span lang="ZH-CN">
																						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">实模式工作机理：</span>
																				</span>
																		</font>
																</span>
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																		<font face="宋体">
																				<span lang="ZH-CN">
																						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
																								<p>
																										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																												<font size="2">1&gt; <font face="宋体"><span lang="ZH-CN">对于</span>8086/8088<span lang="ZH-CN">来说计算实际地址是用绝对地址对</span>1M<span lang="ZH-CN">求模。</span>8086<span lang="ZH-CN">的地址线的物理结构：</span>20<span lang="ZH-CN">根，也就是它可以物理寻址的内存范围为</span>2^20<span lang="ZH-CN">个字节，即</span>1 M<span lang="ZH-CN">空间，但由于</span>8086/8088<span lang="ZH-CN">所使用的寄存器都是</span>16<span lang="ZH-CN">位，能够表示的地址范围只有</span>0-64K<span lang="ZH-CN">，这和</span>1M<span lang="ZH-CN">地址空间来比较也太小了，所以为了在</span>8086/8088<span lang="ZH-CN">下能够访问</span>1M<span lang="ZH-CN">内存，</span>Intel<span lang="ZH-CN">采取了分段寻址的模式：</span>16<span lang="ZH-CN">位段基地址</span>:16<span lang="ZH-CN">位偏移</span>EA<span lang="ZH-CN">。其绝对地址计算方法为：</span>16<span lang="ZH-CN">位基地址左移</span>4<span lang="ZH-CN">位</span>+16<span lang="ZH-CN">位偏移</span>=20<span lang="ZH-CN">位地址。</span>  </font></font>
																												<font face="宋体">
																														<font size="2">
																																<i>
																																		<span lang="ZH-CN">比如：</span>DS=1000H EA=FFFFH <span lang="ZH-CN">那么绝对地址就为：</span>10000H +<br />0FFFFH = 1FFFFH <span lang="ZH-CN">地址单元</span></i>
																																<span lang="ZH-CN">。通过这种方法来实现使用</span>16<span lang="ZH-CN">位寄存器访问</span>1M<span lang="ZH-CN">的地址空间，这种技术是处理器内部实现的，通过上述分段技术模式，能够表示的最大内存为：</span></font>
																												</font>
																										</span>
																										<span style="font-size: 10.5pt; font-family: SimSun; letter-spacing: 0.5pt;">
																												<font size="2">FFFFh: FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes<font face="宋体"><span lang="ZH-CN">（</span>1M<span lang="ZH-CN">多余出来的部分被称做高端内存区</span>HMA<span lang="ZH-CN">）<span style="color: black;">。但</span></span><span style="color: black;">8086/8088<span lang="ZH-CN">只有</span>20<span lang="ZH-CN">位地址线，只能够访问</span>1M<span lang="ZH-CN">地址范围的数据，所以如果访问</span>100000h~10FFEFh<span lang="ZH-CN">之间的内存（大于</span>1M<span lang="ZH-CN">空间），则必须有第</span>21<span lang="ZH-CN">根地址线来参与寻址（</span>8086/8088<span lang="ZH-CN">没有）。因此，当程序员给出超过</span>1M<span lang="ZH-CN">（</span>100000H-10FFEFH<span lang="ZH-CN">）的地址时，因为逻辑上正常，系统并不认为其访问越界而产生异常，而是自动从</span>0<span lang="ZH-CN">开始计算，也就是说系统计算实际地址的时候是按照对</span>1M<span lang="ZH-CN">求模的方式进行的，这种技术被称为</span>wrap-around<span lang="ZH-CN">。</span></span></font></font>
																										</span>
																										<font color="#444444" face="Verdana" size="2">
																										</font>
																								</p>
																						</span>
																				</span>
																		</font>
																</span>
														</p>
												</span>
										</font>
								</span>
						</span>
				</span>
		</p>
		<p>
				<font size="2"> </font>
		</p>
		<p>
				<font size="2"> </font>
		</p>
		<p>
				<font size="2">
						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">2&gt; <font face="宋体"><span lang="ZH-CN">对于</span>80286<span lang="ZH-CN">或以上的</span>CPU<span lang="ZH-CN">通过</span>A20 GATE<span lang="ZH-CN">来控制</span>A20<span lang="ZH-CN">地址线</span></font></span>
						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">。</span>
						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN"> 技术发展到了</span>
						<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">80286<font face="宋体"><span lang="ZH-CN">，虽然系统的地址总线由原来的</span>20<span lang="ZH-CN">根发展为</span>24<span lang="ZH-CN">根，这样能够访问的内存可以达到</span>2^24=16M,<span lang="ZH-CN">但是</span>Intel<span lang="ZH-CN">在设计</span>80286<span lang="ZH-CN">时提出的目标是向下兼容</span>,<span lang="ZH-CN">所以在实模式下，系统所表现的行为应该和</span>8086/8088<span lang="ZH-CN">所表现的完全一样，也就是说，在实模式下，</span>80386<span lang="ZH-CN">以及后续系列应该和</span>8086/8088<span lang="ZH-CN">完全兼容仍然使用</span>A20<span lang="ZH-CN">地址线。所以说</span>80286<span lang="ZH-CN">芯片存在一个</span>BUG<span lang="ZH-CN">：它开设</span>A20<span lang="ZH-CN">地址线。如果程序员访问</span>100000H-10FFEFH<span lang="ZH-CN">之间的内存，系统将实际访问这块内存（没有</span>wrap-around<span lang="ZH-CN">技术），而不是象</span>8086/8088<span lang="ZH-CN">一样从</span>0<span lang="ZH-CN">开始。我们来看一副图：</span></font></span>
				</font>
		</p>
		<p>
				<br />
				<img style="cursor: pointer;" title="在新窗口打开图片" src="http://www.cublog.cn/u/7257/photo/060425145734.jpg" alt="" />
		</p>
		<p>
				<font face="宋体">
						<font size="2">
								<span lang="ZH-CN">为了解决上述兼容性问题，</span>IBM<span lang="ZH-CN">使用键盘控制器上剩余的一些输出线来管理第</span>21<span lang="ZH-CN">根地址线（从</span>0<span lang="ZH-CN">开始数是第</span>20<span lang="ZH-CN">根）</span> <span lang="ZH-CN">的有效性，被称为</span>A20 Gate<span lang="ZH-CN">：</span></font>
				</font>
		</p>
		<p>
				<span lang="ZH-CN">
						<font face="宋体">
								<font size="2">1&gt; <span lang="ZH-CN">如果</span>A20 Gate<span lang="ZH-CN">被打开，则当程序员给出</span>100000H-10FFEFH<span lang="ZH-CN">之间的地址的时候，系统将真正访问这块内存区域；</span></font>
						</font>
				</span>
		</p>
		<p>
				<span lang="ZH-CN">
						<span lang="ZH-CN">
								<font face="宋体">
										<font size="2">2 <span lang="ZH-CN">如果</span>A20 Gate<span lang="ZH-CN">被禁止，则当程序员给出</span>100000H-10FFEFH<span lang="ZH-CN">之间的地址的时候，系统仍然使用</span>8086/8088<span lang="ZH-CN">的方式即取模方式（</span>8086<span lang="ZH-CN">仿真）。绝大多数</span>IBM PC<span lang="ZH-CN">兼容机默认的</span>A20 Gate<span lang="ZH-CN">是被禁止的。现在许多新型</span>PC<span lang="ZH-CN">上存在直接通过</span>BIOS<span lang="ZH-CN">功能调用来控制</span>A20 Gate<span lang="ZH-CN">的功能。</span></font>
								</font>
						</span>
				</span>
		</p>
		<p>
				<span lang="ZH-CN">
						<span lang="ZH-CN">
								<span lang="ZH-CN">
										<font face="宋体">
												<font size="2">
														<span lang="ZH-CN">上面所述的内存访问模式都是实模式，在</span>80286<span lang="ZH-CN">以及更高系列的</span>PC<span lang="ZH-CN">中，即使</span>A20 Gate<span lang="ZH-CN">被打开，在实模式下所能够访问的内存最大也只能为</span>10FFEFH<span lang="ZH-CN">，尽管它们的地址总线所能够访问的能力都大大超过这个限制。为了能够访问</span>10FFEFH<span lang="ZH-CN">以上的内存，则必须进入保护模式。</span></font>
										</font>
								</span>
						</span>
				</span>
		</p>
		<p>
				<span lang="ZH-CN">
						<span lang="ZH-CN">
								<span lang="ZH-CN">
										<span lang="ZH-CN">
												<font size="2">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
																<strong>保护模式：</strong>
														</span>
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">经常缩写为</span>
												</font>
												<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
														<font size="2">p-mode,<span lang="ZH-CN"><font face="宋体">在</font></span></font>
														<font face="宋体">
																<font size="2">
																		<span>Intel iAPX 286<span lang="ZH-CN">程序员参考手册中（</span>iAPX<br />286<span lang="ZH-CN">是</span>Intel 80286<span lang="ZH-CN">的另一种叫法）</span></span>
																		<span lang="ZH-CN">它又被称作为虚拟地址保护模式。经管在</span>Intel 80286<span lang="ZH-CN">手册中已经提出了虚地址保护模式，但实际上它只是一个指引，真正的</span>32<span lang="ZH-CN">位地址出现在</span>Intel 80386<span lang="ZH-CN">上。保护模式本身是</span>80286<span lang="ZH-CN">及以后兼容处理器序列之后产成的一种操作模式，它具有许多特性设计为提高系统的多道任务和系统的稳定性。例如内存的保护，分页机制和硬件虚拟存储的支持。现代多数的</span>x86<span lang="ZH-CN">处理器操作系统都运行在保护模式下，包括</span>Linux, Free BSD, <span lang="ZH-CN">和</span>Windows<br />3.0<span lang="ZH-CN">（它也运行在实模式下，为了和</span>Windows 2.x<span lang="ZH-CN">应用程序兼容）及以后的版本。</span></font>
														</font>
												</span>
										</span>
								</span>
						</span>
				</span>
		</p>
		<p>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font size="2">80286<font face="宋体"><span lang="ZH-CN">及以后的处理器另一种工作模式是实模式（仅当系统启动的一瞬间），本着向下兼容的原则屏蔽保护模式特性，从而容许老的软件能够运行在新的芯片上。作为一个设计规范，所有的</span>x86<span lang="ZH-CN">系列处理器，除嵌入式</span>Intel80387<span lang="ZH-CN">之外，都是系统启动工作在实模式下，确保遗留下的操作系统向下兼容。它们都必须被启动程序（操作系统程序最初运行代码）重新设置而相应进入保护模式的，在这之前任何的保护模式特性都是无效的。在现代计算机中，这种匹配进入保护模式是操作系统启动时最前沿的动作之一。</span></font></font>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font face="宋体">
								<span lang="ZH-CN">
								</span>
						</font>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font face="宋体" size="2">
								<span lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">在被调停的多道任务程序中，它可以从新工作在实模式下是相当可能的。保护模式的特性是阻止被其他任务或系统内核破坏已经不健全的程序的运行，保护模式也有对硬件的支持，例如中断运行程序，移动运行进程文档到另一个进程和置空多任务的保护功能。</span>
								</span>
						</font>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<font face="宋体">
								<span lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										</span>
								</span>
						</font>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<font size="2">386<font face="宋体"><span lang="ZH-CN">及以后系列处理器不仅具有保护模式又具有</span>32<span lang="ZH-CN">位寄存器，结果导致了处理功能的混乱，因为</span>80286<span lang="ZH-CN">虽然支持保护模式，但是它的寄存器都是</span>16<span lang="ZH-CN">位的，它是通过自身程序设定而模拟出的</span>32<span lang="ZH-CN">位，并非</span>32<span lang="ZH-CN">位寄存器处理。归咎于这种混乱现象，它促使Windows/386<span style="text-decoration: underline;"></span></span></font></font>
												<a href="http://www.answers.com/main/ntquery?method=4&amp;dsid=2222&amp;dekey=Windows+2.0&amp;curtab=2222_1">
														<span style="color: black;">
														</span>
												</a>
												<font face="宋体">
														<font size="2">
																<span lang="ZH-CN">及以后的版本彻底抛弃</span>80286<span lang="ZH-CN">的虚拟保护模式，以后保护模式的操作系统都是运行在</span>80386<span lang="ZH-CN">以上，不再运行在</span>80286<span lang="ZH-CN">（尽管</span>80286<span lang="ZH-CN">模式支持保护模式），所以说</span>80286<span lang="ZH-CN">是一个过渡芯片，它是一个过渡产品。</span></font>
												</font>
										</span>
								</span>
						</span>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<font face="宋体">
														<span lang="ZH-CN">
														</span>
												</font>
										</span>
								</span>
						</span>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<span lang="ZH-CN">
														<font size="2">
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">尽管</span>
																<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">286<font face="宋体"><span lang="ZH-CN">和</span>386<span lang="ZH-CN">处理器能够实现保护模式和兼容以前的版本，但是内存的</span>1M<span lang="ZH-CN">以上空间还是不易存取，由于内存地址的回绕，</span>IBM PC XT <span lang="ZH-CN">（现以废弃）设计一种模拟系统，它能过欺骗手段访问到</span>1M<span lang="ZH-CN">以上的地址空间，就是开通了</span>A20<span lang="ZH-CN">地址线。在保护模式里，前</span>32<span lang="ZH-CN">个中断为处理器异常预留，例如，中断</span>0D<span lang="ZH-CN">（十进制</span>13<span lang="ZH-CN">）常规保护故障和中断</span>00<span lang="ZH-CN">是除数为零异常。</span></font></span>
														</font>
												</span>
										</span>
								</span>
						</span>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<span lang="ZH-CN">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																<font face="宋体">
																		<span lang="ZH-CN">
																		</span>
																</font>
														</span>
												</span>
										</span>
								</span>
						</span>
				</span>
				<font size="2"> </font>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<span lang="ZH-CN">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																<span lang="ZH-CN">
																		<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
																				<font size="2">如果要访问更多的内存，则必须进入保护模式，那么，在保护模式下，</font>
																		</span>
																		<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																				<font size="2">A20<br />Gate<span lang="ZH-CN"><font face="宋体">对于内存访问有什么影响呢？</font></span></font>
																		</span>
																</span>
														</span>
												</span>
										</span>
								</span>
						</span>
				</span>
		</p>
		<p>
				<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
						<span lang="ZH-CN">
								<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">
										<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
												<span lang="ZH-CN">
														<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																<span lang="ZH-CN">
																		<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;">
																				<span lang="ZH-CN">
																						<font face="宋体">
																								<font size="2">
																										<span lang="ZH-CN">为了搞清楚这一点，我们先来看一看</span>A20<span lang="ZH-CN">的工作原理。</span>A20<span lang="ZH-CN">，从它的名字就可以看出来，其实它就是对于</span>A20<span lang="ZH-CN">（从</span>0<span lang="ZH-CN">开始数）的特殊处理</span>(<span lang="ZH-CN">也就是对第</span>21<span lang="ZH-CN">根地址线的处理</span>)<span lang="ZH-CN">。如果</span>A20 Gate<span lang="ZH-CN">被禁止，对于</span>80286<span lang="ZH-CN">来说，其地址为</span>24<span lang="ZH-CN">根地址线，其地址表示为</span>EFFFFF<span lang="ZH-CN">；对于</span>80386<span lang="ZH-CN">极其随后的</span>32<span lang="ZH-CN">根地址线芯片来说，其地址表示为</span>FFEFFFFF<span lang="ZH-CN">。这种表示的意思是：</span></font>
																						</font>
																				</span>
																		</span>
																</span>
														</span>
												</span>
										</span>
								</span>
						</span>
				</span>
		</p>
		<p>
				<img style="cursor: pointer;" title="在新窗口打开图片" src="http://www.cublog.cn/u/7257/photo/060425145802.jpg" alt="" />
		</p>
		<p>
				<font face="宋体">
						<font size="2">
								<strong>1&gt;</strong>
								<span lang="ZH-CN">如果</span>A20<br />Gate<span lang="ZH-CN">被禁止。则其第</span>A20<span lang="ZH-CN">在</span>CPU<span lang="ZH-CN">做地址访问的时候是无效的，永远只能被作为</span>0<span lang="ZH-CN">。所以，在保护模式下，如果</span>A20<br />Gate<span lang="ZH-CN">被禁止，则可以访问的内存只能是奇数</span>1M<span lang="ZH-CN">段，即</span>1M,3M,5M…<span lang="ZH-CN">，也就是</span>00000-FFFFF, 200000-2FFFFF,300000-3FFFFF…</font>
				</font>
		</p>
		<p>
				<font face="宋体">
						<font size="2">2<span lang="ZH-CN">如果</span>A20 Gate<span lang="ZH-CN">被打开。则其第</span>20-bit<span lang="ZH-CN">是有效的，其值既可以是</span>0<span lang="ZH-CN">，又可以是</span>1<span lang="ZH-CN">。那么就可以使</span>A20<span lang="ZH-CN">线传递实际的地址信号。如果</span>A20 Gate<span lang="ZH-CN">被打开，则可以访问的内存则是连续的。</span></font>
				</font>
				<font size="-0">
						<font size="3">
								<font face="宋体">
										<span lang="ZH-CN">
												<p>
														<font size="2">
																<b>
																		<span style="font-size: 10.5pt; color: black; font-family: SimSun; letter-spacing: 0.5pt;" lang="ZH-CN">实模式和保护模式的区别：</span>
																</b>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">从表面上看，保护模式和实模式并没有太大的区别，二者都使用了内存段、中断和设备驱动来处理硬件，但二者有很多不同之处。我们知道，在实模式中内存被划分成段，每个段的大小为</span>
																<span style="font-size: 10.5pt; font-family: Arial;">64KB</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">，而这样的段地址可以用</span>
																<span style="font-size: 10.5pt; font-family: Arial;">16</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位来表示。内存段的处理是通过和段寄存器相关联的内部机制来处理的，这些段寄存器（</span>
																<span style="font-size: 10.5pt; font-family: Arial;">CS</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">、</span>
																<span style="font-size: 10.5pt; font-family: Arial;">DS</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">、</span>
																<span style="font-size: 10.5pt; font-family: Arial;"> SS</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">和</span>
																<span style="font-size: 10.5pt; font-family: Arial;">ES</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">）的内容形成了物理地址的一部分。具体来说，最终的物理地址是由</span>
																<span style="font-size: 10.5pt; font-family: Arial;">16</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位的段地址和</span>
																<span style="font-size: 10.5pt; font-family: Arial;">16</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位的段内偏移地址组成的。用公式表示为：物理地址</span>
																<span style="font-size: 10.5pt; font-family: Arial;">=</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">左移</span>
																<span style="font-size: 10.5pt; font-family: Arial;">4</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位的段地址</span>
																<span style="font-size: 10.5pt; font-family: Arial;">+</span>
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">偏移地址。</span>
														</font>
												</p>
												<p>
														<font size="2">
																<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">在保护模式下，段是通过一系列被称之为</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">“</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">描述符表</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">”</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">的表所定义的。段寄存器存储的是指向这些表的指针。用于定义内存段的表有两种：全局描述符表</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">(GDT) </span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">和局部描述符表</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">(LDT)</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">。</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">GDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">是一个段描述符数组，其中包含所有应用程序都可以使用的基本描述符。在实模式中，段长是固定的</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">(</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">为</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">64KB)</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">，而在保护模式中，段长是可变的，其最大可达</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">4GB</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">。</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">LDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">也是段描述符的一个数组。与</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">GDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">不同，</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">LDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">是一个段，其中存放的是局部的、不需要全局共享的段描述符。每一个操作系统都必须定义一个</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">GDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">，而每一个正在运行的任务都会有一个相应的</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">LDT</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">。每一个描述符的长度是</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">8</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">个字节，格式如图</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">3</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">所示。当段寄存器被加载的时候，段基地址就会从相应的表入口获得。描述符的内容会被存储在一个程序员不可见的影像寄存器</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">(shadow register)</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">之中，以便下一次同一个段可以使用该信息而不用每次都到表中提取。物理地址由</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">16</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位或者</span>
																		<span style="font-size: 10.5pt; font-family: Arial;">32</span>
																		<span style="font-size: 10.5pt; font-family: SimSun;" lang="ZH-CN">位的偏移加上影像寄存器中的基址组成。实模式和保护模式的不同可以从下图很清楚地看出来。</span>
																</span>
																<span style="font-size: 10.5pt; font-family: Arial;">
																</span>
														</font>
												</p>
												<p>
														<img src="http://www.cublog.cn/u/7257/photo/060425145819.jpg" alt="" />
														<font face="Verdana" size="2">
														</font>
												</p>
										</span>
								</font>
						</font>
				</font>
		</p>
		<p>
				<span lang="ZH-CN">
						<strong>
								<font face="宋体" size="3">实模式下寻址方式</font>
						</strong>
				</span>
		</p>
		<p>
				<span lang="ZH-CN">
				</span>  </p>
		<p>
				<img src="http://www.cublog.cn/u/7257/photo/060425145913.jpg" alt="" />
		</p>
		<p>
				<span>
						<strong>保护模式下寻址方式</strong>
				</span>
		</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/30719.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-24 00:52 <a href="http://www.cppblog.com/mydriverc/articles/30719.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boot Sector结构、系统启动过程简介</title><link>http://www.cppblog.com/mydriverc/articles/30640.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Wed, 22 Aug 2007 16:36:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/30640.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/30640.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/30640.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/30640.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/30640.html</trackback:ping><description><![CDATA[
		<font size="2">解决困扰已久的系统启动过程，可以帮助使多系统共存，而不影响各自启动！</font>
		<p style="margin: 0cm 0cm 0pt;" class="MsoNormal">
				<font size="2">
						<b style="">Boot Sector</b>
						<b style="">结构、系统启动过程简介</b>
						<br />
						<br />一. Boot Sector 的组成 <br /><br />   Boot Sector 也就是硬盘的第一个扇区（注<font face="Times New Roman">1</font>：<font face="Times New Roman">0</font>柱面，<font face="Times New Roman">0</font>磁道，<font face="Times New Roman">1</font>扇区） （注<font face="Times New Roman">2</font>：<font face="Times New Roman"> 1</font>磁道<font face="Times New Roman">=16</font>扇区，<font face="Times New Roman">1</font>扇区<font face="Times New Roman">=512</font>字节）, 它由 MBR (Master Boot Record), DPT (Disk Partition Table) 和 Boot Record ID  三部分组成. <br />   MBR 又称作主引导记录占用 Boot Sector 的前 446 个字节 ( 0 to 0x1BD ), <br />存放系统主引导程序 (它负责检查硬盘分区表、寻找可引导分区并负责将可引导分区的引导扇区（DBR）装入内存). <br />   DPT 即主分区表占用 64 个字节 (0x1BE to 0x1FD), 记录了磁盘的基本分区 <br />信息. 主分区表分为四个分区项, 每项 16 字节, 分别记录了每个主分区的信息 <br />(因此最多可以有四个主分区). <br />   Boot Record ID 即引导区标记占用两个字节 <b>(0x1FE and 0x1FF), 对于合法 <br />
引导区, 它等于 </b><b>0xAA55,</b> 这是判别引导区是否合法的标志. <br />   Boot Sector 的具体结构如下图所示: <br />     0000  |------------------------------------------------| <br />           |                                                | <br />           |                                                | <br />           |             Master Boot Record                 | <br />           |                                                | <br />           |                                                | <br />           |             主引导记录(446字节)                | <br />           |                                                | <br />           |                                                | <br />           |                                                | <br />     01BD  |                                                | <br />     01BE  |------------------------------------------------| <br />           |                                                | <br />     01CD  |             分区信息  1(16字节)                | <br />     01CE  |------------------------------------------------| <br />           |                                                | <br />     01DD  |             分区信息  2(16字节)                | <br />     01DE  |------------------------------------------------| <br />           |                                                | <br />     01ED  |             分区信息  3(16字节)                | <br />     01EE  |------------------------------------------------| <br />           |                                                | <br />     01FD  |             分区信息  4(16字节)                | <br />           |------------------------------------------------| <br />           | 01FE                | 01FF                     | <br />           |         55          |           AA             | <br />           |------------------------------------------------|</font>
		</p>
		<p style="margin: 0cm 0cm 0pt;" class="MsoNormal">
				<font size="2">二. 系统启动过程简介 <br /><br />   系统启动过程主要由一下几步组成(以硬盘启动为例): <br /><br />   1. 开机<br />   2. BIOS 加电自检 ( Power On Self Test -- POST ) <br />      内存地址为 0ffff:0000 <br />   3. 将硬盘第一个扇区 (0头0道1扇区, 也就是Boot Sector) <br />      读入<b>内存地址 0000:<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">7c</chmetcnv>00</b> 处.（注<font face="Times New Roman">3</font>：遇到最后两个字节55 AA读入内存执行） <br />   4. 检查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于 <br />      则转去尝试其他启动介质, 如果没有其他启动介质则显示 <br />      "No ROM BASIC" 然后死机. <br />   5. 跳转到 0000:<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">7c</chmetcnv>00 处执行 MBR 中的程序. <br />   6. MBR 首先将自己复制到 0000:0600 处, 然后继续执行. <br />   7. 在主分区表中搜索标志为活动的分区. 如果发现没有活动 <br />      分区或有不止一个活动分区, 则转停止. <br />   8. 将活动分区的第一个扇区读入内存地址 0000:<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">7c</chmetcnv>00 处.</font>
		</p>
		<p style="margin: 0cm 0cm 0pt;" class="MsoNormal">
				<font size="2">（注4：在分区表的四个记录中，一般来说有且只有一个记录的标记是活动的，MBR（主要负责从活动分区中装载并运行系统引导程序）会去找到这个分区记录，根据记录的起始扇区加载该分区的逻辑 0 扇区（起始扇区）的内容到 0x<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">07C</chmetcnv>0:0000，并且执行 JUMP 0x<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">07C</chmetcnv>0:0000（按照规范，BOOT RECORD 也应该从 0x<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">07C</chmetcnv>0:0000
处开始执行，所以 MBR 通常都要先将自己搬移，以腾出位置去加载 BOOT RECORD）。控制权切换到 BOOT RECORD。BOOT
RECORD(包括boot loader如grub或lilo 等)以 linux 为例，它会读取 linux 内核镜像到地址
0x9000:0000，然后开始切换到 0x9000:0000 继续运行。 以 MS-DOS 为例，则它会读取文件系统根目录下的 IO.SYS
和 MSDOS.SYS 两个文件然后加载到内存中继续运行。）<br />   9. 检查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于则 <br />      显示 "Missing Operating System" 然后停止, 或尝试 <br />      软盘启动. <br />   10. 跳转到 0000:<chmetcnv unitname="C" sourcevalue="7" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">7c</chmetcnv>00 处继续执行特定系统的启动程序. <br />   11. 启动系统 ... <br /><br />   以上步骤中 2,3,4,5 步是由 <b>BIOS 的引导程序</b>完成. 6,7,8,9,10 <br />步由<b>MBR中的引导程序</b>完成. <br /><br />   一般多系统引导程序 (如 SmartFDISK, BootStar, PQBoot 等) <br />都是将标准主引导记录替换成自己的引导程序, 在运行系统启动程序 <br />之前让用户选择要启动的分区. <br />   而某些系统自带的多系统引导程序 (如 lilo, NT Loader 等) <br />则可以将自己的引导程序放在系统所处分区的第一个扇区中, 在 Linux <br />中即为 SuperBlock (其实 SuperBlock 是两个扇区). <br />   注: 以上各步骤中使用的是标准 MBR, 其他多系统引导程序的引导过程与此不同.</font>
		</p>
		<font size="2">注5：一些早期的引导型病毒，以及某些 bootloader，还有些硬盘加密卡，他们会修改 MBR，做个“钩子”出来。</font>
<img src ="http://www.cppblog.com/mydriverc/aggbug/30640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-23 00:36 <a href="http://www.cppblog.com/mydriverc/articles/30640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>二.Linux 2.4内核中的轻量进程实现</title><link>http://www.cppblog.com/mydriverc/articles/29745.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 10 Aug 2007 17:38:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29745.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29745.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29745.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29745.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29745.html</trackback:ping><description><![CDATA[
		<p>最初的进程定义都包含程序、资源及其执行三部分，其中程序通常指代码，资源在操作系统层面上通常包括内存资源、IO资源、信号处理等部分，而程序的
执行通常理解为执行上下文，包括对cpu的占用，后来发展为线程。在线程概念出现以前，为了减小进程切换的开销，操作系统设计者逐渐修正进程的概念，逐渐
允许将进程所占有的资源从其主体剥离出来，允许某些进程共享一部分资源，例如文件、信号，数据内存，甚至代码，这就发展出轻量进程的概念。Linux内核
在2.0.x版本就已经实现了轻量进程，应用程序可以通过一个统一的clone()系统调用接口，用不同的参数指定创建轻量进程还是普通进程。在内核中，
clone()调用经过参数传递和解释后会调用do_fork()，这个核内函数同时也是fork()、vfork()系统调用的最终实现：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;linux-2.4.20/kernel/fork.c&gt;<br />int do_fork(unsigned long clone_flags, unsigned long stack_start, <br />struct pt_regs *regs, unsigned long stack_size)<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>其中的clone_flags取自以下宏的"或"值：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;linux-2.4.20/include/linux/sched.h&gt;<br />#define CSIGNAL			0x000000ff	/* signal mask to be sent at exit */<br />#define CLONE_VM		0x00000100	/* set if VM shared between processes */<br />#define CLONE_FS        0x00000200	/* set if fs info shared between processes */<br />#define CLONE_FILES     0x00000400	/* set if open files shared between processes */<br />#define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */<br />#define CLONE_PID		0x00001000	/* set if pid shared */<br />#define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */<br />#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */<br />#define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */<br />#define CLONE_THREAD	0x00010000	/* Same thread group? */<br />#define CLONE_NEWNS	0x00020000	/* New namespace group? */<br />#define CLONE_SIGNAL	 (CLONE_SIGHAND | CLONE_THREAD)<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在do_fork()中，不同的
clone_flags将导致不同的行为，对于LinuxThreads，它使用（CLONE_VM | CLONE_FS |
CLONE_FILES |
CLONE_SIGHAND）参数来调用clone()创建"线程"，表示共享内存、共享文件系统访问计数、共享文件描述符表，以及共享信号处理方式。本
节就针对这几个参数，看看Linux内核是如何实现这些资源的共享的。</p>
		<p>
				<b>1.CLONE_VM</b>
		</p>
		<p>do_fork
()需要调用copy_mm()来设置task_struct中的mm和active_mm项，这两个mm_struct数据与进程所关联的内存空间相对
应。如果do_fork()时指定了CLONE_VM开关，copy_mm()将把新的task_struct中的mm和active_mm设置成与
current的相同，同时提高该mm_struct的使用者数目（mm_struct::mm_users）。也就是说，轻量级进程与父进程共享内存地
址空间，由下图示意可以看出mm_struct在进程中的地位：</p>
		<br />
		<img alt="" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/1.gif" border="0" height="380" width="567" />
		<br />
		<p>
				<b>2.CLONE_FS</b>
		</p>
		<p>task_struct
中利用fs（struct fs_struct
*）记录了进程所在文件系统的根目录和当前目录信息，do_fork()时调用copy_fs()复制了这个结构；而对于轻量级进程则仅增加fs-
&gt;count计数，与父进程共享相同的fs_struct。也就是说，轻量级进程没有独立的文件系统相关的信息，进程中任何一个线程改变当前目录、
根目录等信息都将直接影响到其他线程。</p>
		<p>
				<b>3.CLONE_FILES</b>
		</p>
		<p>一
个进程可能打开了一些文件，在进程结构task_struct中利用files（struct files_struct
*）来保存进程打开的文件结构（struct
file）信息，do_fork()中调用了copy_files()来处理这个进程属性；轻量级进程与父进程是共享该结构的，copy_files()
时仅增加files-&gt;count计数。这一共享使得任何线程都能访问进程所维护的打开文件，对它们的操作会直接反映到进程中的其他线程。</p>
		<p>
				<b>4.CLONE_SIGHAND</b>
		</p>
		<p>每
一个Linux进程都可以自行定义对信号的处理方式，在task_struct中的sig（struct
signal_struct）中使用一个struct
k_sigaction结构的数组来保存这个配置信息，do_fork()中的copy_sighand()负责复制该信息；轻量级进程不进行复制，而仅
仅增加signal_struct::count计数，与父进程共享该结构。也就是说，子进程与父进程的信号处理方式完全相同，而且可以相互更改。</p>
		<p>do_fork()中所做的工作很多，在此不详细描述。对于SMP系统，所有的进程fork出来后，都被分配到与父进程相同的cpu上，一直到该进程被调度时才会进行cpu选择。</p>
		<p>尽
管Linux支持轻量级进程，但并不能说它就支持核心级线程，因为Linux的"线程"和"进程"实际上处于一个调度层次，共享一个进程标识符空间，这种
限制使得不可能在Linux上实现完全意义上的POSIX线程机制，因此众多的Linux线程库实现尝试都只能尽可能实现POSIX的绝大部分语义，并在
功能上尽可能逼近。</p>
		<br />
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td>
										<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
										<br />
										<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" align="right" cellpadding="0" cellspacing="0">
				<tbody>
						<tr align="right">
								<td>
										<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
										<br />
										<table border="0" cellpadding="0" cellspacing="0">
												<tbody>
														<tr>
																<td valign="middle">
																		<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																		<br />
																</td>
																<td align="right" valign="top">
																		<a href="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/#main" class="fbox">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100A8">
						<span class="atitle">三.LinuxThread的线程机制</span>
				</a>
		</p>
		<p>LinuxThreads
是目前Linux平台上使用最为广泛的线程库，由Xavier Leroy
(Xavier.Leroy@inria.fr)负责开发完成，并已绑定在GLIBC中发行。它所实现的就是基于核心轻量级进程的"一对一"线程模型，一
个线程实体对应一个核心轻量级进程，而线程之间的管理在核外函数库中实现。</p>
		<p>
				<a name="N100B1">
						<span class="smalltitle">1.线程描述数据结构及实现限制</span>
				</a>
		</p>
		<p>LinuxThreads
定义了一个struct
_pthread_descr_struct数据结构来描述线程，并使用全局数组变量__pthread_handles来描述和引用进程所辖线程。在
__pthread_handles中的前两项，LinuxThreads定义了两个全局的系统线程：__pthread_initial_thread
和__pthread_manager_thread，并用__pthread_main_thread表征
__pthread_manager_thread的父线程（初始为__pthread_initial_thread）。</p>
		<p>struct
_pthread_descr_struct是一个双环链表结构，__pthread_manager_thread所在的链表仅包括它一个元素，实际
上，__pthread_manager_thread是一个特殊线程，LinuxThreads仅使用了其中的errno、p_pid、
p_priority等三个域。而__pthread_main_thread所在的链则将进程中所有用户线程串在了一起。经过一系列
pthread_create()之后形成的__pthread_handles数组将如下图所示：</p>
		<br />
		<img alt="图2 __pthread_handles数组结构" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/2.gif" border="0" height="251" width="491" />
		<br />
		<p>新创建的线程将首先在__pthread_handles数组中占据一项，然后通过数据结构中的链指针连入以__pthread_main_thread为首指针的链表中。这个链表的使用在介绍线程的创建和释放的时候将提到。</p>
		<p>LinuxThreads
遵循POSIX1003.1c标准，其中对线程库的实现进行了一些范围限制，比如进程最大线程数，线程私有数据区大小等等。在LinuxThreads的
实现中，基本遵循这些限制，但也进行了一定的改动，改动的趋势是放松或者说扩大这些限制，使编程更加方便。这些限定宏主要集中在
sysdeps/unix/sysv/linux/bits/local_lim.h（不同平台使用的文件位置不同）中，包括如下几个：</p>
		<p>每
进程的私有数据key数，POSIX定义_POSIX_THREAD_KEYS_MAX为128，LinuxThreads使用
PTHREAD_KEYS_MAX，1024；私有数据释放时允许执行的操作数，LinuxThreads与POSIX一致，定义
PTHREAD_DESTRUCTOR_ITERATIONS为4；每进程的线程数，POSIX定义为64，LinuxThreads增大到1024
（PTHREAD_THREADS_MAX）；线程运行栈最小空间大小，POSIX未指定，LinuxThreads使用
PTHREAD_STACK_MIN，16384（字节）。</p>
		<p>
				<a name="N100D7">
						<span class="smalltitle">2.管理线程</span>
				</a>
		</p>
		<p>"
一对一"模型的好处之一是线程的调度由核心完成了，而其他诸如线程取消、线程间的同步等工作，都是在核外线程库中完成的。在LinuxThreads中，
专门为每一个进程构造了一个管理线程，负责处理线程相关的管理工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建
（__clone()）并启动管理线程。</p>
		<p>在一个进程空间内，管理线程与其他线程之间通过一对"管理管道
（manager_pipe[2]）"来通讯，该管道在创建管理线程之前创建，在成功启动了管理线程之后，管理管道的读端和写端分别赋给两个全局变量
__pthread_manager_reader和__pthread_manager_request，之后，每个用户线程都通过
__pthread_manager_request向管理线程发请求，但管理线程本身并没有直接使用
__pthread_manager_reader，管道的读端（manager_pipe[0]）是作为__clone()的参数之一传给管理线程的，
管理线程的工作主要就是监听管道读端，并对从中取出的请求作出反应。</p>
		<p>创建管理线程的流程如下所示：
        <br />
（全局变量pthread_manager_request初值为-1）
      </p>
		<br />
		<img alt="图3 创建管理线程的流程" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/3.gif" border="0" height="332" width="416" />
		<br />
		<p>初
始化结束后，在__pthread_manager_thread中记录了轻量级进程号以及核外分配和管理的线程id，
2*PTHREAD_THREADS_MAX+1这个数值不会与任何常规用户线程id冲突。管理线程作为pthread_create()的调用者线程的
子线程运行，而pthread_create()所创建的那个用户线程则是由管理线程来调用clone()创建，因此实际上是管理线程的子线程。（此处子
线程的概念应该当作子进程来理解。）</p>
		<p>__pthread_manager()就是管理线程的主循环所在，在进行一系列初始
化工作后，进入while(1)循环。在循环中，线程以2秒为timeout查询（__poll()）管理管道的读端。在处理请求前，检查其父线程（也就
是创建manager的主线程）是否已退出，如果已退出就退出整个进程。如果有退出的子线程需要清理，则调用pthread_reap_children
()清理。</p>
		<p>然后才是读取管道中的请求，根据请求类型执行相应操作（switch-case）。具体的请求处理，源码中比较清楚，这里就不赘述了。</p>
		<p>
				<a name="N10102">
						<span class="smalltitle">3.线程栈</span>
				</a>
		</p>
		<p>在LinuxThreads中，管理线程的栈和用户线程的栈是分离的，管理线程在进程堆中通过malloc()分配一个THREAD_MANAGER_STACK_SIZE字节的区域作为自己的运行栈。</p>
		<p>用
户线程的栈分配办法随着体系结构的不同而不同，主要根据两个宏定义来区分，一个是NEED_SEPARATE_REGISTER_STACK，这个属性仅
在IA64平台上使用；另一个是FLOATING_STACK宏，在i386等少数平台上使用，此时用户线程栈由系统决定具体位置并提供保护。与此同时，
用户还可以通过线程属性结构来指定使用用户自定义的栈。因篇幅所限，这里只能分析i386平台所使用的两种栈组织方式：FLOATING_STACK方式
和用户自定义方式。</p>
		<p>在FLOATING_STACK方式下，LinuxThreads利用mmap()从内核空间中分配
8MB空间（i386系统缺省的最大栈空间大小，如果有运行限制（rlimit），则按照运行限制设置），使用mprotect()设置其中第一页为非访
问区。该8M空间的功能分配如下图：</p>
		<br />
		<img alt="图4 栈结构示意" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/4.gif" border="0" height="246" width="527" />
		<br />
		<p>低地址被保护的页面用来监测栈溢出。</p>
		<p>对于用户指定的栈，在按照指针对界后，设置线程栈顶，并计算出栈底，不做保护，正确性由用户自己保证。</p>
		<p>不论哪种组织方式，线程描述结构总是位于栈顶紧邻堆栈的位置。</p>
		<p>
				<a name="N1012B">
						<span class="smalltitle">4.线程id和进程id</span>
				</a>
		</p>
		<p>每个LinuxThreads线程都同时具有线程id和进程id，其中进程id就是内核所维护的进程号，而线程id则由LinuxThreads分配和维护。</p>
		<p>__pthread_initial_thread
的线程id为PTHREAD_THREADS_MAX，__pthread_manager_thread的是
2*PTHREAD_THREADS_MAX+1，第一个用户线程的线程id为PTHREAD_THREADS_MAX+2，此后第n个用户线程的线程
id遵循以下公式：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">	tid=n*PTHREAD_THREADS_MAX+n+1<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这种分配方式保证了进程中所有的线程（包括已经退出）都不会有相同的线程id，而线程id的类型pthread_t定义为无符号长整型（unsigned long int），也保证了有理由的运行时间内线程id不会重复。</p>
		<p>从线程id查找线程数据结构是在pthread_handle()函数中完成的，实际上只是将线程号按PTHREAD_THREADS_MAX取模，得到的就是该线程在__pthread_handles中的索引。</p>
		<p>
				<a name="N10141">
						<span class="smalltitle">5.线程的创建</span>
				</a>
		</p>
		<p>在pthread_create
()向管理线程发送REQ_CREATE请求之后，管理线程即调用pthread_handle_create()创建新线程。分配栈、设置thread
属性后，以pthread_start_thread()为函数入口调用__clone()创建并启动新线程。pthread_start_thread
()读取自身的进程id号存入线程描述结构中，并根据其中记录的调度方法配置调度。一切准备就绪后，再调用真正的线程执行函数，并在此函数返回后调用
pthread_exit()清理现场。</p>
		<p>
				<a name="N1014A">
						<span class="smalltitle">6.LinuxThreads的不足</span>
				</a>
		</p>
		<p>由于Linux内核的限制以及实现难度等等原因，LinuxThreads并不是完全POSIX兼容的，在它的发行README中有说明。</p>
		<p>
				<b>1)进程id问题</b>
		</p>
		<p>这个不足是最关键的不足，引起的原因牵涉到LinuxThreads的"一对一"模型。</p>
		<p>Linux
内核并不支持真正意义上的线程，LinuxThreads是用与普通进程具有同样内核调度视图的轻量级进程来实现线程支持的。这些轻量级进程拥有独立的进
程id，在进程调度、信号处理、IO等方面享有与普通进程一样的能力。在源码阅读者看来，就是Linux内核的clone()没有实现对
CLONE_PID参数的支持。</p>
		<p>在内核do_fork()中对CLONE_PID的处理是这样的：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">          if (clone_flags &amp; CLONE_PID) {<br />                if (current-&gt;pid)<br />                        goto fork_out;<br />        }<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这段代码表明，目前的Linux内核仅在pid为0的时候认可CLONE_PID参数，实际上，仅在SMP初始化，手工创建进程的时候才会使用CLONE_PID参数。</p>
		<p>按照POSIX定义，同一进程的所有线程应该共享一个进程id和父进程id，这在目前的"一对一"模型下是无法实现的。</p>
		<p>
				<b>2)信号处理问题</b>
		</p>
		<p>由于异步信号是内核以进程为单位分发的，而LinuxThreads的每个线程对内核来说都是一个进程，且没有实现"线程组"，因此，某些语义不符合POSIX标准，比如没有实现向进程中所有线程发送信号，README对此作了说明。</p>
		<p>如
果核心不提供实时信号，LinuxThreads将使用SIGUSR1和SIGUSR2作为内部使用的restart和cancel信号，这样应用程序就
不能使用这两个原本为用户保留的信号了。在Linux kernel
2.1.60以后的版本都支持扩展的实时信号（从_SIGRTMIN到_SIGRTMAX），因此不存在这个问题。</p>
		<p>某些信号的缺省动作难以在现行体系上实现，比如SIGSTOP和SIGCONT，LinuxThreads只能将一个线程挂起，而无法挂起整个进程。</p>
		<p>
				<b>3)线程总数问题</b>
		</p>
		<p>LinuxThreads将每个进程的线程最大数目定义为1024，但实际上这个数值还受到整个系统的总进程数限制，这又是由于线程其实是核心进程。</p>
		<p>在kernel 2.4.x中，采用一套全新的总进程数计算方法，使得总进程数基本上仅受限于物理内存的大小，计算公式在kernel/fork.c的fork_init()函数中：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">	max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在i386
上，THREAD_SIZE=2*PAGE_SIZE，PAGE_SIZE=2^12（4KB），mempages=物理内存大小/PAGE_SIZE，
对于256M的内存的机器，mempages=256*2^20/2^12=256*2^8，此时最大线程数为4096。</p>
		<p>但为了保证每个用户（除了root）的进程总数不至于占用一半以上物理内存，fork_init()中继续指定：</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">    init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;<br />    init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;<br /></pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这些进程数目的检查都在do_fork()中进行，因此，对于LinuxThreads来说，线程总数同时受这三个因素的限制。</p>
		<p>
				<b>4)管理线程问题</b>
		</p>
		<p>管理线程容易成为瓶颈，这是这种结构的通病；同时，管理线程又负责用户线程的清理工作，因此，尽管管理线程已经屏蔽了大部分的信号，但一旦管理线程死亡，用户线程就不得不手工清理了，而且用户线程并不知道管理线程的状态，之后的线程创建等请求将无人处理。</p>
		<p>
				<b>5)同步问题</b>
		</p>
		<p>LinuxThreads中的线程同步很大程度上是建立在信号基础上的，这种通过内核复杂的信号处理机制的同步方式，效率一直是个问题。</p>
		<p>
				<b>6）其他POSIX兼容性问题</b>
		</p>
		<p>Linux中很多系统调用，按照语义都是与进程相关的，比如nice、setuid、setrlimit等，在目前的LinuxThreads中，这些调用都仅仅影响调用者线程。</p>
		<p>
				<b>7）实时性问题</b>
		</p>
		<p>线程的引入有一定的实时性考虑，但LinuxThreads暂时不支持，比如调度选项，目前还没有实现。不仅LinuxThreads如此，标准的Linux在实时性上考虑都很少。</p>
		<br />
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td>
										<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
										<br />
										<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" align="right" cellpadding="0" cellspacing="0">
				<tbody>
						<tr align="right">
								<td>
										<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
										<br />
										<table border="0" cellpadding="0" cellspacing="0">
												<tbody>
														<tr>
																<td valign="middle">
																		<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																		<br />
																</td>
																<td align="right" valign="top">
																		<a href="http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/#main" class="fbox">
																				<b>回页首</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N101BC">
						<span class="atitle">四.其他的线程实现机制</span>
				</a>
		</p>
		<p>LinuxThreads
的问题，特别是兼容性上的问题，严重阻碍了Linux上的跨平台应用（如Apache）采用多线程设计，从而使得Linux上的线程应用一直保持在比较低
的水平。在Linux社区中，已经有很多人在为改进线程性能而努力，其中既包括用户级线程库，也包括核心级和用户级配合改进的线程库。目前最为人看好的有
两个项目，一个是RedHat公司牵头研发的NPTL（Native Posix Thread
Library），另一个则是IBM投资开发的NGPT（Next Generation Posix
Threading），二者都是围绕完全兼容POSIX
1003.1c，同时在核内和核外做工作以而实现多对多线程模型。这两种模型都在一定程度上弥补了LinuxThreads的缺点，且都是重起炉灶全新设
计的。</p>
		<p>
				<b>1.NPTL</b>
		</p>
		<p>NPTL的设计目标归纳可归纳为以下几点：</p>
		<ul>
				<li>POSIX兼容性</li>
				<li>SMP结构的利用</li>
				<li>低启动开销</li>
				<li>低链接开销（即不使用线程的程序不应当受线程库的影响）</li>
				<li>与LinuxThreads应用的二进制兼容性</li>
				<li>软硬件的可扩展能力</li>
				<li>多体系结构支持</li>
				<li>NUMA支持</li>
				<li>与C++集成</li>
		</ul>
		<p>在
技术实现上，NPTL仍然采用1:1的线程模型，并配合glibc和最新的Linux
Kernel2.5.x开发版在信号处理、线程同步、存储管理等多方面进行了优化。和LinuxThreads不同，NPTL没有使用管理线程，核心线程
的管理直接放在核内进行，这也带了性能的优化。</p>
		<p>主要是因为核心的问题，NPTL仍然不是100%POSIX兼容的，但就性能而言相对LinuxThreads已经有很大程度上的改进了。</p>
		<p>
				<b>2.NGPT</b>
		</p>
		<p>IBM的开放源码项目NGPT在2003年1月10日推出了稳定的2.2.0版，但相关的文档工作还差很多。就目前所知，NGPT是基于GNU Pth（GNU Portable Threads）项目而实现的M:N模型，而GNU Pth是一个经典的用户级线程库实现。</p>
		<p>按照2003年3月NGPT官方网站上的通知，NGPT考虑到NPTL日益广泛地为人所接受，为避免不同的线程库版本引起的混乱，今后将不再进行进一步开发，而今进行支持性的维护工作。也就是说，NGPT已经放弃与NPTL竞争下一代Linux POSIX线程库标准。</p>
		<p>
				<b>3.其他高效线程机制</b>
		</p>
		<p>此
处不能不提到Scheduler
Activations。这个1991年在ACM上发表的多线程内核结构影响了很多多线程内核的设计，其中包括Mach3.0、NetBSD和商业版本
Digital Unix（现在叫Compaq True64
Unix）。它的实质是在使用用户级线程调度的同时，尽可能地减少用户级对核心的系统调用请求，而后者往往是运行开销的重要来源。采用这种结构的线程机
制，实际上是结合了用户级线程的灵活高效和核心级线程的实用性，因此，包括Linux、FreeBSD在内的多个开放源码操作系统设计社区都在进行相关研
究，力图在本系统中实现Scheduler Activations。</p>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">参考资料 </span>
				</a>
		</p>
		<ul>
				<li>[Linus Torvalds，2002] Linux内核源码v2.4.20<br /><br /></li>
				<li>[GNU，2002] Glibc源码v2.2.2（内含LinuxThreads v0.9）<br /><br /></li>
				<li>[Thomas E. Terrill，1997] An Introduction to Threads Using The LinuxThreads Interface<br /><br /></li>
				<li>[Ulrich Drepper，Ingo Molnar，2003] The Native POSIX Thread Library for Linux<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/oss/pthreads/?S_TACT=105AGX52&amp;S_CMP=cn-a-l">http://www.ibm.com/developerworks/oss/pthreads/</a>，NGPT官方网站
        <br /><br /></li>
				<li>[Ralf S. Engelschall，2000] Portable Multithreading<br /><br /></li>
				<li>[Thomas
E. Anderson, Brian N. Bershad, Edward D. Lazowska, Henry M. Levy，1992]
Scheduler Activations: Effective Kernel Support for the User-Level
Management of Parallelism<br /><br /></li>
				<li>[pcjockey@21cn.com] Linux线程初探<br /></li>
		</ul>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">关于作者</span>
				</a>
		</p>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td colspan="3">
										<img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%" />
								</td>
						</tr>
						<tr align="left" valign="top">
								<td>
										<br />
								</td>
								<td>
										<img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="4" />
								</td>
								<td width="100%">
										<p>杨沙洲，目前在国防科技大学计算机学院攻读软件方向博士学位。</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29745.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-11 01:38 <a href="http://www.cppblog.com/mydriverc/articles/29745.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 线程实现机制分析</title><link>http://www.cppblog.com/mydriverc/articles/29744.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Fri, 10 Aug 2007 17:37:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29744.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29744.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29744.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29744.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29744.html</trackback:ping><description><![CDATA[
		<blockquote>自从多线程编程的概念出现在 Linux 中以来，Linux
多线应用的发展总是与两个问题脱不开干系：兼容性、效率。本文从线程模型入手，通过分析目前 Linux 平台上最流行的 LinuxThreads
线程库的实现及其不足，描述了 Linux 社区是如何看待和解决兼容性和效率这两个问题的。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>
				<a name="N1003E">
						<span class="atitle">一.基础知识：线程和进程</span>
				</a>
		</p>
		<p>按照教科书上的定义，进程是资源管理的最小单位，线程是程序执行的最小单位。在操作系统设计上，<b>从进程演化出线程，最主要的目的就是更好的支持SMP以及减小（进程/线程）上下文切换开销。</b></p>
		<p>无
论按照怎样的分法，一个进程至少需要一个线程作为它的指令执行体，进程管理着资源（比如cpu、内存、文件等等），而将线程分配到某个cpu上执行。一个
进程当然可以拥有多个线程，此时，如果进程运行在SMP机器上，它就可以同时使用多个cpu来执行各个线程，达到最大程度的并行，以提高效率；同时，即使
是在单cpu的机器上，采用多线程模型来设计程序，正如当年采用多进程模型代替单进程模型一样，使设计更简洁、功能更完备，程序的执行效率也更高，例如采
用多个线程响应多个输入，而此时多线程模型所实现的功能实际上也可以用多进程模型来实现，而与后者相比，线程的上下文切换开销就比进程要小多了，从语义上
来说，同时响应多个输入这样的功能，实际上就是共享了除cpu以外的所有资源的。</p>
		<p>
				<b>针对线程模型的两大意义，分别开发出了核
心级线程和用户级线程两种线程模型，分类的标准主要是线程的调度者在核内还是在核外。前者更利于并发使用多处理器的资源，而后者则更多考虑的是上下文切换
开销。</b>在目前的商用系统中，通常都将两者结合起来使用，既提供核心线程以满足smp系统的需要，也支持用线程库的方式在用户态实现另一套线程机制，此时一
个核心线程同时成为多个用户态线程的调度者。正如很多技术一样，"混合"通常都能带来更高的效率，但同时也带来更大的实现难度，出于"简单"的设计思路，
Linux从一开始就没有实现混合模型的计划，但它在实现上采用了另一种思路的"混合"。</p>
		<p>在线程机制的具体实现上，可以在
操作系统内核上实现线程，也可以在核外实现，后者显然要求核内至少实现了进程，而前者则一般要求在核内同时也支持进程。核心级线程模型显然要求前者的支
持，而用户级线程模型则不一定基于后者实现。这种差异，正如前所述，是两种分类方式的标准不同带来的。</p>
		<p>当核内既支持进程也
支持线程时，就可以实现线程-进程的"多对多"模型，即一个进程的某个线程由核内调度，而同时它也可以作为用户级线程池的调度者，选择合适的用户级线程在
其空间中运行。这就是前面提到的"混合"线程模型，既可满足多处理机系统的需要，也可以最大限度的减小调度开销。绝大多数商业操作系统（如Digital
Unix、Solaris、Irix）都采用的这种能够完全实现POSIX1003.1c标准的线程模型。在核外实现的线程又可以分为"一对一"、"多对
一"两种模型，前者用一个核心进程（也许是轻量进程）对应一个线程，将线程调度等同于进程调度，交给核心完成，而后者则完全在核外实现多线程，调度也在用
户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式，显然，这种核外的线程调度器实际上只需要完成线程运行栈的切换，调度开销非常小，但同时因
为核心信号（无论是同步的还是异步的）都是以进程为单位的，因而无法定位到线程，所以这种实现方式不能用于多处理器系统，而这个需求正变得越来越大，因
此，在现实中，纯用户级线程的实现，除算法研究目的以外，几乎已经消失了。</p>
		<p>Linux内核只提供了轻量进程的支持，限制了
更高效的线程模型的实现，但Linux着重优化了进程的调度开销，一定程度上也弥补了这一缺陷。目前最流行的线程机制LinuxThreads所采用的就
是线程-进程"一对一"模型，调度交给核心，而在用户级实现一个包括信号处理在内的线程管理机制。Linux-LinuxThreads的运行机制正是本
文的描述重点。</p>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29744.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-08-11 01:37 <a href="http://www.cppblog.com/mydriverc/articles/29744.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>