内存管理
有效的内存管理在多道程序设计系统中是至关重要的。如果只有少量进程在内存中,所有进程大部分时间都用来等待IO,这种情况下,处理器也处于空闲状态。因此,必须有效地分配内存来保证有适当数目的就绪进程可以占用这些可用的处理器时间。
内存管理的需求
1:重定位:
在多道程序设计系统中,可用的内存空间通常被多个进程共享。通常情况下,程序员并不能事先知道在某个程序执行期间会有其他哪些程序驻留在内存中。此外还希望通过提供一个巨大的就绪进程池,能够把活动进程换入或换出内存,以便使处理器的利用率最大化。一旦程序被换出磁盘,当下一次被换入时,如果必须放在和被换出前相同的内存区域,那么这将会是一个很大限制。为了避免这种限制,需要把进程重定们到内存的不同区域。
因此,我们事先不知道程序将会被放置到哪个区域,并且我们必须允许程序通过交换技术在内存中移动。处理器硬件和操作系统软件必须能够通过某种方式把程序代码中的内存访问转换成实际的物理内存地址,并反映程序在内存中的当前位置。
2:保护
每个进程都应该受到保护,以免被其他进程有意或无意地干涉。因此,该进程以外的其他进程中的程序不能未经授权地访问(进行读操作或写操作)该进程的内存单元。在某种意义上,要满足重定位的需求增加了满足保护需求的难度。由于程序在内存中的位置是不可预测的,因而在编译时不可能检查绝对地址来确保保护。
内存保护的需求必须由处理器(硬件)来满足,而不是操作系统(软件)来满足。这是因为操作系统不能预测程序可能产生的所有内存访问;即使可以预测,提前审查每个进程中可能存在的内存违法也是非常费时的。因此,只能在指令访问内存时来判断这个内存访问是否违法(存取数据或跳转)。为实现这一点,处理器硬件必须具有这个能力。
3:共享
任何保护机制都必须具有一定的灵活性,以允许多个进程访问内存的同一部分。例如,如果多个进程正在执行同一个程序,则允许每个进程访问该程序的同一副本要比让每一个进程有自己单独副本更有优势。合作完成同一个任务的进程可能需要共享访问相同的数据结构。因此内存管理系统必须允许对内存共享区域进程受控访问,而不会损害基本的保护。
4:逻辑组织
计算机系统中的内存总是被组织成线性的(或一维的)地址空间,并且地址空间是由一系列字节或字组成的。外部存储器(简称外存)在物理层上也是按类似方式组织的。尽管这种组织方式类似于实际的机器硬件,但它并不符合程序构造的典型方法。大多数程序被组织成模块,某些模块是不可修改的(只读、只执行),某些模块包含可以修改的数据。如果操作系统和计算机硬件能够有效地处理以某种模块的形式组织的用户程序和数据,则会带来很多好处
5:物理组织
计算机的存储器至少要被组织成两级,称为内存和外存。内存提供快速的访问,成本也相对较高,并且内存是易失性的,即它不能提供永久存储的。外存比内存慢而且便宜,它通常是非易失性的。因此,大容量的外存可以用于长期存储程序和数据,而较小的内存则用于保存当前使用的程序和数据。
显然,在两级存储间移动信息的任务应该是一种系统的责任,而该任务恰恰就是存储管理的本质所在。
内存分区
内存管理最基本的操作是由处理器把程序装入内存中执行。在大部分现代多道程序设计系统中,这往往还涉及一种称为虚拟内存的精密方案。虚拟内存又基于分段和分页两种基本技术或其中的一种技术。分区技术曾用于许多已经过时的操作系统中。
固定分区
在大多数内存管理方案中,可以假定操作系统占据了内存中的某些固定部分,内存的其余部分可供多个用户进程使用。管理用户内存空间的最简单的方案就是把它分区,从而形成若干边界固定的区域。
使用大小相等的固定分区有两个难点:
1:程序可能太大而不能放到一个分区中。在这种情况下,程序员必须使用覆盖技术设计程序,使得在任何时候该程序只有一部分需要放到内存。当需要的模块不在时,用户程序必须把这个模块装入程序的分区,覆盖掉该分区中的任何程序和数据。
2:内存的利用率非常低。任何程序,即使很小,都需要占据一个完整的分区。
动态分区
为了克服固定分区的一些缺点,又出现了一种动态分区方法。同样,这种方法也已经被很多更先进的内存管理技术所取代。使用该技术的一个重要的操作系统是IBM主机操作系统OS/MVT(具有可变任务数的多道程序设计系统)。
对于动态分区,分区长度和数目是可变的。当进程被装入内存时,系统会给它分配和它所需容量完全相等的内存,不多也不少。动态分区方法在开始时是很好的,但它最终会导致在内存中出现很多小的空洞。随着时间的推移,内存中产生了越来越多的碎片,内存的利用率随之下降。这种现象称为外部碎片。
重定位
在考虑解决分区技术的缺陷之前,必须先解决与进程在内存中的放置相关的一个遗留问题。当使用固定分区方案时,一个进程可以总是被指定到同一个分区。也就是说,当装入一个新进程时,不论选择哪一个分区,当这个进程以后被换出又换入时,仍旧使用这个分区。在这种情况下,需要使用一个简单的定位加载器:当一个进程被首次加载时,代码中的相对内存访问被绝对的内存地址代替,这个绝对地址由进程被加载到的基地址确定。
对于大小相等的分区及只有一个进程队列的大小不等的分区的情况,一个进程在它的生命周期中可能被换出,当它再次换入时,可能被指定到与上一次不同的分区中。动态分区也存在同样的情况。因此,进程访问的(指令和数据单元的)位置不是固定的。当进程被换入或在内存中移动时,指令和数据单元的位置会发生改变。为解决这个问题,需要对几种地址类型进行区分。
逻辑地址是指与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存的访问之前必须把它转换成物理地址。
相对地址是逻辑地址的一个特例,是相对于某些已知点(通常是程序的开始处)的存储单元。
物理地址或绝对地址是数据在内存中的实际位置。
系统采用运行时动态加载的方式把使用相对地址的程序加载到内存。通常情况下,被加载进程中的所有内存访问都相对于程序的开始点。因此,在执行包括这类访问的指令时,需要一个硬件机制把相对地址转换成物理内存地址。
当进程处于运行态时,一个特殊的处理器寄存器,有时也称为基址寄存器,被载入程序在内存中的起始地址。还有一个界限寄存器指明程序的终止位置。当程序被装入内存或当该进程的映像被换入时,必须设置这两个寄存器。在进程的执行过程中会遇到相对地址,包括指令寄存器的内容、跳转或调用指令中的指令地址,以及加载和存储指令中的数据地址。每个这样的相对地址都经过处理器的两步操作。首先,基址寄存器中的值加上相对地址产生一个绝对地址;然后,得到的结果与界限寄存器的值相比较,如果这个地址在界限范围内,则继续该指令的执行;否则,向操作系统发出一个中断信号,操作系统必须以某种方式对这个错误做出响应。并且它还提供了一种保护:每个进程映像根据基址和界限寄存器的内容被隔开,以免受到其他进程的越权访问。
分页
大小不等的固定分区与大小可变的分区技术在内存的使用上都是低效的,前者会产生内部碎片,后者会产生外部碎片。但是,假如内存被划分成大小固定相等的块,且块相对比较小,每个进程也被分成同样大小的小块,那么进程中称为页的块可以指定到内存中称为页框的可用块。使用分页技术在内存中为每个进程浪费的空间,仅仅是进程最后一页的一小部分形成的内部碎片。没有任何外部碎片。
没有足够的连续页框来保存进程D,这会阻止操作系统加载该进程吗?答案是否定的,因为可以使用逻辑地址来解决这个问题。这时仅有一个简单的基址寄存器是不够的,操作系统需要为每个进程维护一个页表。页表给出了该进程的每一页对应的页框的位置。在程序中,每个逻辑地址包括一个页号和在该页中的偏移量。在简单分区的情况下,逻辑地址是一个相对于程序开始处的位置,处理器把它转换成一个物理地址。在分页中,逻辑地址到物理地址转换仍然由处理器硬件完成,并且处理器必须知道如何访问当前进程的页表。给出逻辑地址(页号,偏移量),处理器使用页表产生物理地址(页框号,偏移量)。
分段
细分用户程序的另一种可选方案是分段。采用分段技术,可以把程序和其相关的数据划分到几个段中。尽管段中有一个最大的长度限制,但并不要求所有程序的所有段的长度都相等。和分页一样,采用分段技术时的逻辑地址也是由两部分组成的:段号和偏移量。
由于使用大小不等的段,分段类似于动态分区。在没有采用覆盖方案或使用虚拟内存的情况下,为执行一个程序,需要把它所有段都装入内存。与动态分区不同的是,在分段方案中,一个程序可以占据多个分区,并且这些分区不要求是连续的。分段消除了内部碎片,但是和动态分区一样,它会产生外部碎片。不过由于进程被分为多个小块,因此外部碎处也会很小。
分页对程序员来说是透明的,而分段通常是可见的,并且作为组织程序和数据的一种方便提供给程序。一般情况下,程序员或编译器会把程序和数据指定到不同的段。为了实现模块化程序设计的目的,程序或数据可能进一步分为多个段。这种方法最不方便的地方是程序必须清楚段的最大长度限制。
采用大小不等的段的另一个结果是,逻辑地址和物理地址间不再具有简单的对应关系。类似分布,在简单的分段方案中,每个进程都有一个段表,系统也会维护一个内存中的空闲块列表。每个段表项必须给出相应的段在内存的起始地址,还必须指明段的长度,以确保不会使用无效地址。当进程进入运行状态时,系统会把其段表地址装载到一个寄存器中,由内存管理硬件来使用这个寄存器。
相关推荐
在编程世界中,C语言因其低级特性而被广泛用于系统级编程,其中包括内存管理。内存管理是程序设计中的核心部分,它涉及到如何有效地分配、使用和释放内存资源。本主题将深入探讨如何使用C语言来实现内存管理的关键...
C#内存管理机制 C#内存管理机制是C#编程语言中的一种机制,旨在帮助程序员更好地管理内存资源。在C#中,程序员不需要手动管理内存,因为垃圾收集器会处理所有的内存清理工作。但是,了解C#内存管理机制仍然非常重要...
在计算机科学领域,内存管理是程序设计中的一个关键部分,特别是在高级编程语言中。动态内存管理允许程序在运行时请求和释放内存,提高了程序的灵活性。本项目关注的是连续动态内存管理,具体实现了一个基于“首次...
文将对 Linux™ 程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言。文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半...
Linux内存管理是操作系统的核心组成部分,它负责有效地分配和回收内存资源,确保系统高效稳定地运行。在本实验中,我们主要探讨了Linux虚拟内存管理的原理、动态内存操作函数的使用,以及如何检测主存使用情况。 ...
操作系统内存管理是计算机科学中的核心领域,特别是在Linux这样的操作系统中,理解内存的分配、管理和优化至关重要。本实验报告聚焦于在Red Hat 9环境下进行的内存管理实践,旨在深入理解内存分片、分配策略以及其对...
操作系统内存管理是计算机科学中的核心概念,它涉及如何在多任务环境下有效地分配、使用和回收内存资源。在这个“操作系统内存管理实验(C语言实现)”中,我们将深入探讨这一主题,并通过C语言编写代码来模拟和理解...
Linux 内存管理源代码导读 Linux 内存管理是操作系统中最重要的组件之一,它负责管理计算机的内存资源,确保系统的正确运行。在 Linux 操作系统中,内存管理是通过页框管理、Slab 算法和动态存储器管理来实现的。 ...
在分析glibc内存管理的ptmalloc源代码之前,我们需要先了解一些基础知识,包括操作系统对内存的分配和管理方法,以及glibc内存分配机制。 内存管理是操作系统的一个核心功能,它负责维护和管理计算机系统中的物理和...
C++内存管理是编程领域中的一个关键话题,尤其对于C++开发者来说,它既是挑战,也是机遇。内存管理是C++复杂性和灵活性的核心,既能让经验丰富的程序员获得高性能和自由,也可能让初学者陷入无尽的调试困扰。由于C++...
整个内存管理系统可以分为2部分来看待: 第一部分是对物理内存的管理, 第二部分是对虚拟内存的管理. 物理内存管理的对象是板载的物理内存(DDRAM), 它把物理内存按页划分, 并把这些页放到一个池子里面. 物理内存管理...
模拟内存管理程序 本实验的目的是了解简单的固定大小内存分配方法,掌握分区存储管理技术,了解在分区管理机制下所需的数据结构。通过模拟内存管理程序,学生可以学习如何设计和实现一个基本的内存管理系统,包括...
### Linux源代码分析之内存管理 #### 摘要与关键词 本文档是对Linux内核内存管理系统的一个深入分析,特别是针对其源代码进行了详细的解读。Linux的内存管理是内核中最复杂的部分之一,它涉及到了内存初始化、地址...
在这个“操作系统课程设计内存管理”项目中,我们看到开发者使用了Microsoft Foundation Classes (MFC)库,这是一个C++类库,由微软为Windows应用程序开发设计。MFC简化了用户界面、数据库访问和网络编程等任务,但...
这份"内存管理源代码"提供了深入理解内存管理机制的宝贵资料,对于想要自己编写操作系统或者正在改进无内存管理系统的人来说,具有很高的参考价值。 在操作系统设计中,内存管理的目标主要有三个:防止内存碎片、...
Linux操作系统内存管理 Linux操作系统的内存管理是计算机科学中一个重要的领域。本文将详细阐述Linux操作系统的内存管理机制,包括物理内存和虚拟内存的管理机制、地址映射机制、内存碎片和内存不连续的问题解决等...
内存管理是计算机科学中的核心概念,特别是在编程语言如C++中。动态分区内存管理是一种用于在程序运行时分配和释放内存的方法,它允许程序根据需要动态地调整内存使用。本主题将深入探讨动态分区内存管理的基本原理...