Chinaunix首页 | 论坛 | 博客
  • 博客访问: 329371
  • 博文数量: 79
  • 博客积分: 2466
  • 博客等级: 大尉
  • 技术积分: 880
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-07 16:47
文章分类

全部博文(79)

文章存档

2014年(3)

2012年(7)

2011年(14)

2010年(2)

2009年(2)

2008年(2)

2007年(18)

2006年(31)

分类: LINUX

2006-05-17 12:01:07

从这一篇开始,贴出来的时候不贴原文,也不再完整地做全文的翻译,只挑其中的重点和自己不熟悉的部分翻译并且贴出来,以便加深理解和日后复习。

 

 

 

1.6.8. 内存管理

  

1.6.8.1. 虚存

 

所有新的Unix系统都提供一种很有用的抽象,叫做虚存。虚存,是介于应用程序的内存请求和硬件中的内存管理单元之间的一个逻辑层。虚存有很多目标和好处:

 

很多进程可以同时在系统中执行。

 

可以运行内存需求比实际的物理内存空间更大的程序

 

进程可以在程序仅仅部分装载到内存中的时候,就可以开始执行

 

每个进程都可以被限制到,只能访问全部可用的物理内存中的一部分

 

不同进程可以共享一个库文件或者程序的内存映像。

 

程序可以被重定位,即它们可以被装载到物理内存的任何位置。

 

程序员可以写出不依赖于具体硬件的代码,因为他们不需要关心物理内存的组织方式。

 

虚存地址空间这个概念是虚存子系统的主要构成成分。一个进程可以引用的内存地址空间,不同于物理内存地址空间。当一个进程引用一个虚存地址的时候,内核和MMU合作,将其翻译为被引用的内存元素的物理地址。虚存地址还有一些其他的称呼,这是依赖于计算机本身的体系结构的。Intel体系的硬件手册通常只说“逻辑地址”。

 

当今的CPU都包括了能够自动将虚存地址转换为物理地址的硬件电路。可用的RAM被划分为页框,这些页框通常的大小是4KB或者8KB。页表被用来定义虚存地址怎样和物理地址对应。这些电路简化了内存分配,如果有分配一个很大的连续虚存地址的请求,可以分配物理地址并不连续的页框,通过设定页表来使它们对应的虚存地址连续,最终满足这个内存分配请求。

 

1.6.8.2. 随机访问内存的使用

 

所有的UNIX操作系统都把随机访问内存清晰地划分为两个部分。其中大概有几Mega的内存被专门用来存储内核映像(包括内核代码和静态数据结构)。其余的部分通常被虚存系统管理,可能被用于以下三种用途:

 

满足内核对缓冲区,描述符,以及其他动态内核数据结构的存储需求。

 

满足进程对内存区域的一般访问需求,或者把文件映射到内存的需求。

 

当作高速缓存使用,以提高磁盘和其他缓冲设备的性能

 

RAM是有限的,所以对各种请求类型必须有个平衡,尤其是当剩余的可用内存不多的时候。此外如果可用内存量低于某个门限,内存严重不足的时候,内存页面释放过程必须释放一些页面,但怎样选择释放哪些页面?对这个问题还没有有效的理论来解决,一般的做法只是选择一个经过严格调试的、从实践经验得来的算法。

 

虚存系统必须解决的一个问题是内存的碎片化。理想状态下,一个内存访问请求仅在可用的物理内存(不一定连续)总量不足的时候,才会失败。但是内核在很多情况下不得不使用连续的物理内存。因此在很多情况下,虽然可用物理内存的总量足够,但不连续,也会造成内存访问失败。

 

1.6.8.3. 内核内存分配器

 

KMA是试图满足系统各部分的内存需求的一个子系统。这些内存需求,部分来自内核的其他子系统,部分来自用户进程通过系统调用扩大进程内存空间的需求。一个好的KMA应该具有以下特征:

 

快。事实上这是最重要的一个特征,因为它会被内核的所有子系统调用(包括中断处理函数)。

 

它对内存的占用越少越好。

 

它必须尽可能地减少内存的碎片化。

 

它必须能和其他内存管理子系统合作,以便从别处借用页面,或者通过他们来释放一些页面。

 

曾经出现过基于各种不同算法的KMA

 

Resource map allocator

 

Power-of-two free lists

 

McKusick-Karels allocator

 

Buddy system

 

Mach's Zone allocator

 

Dynix allocator

 

Solaris 's Slab allocator

 

LinuxKMA使用的是在buddy system上加了一层Slab allocator的结构。

 

