📄 1152.html
字号:
$符号是由用户登录外壳(这里指Bash)提供的提示符。它表示正在等待用户敲入一些命令。敲入ls命令,首先键盘驱动程序识别出敲入的内容。然后键盘驱动将它们传递给外壳程序,由外壳程序来负责查找同名的可执行程序(ls)。 如果在/bin/ls目录中找到了ls,则调用核心服务将ls的可执行映象读入虚拟内存并开始执行。ls调用核心的文件子系统来寻找那些文件是可用的。文件系统使用缓冲过的文件系统信息,或者调用磁盘设备驱动从磁盘上读取信息。当然ls还可能引起网络驱动程序和远程机器来交换信息以找出关于系统要访问的远程文件系统信息(文件系统可以通过网络文件系统或者NFS进行远程安装)。当得到这些信息后,ls将这些信息通过调用视频驱动写到显示器屏幕上。 以上这些听起来十分复杂。这个非常简单命令的处理过程告诉我们操作系统是一组协同工作的函数的集合,它们给所有的用户对系统有一致的印象。 <br>
<br>
<br>
2.2.1 内存管理 <br>
由于资源的有限,比如内存,操作系统处理事务的过程看起来十分冗长。操作系统的一个基本功能就是使一个只有少量物理内存的系统工作起来象有多得多的内存一样。这个大内存叫为虚拟内存。其思想就是欺骗系统中运行的软件,让它们认为有大量内存可用。系统将内存划分成易于处理的页面,在系统运行时将这些页面交换到硬盘上去。 由于有另外一个技巧:多处理的存在,这些软件更加感觉不到系统中真实内存的大小。 <br>
<br>
<br>
2.2.2 进程 <br>
进程可以认为是处于执行状态的程序,每个进程有一个特定的程序实体。观察以下Linux系统中的进程,你会发现有比你想象的要多得多的进程存在。比如,在我的系统中敲入ps命令,将得到以下结果: <br>
<br>
$ ps <br>
PID TTY STAT TIME COMMAND <br>
158 pRe 1 0:00 -bash <br>
174 pRe 1 0:00 sh /usr/X11R6/bin/startx <br>
175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc -- <br>
178 pRe 1 N 0:00 bowman <br>
182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black <br>
184 pRe 1 <br>
185 pRe 1 <br>
187 pp6 1 9:26 /bin/bash <br>
202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black <br>
203 ppc 2 0:00 /bin/bash <br>
1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black <br>
1797 v06 1 0:00 /bin/bash <br>
3056 pp6 3 <br>
3270 pp6 3 0:00 ps <br>
$ <br>
<br>
如果系统有许多个CPU,则每个进程可以运行在不同的CPU上。不幸的是,大多数系统中只有一个CPU。这样 操作系统将轮流运行几个程序以产生它们在同时运行的假象。这种方式叫时间片轮转。同时这种方法还骗过了进程使它们都认为只有自己在运行。进程之间被隔离开,以便某个进程崩溃或者误操作不会影响到别的进程。操作系统通过为每个进程提供分立的地址空间来作到这一点。 <br>
<br>
<br>
2.2.3 设备驱动 <br>
设备驱动组成了Linux核心的主要部分。象操作系统的其他部分一样,它们运行在高权限环境中且一旦出错 将引起灾难性后果。设备驱动控制操作系统和硬件设备之间的相互操作。例如当文件系统通过使用通用块设备接口来对IDE磁盘写入数据块。设备驱动负责处理所有设备相关细节。设备驱动与特定的控制器芯片有关,如果系统中有一个NCR810 SCSI控制卡则需要有NCR810 SCSI的驱动程序。 <br>
<br>
<br>
2.2.4 文件系统 <br>
Linux和Unix一样,系统中的独立文件系统不是通过设备标志符来访问,而是通过表示文件系统的层次树结构来访问。当Linux添加一个新的文件系统到系统中时,会将它mount到一个目录下,比如说/mnt/cdrom。 Linux的一个重要特征就是支持多种文件系统。这使得它非常灵活并且可与其他操作系统并存。Linux中最常用的文件系统是EXT2文件系统,它在大多数Linux分发版本中都得到了支持。 文件系统提供给用户一个关于系统的硬盘上文件和目录的总体映象,而不管文件的类型和底层物理设备的特性。 Linux透明地支持多种文件系统并将当前安装的所有文件和文件系统集成到虚拟文件系统中去。所以,用户和进程一般都不知道某个文件位于哪种文件系统中,他们只是使用它。 块设备驱动将物理块设备类型(例如IDE和SCSI)和文件系统中的差别隐藏起来,物理设备只是数据块的线性存储集合。设备的不同导致块大小的不同,从软盘设备的512字节到IDE磁盘的1024字节。这些都隐藏了起来,对系统用户来说这都是不可见的。不管设备类型如何,EXT2文件系统看起来总是一样。 <br>
<br>
<br>
2.3 核心数据结构 <br>
操作系统可能包含许多关于系统当前状态的信息。当系统发生变化时,这些数据结构必须做相应的改变以反映这些情况。例如,当用户登录进系统时将产生一个新的进程。核心必须创建表示新进程的数据结构,同时 将它和系统中其他进程的数据结构连接在一起。 大多数数据结构存在于物理内存中并只能由核心或者其子系统来访问。数据结构包括数据和指针;还有其他数据结构的地址或者子程序的地址。它们混在一起让Linux核心数据结构看上去非常混乱。尽管可能被几个核心子系统同时用到,每个数据结构都有其专门的用途。理解Linux核心的关键是理解它的数据结构以及Linux核心中操纵这些数据结构的各种函数。本书把Linux核心的 描叙重点放在数据结构上,主要讨论每个核心子系统的算法,完成任务的途径以及对核心数据结构的使用。 <br>
<br>
<br>
2.3.1 连接列表 <br>
Linux使用的许多软件工程的技术来连接它的数据结构。在许多场合下,它使用linked或者chained数据结构。 每个数据结构描叙某一事物,比如某个进程或网络设备,核心必须能够访问到所有这些结构。在链表结构中,个根节点指针包含第一个结构的地址,而在每个结构中又包含表中下一个结构的指针。表的最后一项必须是0或者NULL,以表明这是表的尾部。在双向链表中,每个结构包含着指向表中前一结构和后一结构的指针。使用双向链表的好处在于更容易在表的中部添加与删除节点,但需要更多的内存操作。这是一种典型的操作系统开销与CPU循环之间的折中。 <br>
<br>
2.3.2 散列表 <br>
链表用来连接数据结构比较方便,但链表的操作效率不高。如果要搜寻某个特定内容,我们可能不得不遍历整个链表。Linux使用另外一种技术:散列表来提高效率。散列表是指针的数组或向量,指向内存中连续的相邻数据集合。散列表中每个指针元素指向一个独立链表。如果你使用数据结构来描叙村子里的人,则你可以使用年龄作为索引。为了找到某个人的数据,可以在人口散列表中使用年龄作为索引,找到包含此人特定数据的数据结构。但是在村子里有很多人的年龄相同,这样散列表指针变成了一个指向具有相同年龄的人数据链表的指针。搜索这个小链表的速度显然要比搜索整个数据链表快得多。 由于散列表加快了对数据结构的访问速度,Linux经常使用它来实现Caches。Caches是保存经常访问的信息的子集。经常被核心使用的数据结构将被放入Cache中保存。Caches的缺点是比使用和维护单一链表和散列表更复杂。寻找某个数据结构时,如果在Cache中能够找到(这种情况称为cache 命中),这的确很不错。但是如果没有找到,则必须找出它,并且添加到Cache中去。如果Cache空间已经用完则Linux必须决定哪一个结构将从其中抛弃,但是有可能这个要抛弃的数据就是Linux下次要使用的数据。 <br>
<br>
<br>
2.3.3 抽象接口 <br>
Linux核心常将其接口抽象出来。接口指一组以特定方式执行的子程序和数据结构的集合。例如,所有的网络设备驱动必须提供对某些特定数据结构进行操作的子程序。通用代码可能会使用底层的某些代码。例如网络层代码是通用的,它得到遵循标准接口的特定设备相关代码的支持。 通常在系统启动时,底层接口向更高层接口注册(Register)自身。这些注册操作包括向链表中加入结构节点。例如,构造进核心的每个文件系统在系统启动时将其自身向核心注册。文件/proc/filesysems中可以看到已经向核心注册过的文件系统。注册数据结构通常包括指向函数的指针,以文件系统注册为例,它向Linux核心注册时必须将那些mount文件系统连接时使用的一些相关函数的地址传入。 <br>
<br>
Linux 核心--4.内存管理 <br>
原著: David A Rusling 翻译: Banyan & fifa (2001-04-27 13:54:58) <br>
第三章 存储管理 <br>
<br>
存储管理子系统时操作系统中最重要的组成部分之一。在早期计算时代,由于人们所需要的内存数目远远大于物理内存,人们设计出了各种各样的策略来解决此问题,其中最成功的是虚拟内存技术。它使得系统中为有限物理内存竞争的进程所需内存空间得到满足。 <br>
<br>
虚拟内存技术不仅仅可让我们可以使用更多的内存,它还提供了以下功能: <br>
<br>
巨大的寻址空间 <br>
<br>
操作系统让系统看上去有比实际内存大得多的内存空间。虚拟内存可以是系统中实际物理空间的许多倍。每个进程运行在其独立的虚拟地址空间中。这些虚拟空间相互之间都完全隔离开来,所以进程间不会互相影响。同时,硬件虚拟内存机构可以将内存的某些区域设置成不可写。这样可以保护代码与数据不会受恶意程序的干扰。 <br>
<br>
内存映射 <br>
<br>
内存映射技术可以将映象文件和数据文件直接映射到进程的地址空间。在内存映射中,文件的内容被直接连接到进程虚拟地址空间上。 <br>
<br>
公平的物理内存分配 <br>
<br>
内存管理子系统允许系统中每个运行的进程公平地共享系统中的物理内存。 <br>
<br>
共享虚拟内存 <br>
<br>
尽管虚拟内存允许进程有其独立的虚拟地址空间,但有时也需要在进程之间共享内存。 例如有可能系统中有几个进程同时运行BASH命令外壳程序。为了避免在每个进程的虚拟内存空间内都存在BASH程序的拷贝,较好的解决办法是系统物理内存中只存在一份BASH的拷贝并在多个进程间共享。动态库则是另外一种进程间共享执行代码的方式。共享内存可用来作为进程间通讯(IPC)的手段,多个进程通过共享内存来交换信息。 Linux支持SYSTEM V的共享内存IPC机制。 <br>
<br>
3.1 虚拟内存的抽象模型 <br>
<br>
<br>
<br>
图3.1 虚拟地址到物理地址映射的抽象模型 <br>
<br>
<br>
在讨论Linux是如何具体实现对虚拟内存的支持前,有必要看一下更简单的抽象模型。 <br>
<br>
在处理器执行程序时需要将其从内存中读出再进行指令解码。在指令解码之前它必须向内存中某个位置取出或者存入某个值。然后执行此指令并指向程序中下一条指令。在此过程中处理器必须频繁访问内存,要么取指取数,要么存储数据。 <br>
<br>
虚拟内存系统中的所有地址都是虚拟地址而不是物理地址。通过操作系统所维护的一系列表格由处理器实现由虚拟地址到物理地址的转换。 <br>
<br>
为了使转换更加简单,虚拟内存与物理内存都以页面来组织。不同系统中页面的大小可以相同,也可以不同,这样将带来管理的不便。Alpha AXP处理器上运行的Linux页面大小为8KB,而Intel X86系统上使用4KB页面。每个页面通过一个叫页面框号的数字来标示(PFN) 。 <br>
<br>
页面模式下的虚拟地址由两部分构成:页面框号和页面内偏移值。如果页面大小为4KB,则虚拟地址的 11:0位表示虚拟地址偏移值,12位以上表示虚拟页面框号。处理器处理虚拟地址时必须完成地址分离工作。在页表的帮助下,它将虚拟页面框号转换成物理页面框号,然后访问物理页面中相应偏移处。 <br>
<br>
图3.1给出了两个进程X和Y的虚拟地址空间,它们拥有各自的页表。这些页表将各个进程的虚拟页面映射到内存中的物理页面。在图中,进程X的虚拟页面框号0被映射到了物理页面框号4。理论上每个页表入口应包含以下内容: <br>
<br>
<br>
有效标记,表示此页表入口是有效的 <br>
页表入口描叙的物理页面框号 <br>
访问控制信息。用来描叙此页可以进行哪些操作,是否可写?是否包含执行代码? <br>
虚拟页面框号是为页表中的偏移。虚拟页面框号5对应表中的第6个单元(0是第一个)。 <br>
<br>
为了将虚拟地址转换为物理地址,处理器首先必须得到虚拟地址页面框号及页内偏移。一般将页面大小设为2的次幂。将图3.1中的页面大小设为0x2000字节(十进制为8192)并且在进程Y的虚拟地址空间中某个地址为0x2194,则处理器将其转换为虚拟页面框号1及页内偏移0x194。 <br>
<br>
处理器使用虚拟页面框号为索引来访问处理器页表,检索页表入口。如果在此位置的页表入口有效,则处理器将从此入口中得到物理页面框号。如果此入口无效,则意味着处理器存取的是虚拟内存中一个不存在的区域。在这种情况下,处理器是不能进行地址转换的,它必须将控制传递给操作系统来完成这个工作。 <br>
<br>
某个进程试图访问处理器无法进行有效地址转换的虚拟地址时,处理器如何将控制传递到操作系统依赖于具体的处理器。通常的做法是:处理器引发一个页面失效错而陷入操作系统核心,这样操作系统将得到有关无效虚拟地址的信息以及发生页面错误的原因。 <br>
<br>
再以图3.1为例,进程Y的虚拟页面框号1被映射到系统物理页面框号4,则再物理内存中的起始位置为 0x8000(4 * 0x2000)。加上0x194字节偏移则得到最终的物理地址0x8194。 <br>
<br>
通过将虚拟地址映射到物理地址,虚拟内存可以以任何顺序映射到系统物理页面。例如,在图3.1中,进程X的虚拟页面框号0被映射到物理页面框号1而虚拟页面框号7被映射到物理页面框号0,虽然后者的虚拟页面框号要高于前者。这样虚拟内存技术带来了有趣的结果:虚拟内存中的页面无须在物理内存保持特定顺序。 <br>
<br>
<br>
<br>
3.1.1 请求换页 <br>
在物理内存比虚拟内存小得多的系统中,操作系统必须提高物理内存的使用效率。节省物理内存的一种方法是仅加载那些正在被执行程序使用的虚拟页面。比如说,某个数据库程序可能要对某个数据库进行查询操作,此时并不是数据库的所有内容都要加载到内存中去,而只加载那些要用的部分。如果此数据库查询是一个搜索查询而无须对数据库进行添加记录操作,则加载添加记录的代码是毫无意义的。这种仅将要访问的虚拟页面载入的技术叫请求换页。 <br>
<br>
当进程试图访问当前不在内存中的虚拟地址时,处理器在页表中无法找到所引用地址的入口。在图3.1中,对于虚拟页面框号2,进程X的页表中没有入口,这样当进程X试图访问虚拟页面框号2内容时,处理器不能将此地址转换成物理地址。这时处理器通知操作系统有页面错误发生。 <br>
<br>
如果发生页面错的虚拟地址是无效的,则表明进程在试图访问一个不存在的虚拟地址。这可能是应用程序出错而引起的,例如它试图对内存进行一个随机的写操作。此时操作系统将终止此应用的运行以保护系统中其他进程不受此出错进程的影响。 <br>
<br>
如果出错虚拟地址是有效的,但是它指向的页面当前不在内存中,则操作系统必须将此页面从磁盘映象中读入到内存中来。由于访盘时间较长,进程必须等待一段时间直到页面被取出来。如果系统中还存在其他进程,操作系统就会在读取页面过程中的等待过程中选择其中之一来运行。读取回来的页面将被放在一个空闲的物理页面框中,同时此进程的页表中将添加对应此虚拟页面框号的入口。最后进程将从发生页面错误的地方重新开始运行。此时整个虚拟内存访问过程告一段落,处理器又可以继续进行虚拟地址到物理地址转换,而进程也得以继续运行。 <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -