📄 chapter6.htm
字号:
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">多进程和隔离不同进程地址空间一直都在向更小的计算机上移植,目前在个人电脑以及英特网服务器端都已经十分普通。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt"><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">嵌入式应用中常常会明确的运用多进程机制,但几乎没有多少嵌入式操作系统使用隔离的地址空间。或许这归咎于这种机制在嵌入式</SPAN>CPU<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以及它们上面的操作系统上用处不大并且带来不稳定性,因而显得不那么重要。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt">MIPS<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这种如此之必要以致于导致在</SPAN>1986<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年时工作站</SPAN>CPU<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变的廉价起来的简单机制,或许也可以被证实跟</SPAN>90<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年代后期嵌入式系统的兴起有一定关系。甚至是很小的应用,也被迅速增长的代码大小所困扰,需要使用所有已知的手段来控制软件的复杂度;这种由</SPAN>MIPS<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">首创的灵活的基于软件的方法看来能提供任何所需空间。仅仅几年前,</SPAN>CPU<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的厂商们在定位嵌入式市场时还很难确定</SPAN>MMU<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是否值得包括进去;然而到</SPAN>1997<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年,微软推出的无法在没有内存管理硬件的环境下运行的</SPAN>Windows/CE<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,已被视为针对嵌入式所面对的各种困难的一个成功解决方案。</SPAN></P>
<P class=MsoNormal><B><SPAN
style="FONT-SIZE: 15pt; mso-bidi-font-size: 12.0pt">6.1 </SPAN><SPAN lang=ZH-CN
style="FONT-SIZE: 15pt; FONT-FAMILY: SimSun; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">大型计算机上的内存管理</SPAN><SPAN
style="FONT-SIZE: 15pt; mso-bidi-font-size: 12.0pt"><O:P> </O:P></SPAN></B></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或许从一个类似</SPAN>unix<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">系统的内存管理系统的整个工作开始讨论是最容易的(选择</SPAN>unix<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">作为研究是因为:尽管它体积庞大,却比</SPAN>PC<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上的操作系统简单的多)。在图</SPAN>6-1<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中展示了其典型特征。</SPAN></P>
<P class=MsoNormal><SPAN
style="FONT-SIZE: 14pt; mso-bidi-font-size: 12.0pt">6.1.1 </SPAN><SPAN
lang=ZH-CN
style="FONT-SIZE: 14pt; FONT-FAMILY: SimSun; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">基本的进程空间布局和保护</SPAN><SPAN
style="FONT-SIZE: 14pt; mso-bidi-font-size: 12.0pt"><O:P> </O:P></SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</SPAN>6-1<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中最宽的分隔线是在低半部分——标明“用户程序可访问”的那部分——以及剩余部分之间的。程序的空间中的用户可访问部分就是我们在</SPAN>2.8<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">节所描述的通常在</SPAN>MIPS<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">内存映射中称为“</SPAN>kuseg<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">”的部分。所有的高位地址内存都保留给操作系统。从操作系统的角度来看,地址低半部分是一个用户程序可以随心所欲使用的安全的“沙盒”(</SPAN>sandbox<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">)。假如程序运行错误并且毁坏了自己所有的数据,其他程序并不用担心受到影响。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">从应用程序的角度来看,这块区域可以随意用来创建独享的复杂数据结构来继续自己的工作。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在用户区域内部,也就是在“沙盒”的内部,操作系统给有需求的程序提供更多的栈空间(由于栈在暗中向下增长)。同时也提供一个系统调用,用来从一个以“预先声明数据区域”(</SPAN>declared
data<SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">)最高地址为起始地址并且不断增长的地址空间——人们称之为“堆”(</SPAN>heap<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">)——当中来获取更多数据空间。“堆”用来实现诸如</SPAN>malloc()<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这样的用于给应用程序提供大块额外内存的库函数。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用来构建堆和栈的内存块应该小到足以使系统节约内存,但同时也必须大到可以避免过多的系统调用或者访存异常的产生。不过,在每一次系统调用或访存异常时,操作系统会有机会监督应用程序的内存消耗。操作系统可以增强限制以确保应用程序不会获取过多的内存以致于威胁到系统中关键的运行活动。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</SPAN>unix<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型的系统中,进程在操作系统内核中拥有自己的识别符;为了确保应用程序只能做它们被允许的事情,绝大多数内核服务以特殊子函数的形式(即系统调用)提供出来,应用程序调用时也必须遵从一定的特殊规定。</SPAN></P>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><IMG height=558
src="" width=399 v:shapes="_x0000_i1025"></P>
<P class=MsoNormal style="TEXT-ALIGN: center" align=center><B><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</SPAN>6.1<O:P>
</O:P></B></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">操作系统自己的代码和数据显然不能被用户空间的程序访问到。在某些系统里,这是通过把它们完全安置于一个隔离的地址空间内来完成的;在</SPAN>MIPS<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上运行的操作系统则与用户程序共享同一地址空间,当</SPAN>CPU<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">运行在用户级权限下时,访问内核的空间是非法的,将会导致一个异常产生。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">需要注意的是,尽管每个进程的用户空间会映射到专属于本进程的物理存储空间上去,操作系统的空间却往往是共享的。大部分的操作系统代码以及资源在所有进程看来位于相同的地址——操作系统内核内部是一个多线程单地址空间的——而每个进程的用户地址空间则位于专属自己的隔离空间。应用程序发出的系统调用在内核里的运行过程是完全可信赖的,而应用程序则根本无须被信任。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用户空间的有效部分是被分开的,栈位于空间顶端,而代码和静态编译的数据位于底部。这就使得栈可以向下增长(这是隐式的,由于程序的运行中函数参数的累积)而数据空间能够向上增长(这是显式的,由于程序调用了分配内存的库函数)。操作系统能够为栈或数据空间分配更多的内存并映射到合适的地址上去。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">请注意,为了使程序能够使用庞大的数据空间,通常会让栈从用户空间所允许的最高地址开始向下增长。地址转换方案中必须要妥善应对这种在大跨度的范围内使用地址空间(在被使用空间中有一个巨大空洞)的地址映射特征。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实时系统中为了寻求效率和更多的共享函数,机制更加复杂化。大多数系统把应用程序的代码映射为“只读”(</SPAN>read-only<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">),这意味着这些代码可以被许多进程安全的进行共享——许多进程运行同一应用程序的情况也很常见。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">许多系统不仅仅共享整个应用程序,还可以共享通过库调用来访问的程序段(共享库)。目前我们还是先暂不讨论这所引发的另外一大堆问题吧。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"> <O:P> </O:P></P>
<P class=MsoNormal><SPAN
style="FONT-SIZE: 14pt; mso-bidi-font-size: 12.0pt">6.1.2 </SPAN><SPAN
lang=ZH-CN
style="FONT-SIZE: 14pt; FONT-FAMILY: SimSun; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">把进程空间映射到物理内存</SPAN><SPAN
style="FONT-SIZE: 14pt; mso-bidi-font-size: 12.0pt"><O:P> </O:P></SPAN></P>
<P class=MsoNormal><SPAN
style="mso-tab-count: 1">
</SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">支持这个模型需要什么机制呢?</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN>MIPS<SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">体系结构或多或少地要求程序(不管是应用程序还是内核方法)的地址空间在编译连接期间固定下来。这意味着应用程序在构建时不可能明确使用不同的地址——在我们希望运行同一应用程序的不同拷贝时也是如此。因此,当程序运行时,它的地址会映射到一个在程序装入时就已经由操作系统所固定下来的物理地址上。</SPAN></P>
<P class=MsoNormal style="MARGIN-LEFT: 21pt"><SPAN
style="mso-tab-count: 1"> </SPAN><SPAN lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">尽管在进程上下文切换时更新所有的地址映射信息是可行的,但其效率相当之低。替代办法是:我们给每个进程一个编号(在</SPAN>unix<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">里称作“进程</SPAN>ID<SPAN
lang=ZH-CN
style="FONT-FAMILY: SimSun; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">”,但更准确的叫法应该是“地址空间</SPAN>ID<SPAN
lang=ZH-CN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -