fork、内存管理与虚拟内存总结

一、fork复制进程

在UNIX系统中,进程创建是通过内核系统调用fork()实现的,其本质是将父进程复制一份,生成子进程,且子进程与父进程共享部分资源,同时拥有独立标识和执行上下文。

1.复制流程

当一个进程产生一个fork请求时,操作系统会执行以下功能:

1.为新进程在进程表中分配一个空项

2.为子进程赋一个唯一的进程标识符

3.做一个父进程的逻辑副本,不包括共享内存区

4.增加父进程拥有的所有文件的计数器,以表示有一个另外的进程现在也拥有这些文件

5.把子进程置为就绪态

6.想父进程返回子进程的进程号;对子进程返回0。可以通过获取返回值,判断返回值来让父子进程做不同的事情

2.获取进程PID

getpid:获取当前进程的PID

getppid:获取当前进程的父进程PID

3.fork优化:写时拷贝(Copy-On-Write)

传统的fork()系统调用直接把所有的资源复制给新创建的进程,拷贝的数据并不共享,如果新进程打算立即执行一个新的映像,所有的拷贝都将前功尽弃,造成资源浪费。Linux的fork()使用写时拷贝页实现,优化了这一问题。核心逻辑如下:

1.fork()执行时,内核并不复制父进程的整个进程地址空间,而是让父子进程以只读方式共享同一个拷贝,即共享同一地址空间及页表。

2.仅当父子进程中任一进程对共享内存进行写入或修改操作时,才为该进程分配独立内存空间,复制对应数据页并更新页表,确保各进程拥有独立的可写空间。

写时拷贝是一种++可以推迟甚至免除拷贝数据++的技术。

优化效果:这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行,在页根本不会被写入的情况下,它们就无需被复制。使得fork()的实际开销只是复制父进程的页表以及给子进程创建唯一的进程描述符,而在一般情况下,进程创建后都会马上运行一个可执行文件,这种优化可以避免拷贝大量根本就不会被使用的数据,大幅提升了进程创建效率。

补充:实模式、保护模式

实模式:早期CPU的内存运行模式,仅能访问物理内存,没有虚拟地址、内存保护机制,所有程序共享同一地址空间,安全性低,寻址范围有限。

保护模式:现代CPU的主流运行模式,支持虚拟内存、内存保护、多任务调度,通过MMU实现地址转换,隔离不同进程的内存空间,提升系统安全性和稳定性。

二、内存管理机制

1.内存分区管理

将主存划分为若干分区,为进程分区连续的内存空间,分为固定分区和动态分区两类。

1.1 固定分区

将主存划分为若干大小固定的分区,分区大小可分为"大小相等"和"大小不等"两种模式,进程被分配到满足大小需求的分区中。

难点和缺点

1.适配性差。程序太大而不能放到一个分区中。

2.主存的利用率低。会产生内部碎片------装入的数据块小于分区大小,导致分区内部存在无法利用的空间,且内部碎片无法通过整理消除。

1.2 动态分区

分区长度和数目可变。当进程被装入主存时,给它分配与所需空间相等的存储空间。

问题和解决方案

问题:会产生外部碎片(在所有分区外的存储空间会变成越来越多的碎片)

解决方案:压缩技术------当CPU空闲时,移动主存中的进程,使所有进程占用连续空间,空闲碎片合并为一块连续区域;但压缩过程耗时久,会浪费处理器时间。

1.3 放置算法

分为最佳适配、首次适配、邻近适配三种,三种算法都是在主存中选择等于或大于该进程的空闲块

最佳适配:选择与要求的大小最接近的块

首次适配:从开始扫描,选择大小足够的第一个可用块

邻近适配:从上一次放置的位置开始扫描,选择下一个大小足够的可用块

实现地址转换的方法

采用存储器相对地址的程序使用动态运行时加载的方法进行数据地址加载。通常情况下,被加载进程中的所有存储器访问都相对于程序的开始点。因此,在执行包括这类访问的指令时,需要一个硬件机制把相对地址转换为物理主存地址

一个特殊的寄存器在进程处于运行态时,连同该进程在主存中的起始地址一起装入寄存器。

一个界限寄存器表示程序的终止位置。

当程序被装入内存或当该进程的映像被换入时,必须设置这些值

在进程的执行过程中会遇到相对地址,包括指令寄存器的内容、转跳或调用指令中的指令地址已经加载和存储指令中的数据地址。

每个这样的相对地址都经过处理器的两步操作:

1.基址寄存器的值加上相对地址产生一个绝对地址

2.得到的绝对地址与界限寄存器的值相比较,如果这个地址在界限范围内,就可以继续该指令的执行,否则,产生一个中断,操作系统必须以某种方式对错误做出响应

保护:每个进程映像根据基址和界限寄存器的内容被隔离开,以免受其他进程不必要的访问

2.分页管理

为解决分区管理的碎片问题,分页管理将主存和进程均划分为大小相等的"帧"(主存单位)和"页"(进程单位),实现++非连续++内存分配。

原理

1.主存被划分为许多大小相等且很小的帧(常见大小为4K),每个进程也被分成同样大小的页

进程中的页可以指定到内存中的帧。

2.操作系统要为每个进程维护一个页表,页表给出了该进程的每一页对应的帧的位置,实现页与帧的映射。

3.逻辑地址结构:由"页号+偏移量"组成,无需直接指定物理地址,通过页表即可转换为实际物理地址。

优化

若页大小过小,进程所需页数量多,页表会占用大量内存;多级页表通过分层管理页号,减少页表占用的内存空间,仅需加载当前使用的页表项,提升内存利用率。

较小的进程需要较少的页,较大的进程需要较多的页,所以也不是越小越好,页越小,所需的页表就越多,页越大,所需的页表就越大。

3.分段管理

分段管理按程序的逻辑结构(如代码段、数据段、堆栈段)将进程划分为若干大小不等的"",每个段对应独立的逻辑单元。

1.逻辑地址结构:由两部分组成:段号和偏移量,段号对应进程的某一逻辑段,偏移量为段内地址。

2.段表管理:每个进程拥有一张段表,段表项记录对应段的主存起始地址和段长度,确保访问地址在段范围内,避免无效地址访问。

特点:采用大小不等的段,逻辑地址和物理地址间不具有简单的对应关系。每个进程都有一个段表,主存的空闲块组织到一个列表中。

4.伙伴系统

一种基于二分法的内存分配与回收机制,核心数据结构为二叉树,适用于对内存块进行高效分配和合并。

核心逻辑是:将内存块按2的幂次方大小划分,分配时找到最小的满足需求的块,回收时将相邻的同大小空闲块(伙伴块)合并为更大的块,减少碎片产生。

例子

三、地址重定位和保护

1.地址类型

逻辑地址:与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存的访问之前必须把它转换成物理地址

相对地址:逻辑地址的一个特例,是相对于某些已知点(通常是程序的开始处)的存储单元,常见于程序编译链接阶段

物理地址:数据在主存中的实际位置,是CPU直接访问的地址

虚拟地址:又称为逻辑地址,在虚拟内存机制中,由页表和偏移量组成,通过MMU内存管理单元转换为物理地址

线性地址:介于虚拟地址和物理地址之间的地址,需要经过MMU内存管理单元进一步转换为物理地址

不同进程,逻辑地址相同,对应的物理地址不一定相同;同一进程,逻辑地址相同,物理地址肯定相同。

2.重定位机制

重定位用于解决进程装入内存后,地址不匹配导致的运行错误,分为静态重定位和动态重定位:

1.静态重定位 (固定内存):进程首次加载时,通过重定位加载器将代码中的相对地址转换为决定物理地址,进程运行期间地址固定,换出后重新装入仍使用原分区。

2.动态重定位 (动态分区/分页):进程运行期间,通过硬件机制实时转换地址,核心依赖两个寄存器------基址寄存器 (存储进程在主存的起始地址)、界限寄存器(存储进程的终止地址,用于判断地址有效性)

3.地址转换与保护

进程执行时遇到相对地址(指令地址、数据地址等),处理器将基址寄存器的值与相对地址相加,得到绝对物理地址。

对比绝对地址与界限寄存器的值,若地址在合法范围内,继续执行指令;若超出范围,触发中断,由操作系统处理地址错误

保护作用:通过基址和界限寄存器,隔离不同进程的内存空间,防止进程非法访问其他进程的地址区域

四、虚拟内存

虚拟内存是一种内存管理技术,将主存视为磁盘地址空间的高速缓存,仅加载进程的活动区域,通过磁盘与主存之间的页面交换,实现"运行比主存大的进程"的能力。

虚拟内存提供了三个重要能力:

1.他将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,根据需要在磁盘和主存之间来回传送数据,使得能够运行比内存大得多的进程

2.它位每个进程提供了一致的地址空间,从而简化了存储器管理

3.它保护每个进程的地址空间不被其他进程破坏

32位系统虚拟地址空间分布

32位系统的虚拟地址空间寻址范围由硬件决定,32位地址总线的寻址能力为2^32=4GB,因此32位系统都有一个0~4G的虚拟地址空间:

高地址1GB:由内核独占使用,存储内核代码、内核数据、页表等内核态相关资源,所有进程共享这部分内核虚拟空间

低地址3GB:为用户态地址空间,每个进程用异独立的一份,存储进程的代码段、数据段、堆栈段等用户态资源,进程间相互间隔

内存管理单元(MMU)

硬件组件,负责将虚拟地址转换为物理地址,是虚拟内存实现的核心

页表项扩展

虚拟内存的页表项增加了两个标志位:

1.P位(存在位):表示它所对应的页当前是否在主存中

2.M位(修改位):表示相应页的内容从上一次装入内存中到现在是否已经修改。如果没有改变,则当需要把该页换出时,不需要用帧中的内容更新该页。

交换分区:磁盘上预留的空间,用于存储主存中暂时不使用的页面,是虚拟内存与磁盘交互的载体。

系统抖动

系统抖动是虚拟内存的典序问题,指操作系统频繁进行页面交换,导致处理器的大部分时间都用于交换块,而不是执行指令,严重影响系统性能。

产生原因:当内存不足时,操作系统在读取一块进程块时,必须把另一块扔出,如果扔出的块正好将要被使用,操作系统又不得不把它取回来。

参考书籍:《操作系统------精髓与设计原理》

相关推荐
楼田莉子2 小时前
Linux进程间通信——System V系列
linux·服务器·c++·学习·信息与通信
ONLYOFFICE2 小时前
ONLYOFFICE 桌面编辑器正式成为 ShaniOS 默认办公套件
linux·编辑器·github·onlyoffice
Percep_gan2 小时前
禁用Linux默认端口监听IPv6地址,修改为监听IPv4
linux·运维·服务器
321.。2 小时前
从 0 到 1 实现 Linux 下的线程安全阻塞队列:基于 RAII 与条件变量
linux·开发语言·c++·学习·中间件
济6172 小时前
linux 系统移植(第二十二期)---- 初步测试BusyBox构建的根文件系统---- Ubuntu20.04
linux·运维·服务器
CheungChunChiu2 小时前
视频编解码与 GOP 结构详解
linux·视频编解码
小舞O_o2 小时前
CondaError: Run ‘conda init‘ before ‘conda activate‘
linux·python·conda
RisunJan2 小时前
Linux命令-lha(压缩或解压缩lzh格式文件)
linux·运维·服务器
范纹杉想快点毕业2 小时前
嵌入式通信协议深度解析:从SPI/I2C到CAN总线的完整实现指南嵌入式工程师的炼成之路:从校园到实战的跨越
linux·运维·服务器·数据库·算法