1.6.8.4. 进程虚存地址空间处理

 

一个进程的地址空间包括了该进程能够访问的全部虚存地址。内核通常把一个进程的虚存地址空间存储为一个内存区描述符的列表。例如当一个进程通过exec()系列系统调用来开始执行某个程序的时候,内核为该进程指定的虚存地址空间包括:

 

程序的可执行代码

 

已被初始化的程序数据

 

未被初始化的程序数据

 

程序的起始堆栈(即用户态堆栈)

 

程序用到的共享库中的可执行代码和数据

 

堆空间(程序可以动态请求的内存空间)

 

最近的Unix操作系统都采用了一种称为demand paging的内存分配策略。在这种分配策略中,一个进程可以开始执行一个完全不存在于物理内存中的程序。程序试图访问一个不存在的页面的时候,MMU产生一个异常,异常处理过程会找到受影响的内存区域,分配一个空闲的物理内存页面,并用合适的数据将其初始化。类似地,当进程调用malloc()或者brk()系统调用动态申请内存的时候(事实上malloc就是通过brk来实现的),内核所做的仅仅是更新进程堆空间的大小,只有当进程试图通过虚存地址访问新内存引发异常的时候,kernel才会真正为进程分配一个物理内存页面。

 

虚存地址空间还使其他一些高效率的策略可以被应用到系统中。例如copy on write。创建一个新进程的时候,Kernel只需要把父进程的内存页面原样映射到子进程的虚存空间,同时把它标记为“只读”。父/子进程试图修改其中某个页面的时候,会引发一个异常,异常处理的过程就是把一个新的页面分配给引发异常的进程,并用原页面的内容初始化新页面,这样进程就可以对该页面的私有拷贝进行修改了。

 

1.6.8.5 缓存

 

可用物理内存中相当一部分被用于硬盘和其他block device的缓存。硬盘操作是很慢的,一次对硬盘的访问通常需要几毫秒,和访问RAM的时间相比太长了。硬盘常常成为整个系统性能的瓶颈。从最早的Unix系统开始,一个常用的策略就是尽量推迟向硬盘写入数据的操作。这样,从硬盘读出的数据(也许还经过了处理)即使不再被任何进程引用,也还是被保存在RAM中。

 

这个策略是基于这样一个事实:系统中的新进程很可能会访问到结束不久的那些进程刚刚访问过的那些数据。当某个进程试图访问磁盘的时候,Kernel先检查被访问的数据是不是还在内存里,如果是(称为一个”cache hit”),kernel就可以不访问磁盘而满足进程对数据的请求。

 

系统调用sync()用于强制进行磁盘同步,即把全部“脏”缓冲区(内容已经更改,因而与对应磁盘块上不同的缓冲区)的内容强制写入磁盘。为了避免丢失对数据的更改,所有操作系统都会周期性地进行磁盘同步。

 

1.6.9 设备驱动

 

Kernel通过设备驱动程序与IO设备交互。设备驱动程序是Kernel的一部分,包括了用于控制一个或多个设备所需的数据和函数。硬盘、键盘、鼠标、显示器、网卡、SCSI总线上连接的设备,都是通过这种方式得到控制的。驱动程序和系统内核以及其他驱动程序之间,通过定义好的接口合作完成控制功能。这种工作方式有以下的好处:

o. 针对某种特定硬件的代码可以被封装载一个模块内部

o. 设备提供商只需要知道内核-驱动程序之间的接口即可提供驱动程序,无需知道内核内部细节。

o. 内核通过统一的接口一致地访问全部类型的设备

o. 设备驱动可以被实现为允许动态装载的模块,装载后无需重新启动系统,还可以动态卸载不再需要的模块,从而将RAM中的内核映像大小最小化。

 

当一个用户程序需要操纵某个硬件,它会通过常用的对文件进行操作的系统调用,想内核发出请求。这些特别的“设备文件”通常在/dev目录下。事实上设备文件是驱动接口中的用户可见部分。每个设备文件指向一个特定的设备驱动程序,系统内核通过调用驱动程序事先对具体硬件的操控。

 

图形终端被广泛应用后,X Window System之类的应用程序作为Unix标准进程被执行,直接访问图形接口的IO端口以及RAM中与视频控制有关的区域。最近的一些Unix Kernel,如Linux 2.6,对图形卡的frame buffer提供了一种抽象,使应用程序可以在不知道图形设备接口的IO端口的情况下,访问图形接口和设备。

阅读(980) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~