Linux内存管理机制和虚拟内存技术

Linux内存管理机制和虚拟内存技术

  • 1. 物理内存
    • 1.1 物理内存架构
      • 1.1.1 UMA内存架构
      • 1.1.2 NUMA内存架构
    • 1.2 分页机制
    • 1.3 物理内存模型
      • 1.3.1 FLATMEM 平坦内存模型
      • 1.3.2 DISCONTIGMEM 非连续内存模型
      • 1.3.3 SPARSEMEM 稀疏内存模型
  • 2. 虚拟内存
    • 2.1 分页机制
    • 2.2 多级页表
    • 2.3 内核态和用户态
      • 2.3.1 内核态和用户态的分布
      • 2.3.2 内核态和用户态的切换
    • 2.3. 写时复制
  • 3.常用的内存分析命令
    • 3.1.常用命令
      • 3.1.1 vmstat
      • 3.1.2 top
      • 3.1.3 pmap
    • 3.2 buffer/cache的区别
  • 4. 疑问和思考
    • 4.1 为什么需要虚拟内存?
    • 4.2 为什么要内核态和用户态?
  • 5. 参考文档

本文主要是研究Linux内存管理机制和虚拟内存技术,同时比较Windows NT和Linux在实现时的异同。主要为操作系统底层的实现原理的分析,花费大量篇幅描述虚拟内存技术的模型,这是理解Windows和Linux内存机制实现的基础,仅仅是对技术的介绍,提供概念和理论实现上的认知,如果对具体的实现和源码感兴趣可以自行上网查阅源码或者这方面的文章。


linux在内存管理上分为物理内存和虚拟内存

  • 物理内存:Linux操作系统使用物理内存来存储程序和数据。物理内存是计算机系统中的硬件组件,用于存储正在运行的程序和数据。Linux系统通过内存管理机制来管理物理内存。这些机制包括内存分页、虚拟内存和页面置换等。在Linux系统中,物理内存被划分为多个页面,每个页面的大小通常为4KB或者更大。操作系统将程序和数据存储在这些页面中,并为每个页面分配一个唯一的物理地址。
  • 虚拟内存:Linux系统使用虚拟内存机制,将物理内存划分为多个虚拟内存页面,每个页面与一个独立的虚拟地址相关联。这允许操作系统将物理内存中的页面映射到不同的进程的虚拟地址空间中,从而提供了内存隔离和保护的功能。

这两种内存,应该分别从不同的视角进行关注。

  • 操作系统: 更多关注物理内存的使用情况,并管理物理内存和虚拟内存的映射关系表。
  • 应用层面:应用层面通常使用的内存都是指虚拟内存,通过虚拟内存的方式,对于每个应用程序来说,看到的内存都是一样的,能够形成统一的标准,方便开发和管理。

1. 物理内存

1.1 物理内存架构

在当前的计算机、嵌入式系统中,以内存为研究对象可以分成两种架构

  • 一种是UMA(Uniform Memory Access,统一内存访问)架构
  • 一种是NUMA(Non-Uniform Memory Access,非统一内存访问)架构。

1.1.1 UMA内存架构

在这里插入图片描述
内存可以被其他模块统一寻址,有统一的结构。目前,大部分嵌入式系统及计算机系统都采用UMA架构。如上图所示,是一个UMA架构的系统,有两个cpu位于同一个cluster中,cpu分别有自己的L1D、L1I cache及L2 cache。两个cpu共享L3 cache,通过系统总线可以访问物理内存DDR,SRAM、SSD等模块,并且两个CPU对物理内存的访问消耗是一样的。这种访问模式的处理器被成为SMP(Aymmetric Multiprocessing,对称多处理器)

这种结构的CPU 是通过一条通用总线连接到北桥,北桥中的内存控制器链接着内存。这种设计中,瓶颈马上出现了。第一个瓶颈与设备对RAM 的访问有关。早期,所有设备之间的通信都需要经过 CPU,结果严重影响了整个系统的性能。为了解决这个问题,有些设备加入了直接内存访问(DMA)的能力。DMA 允许设备在北桥的帮助下,无需 CPU 的干涉,直接读写 RAM。到了今天,所有高性能的设备都可以使用 DMA。虽然 DMA 大大降低了 CPU 的负担,却占用了北桥的带宽,与 CPU 形成了争用。所以现在很少使用了。

1.1.2 NUMA内存架构

在这里插入图片描述
系统中有多个内存节点和多个cpu cluster,CPU访问本地内存节点的时间开销最小,访问远端的内存节点的时间开销要大。如上图所示,是一个NUMA架构的系统,其中cpu0、cpu1在cluster0中,与相应的L1I/L1D cache、L2 cache、L3 cache及DDR组成node0节点。同样的,CPU2、CPU3在cluster1中,与相应的L1I/L1D cache、L2 cache、L3 cache及DDR组成node1节点。两个node节点,通过UPI(Ultra Path Interconnect,超路径互联)总线连接。CPU0可以通过这个UPI访问远端node1上的物理内存,但是要比本地node0的内存访问慢得多。

1.2 分页机制

内核会将整个物理内存空间划分为一页一页大小相同的的内存块,称为一个物理内存页,一个物理内存页内部内存时连续的。

一页大小的内存块在内核中用 struct page 结构体来进行管理,struct page 中封装了每页内存块的状态信息,比如:组织结构,使用信息,统计信息,以及与其他内核结构的关联映射信息等。

  • 内核会为每个物理内存页 page 进行统一编号
  • 这个编号称之为 PFN(Page Frame Number),PFN 与 struct page 是一一对应的关系并且全局唯一
  • 然后内核会将划分出来的这些一页一页的内存块统一组织在一个全局数组 mem_map 中管理
  • 后续虚拟内存与物理内存的映射以及调度均是以页为单位进行的。

相关架构如下
在这里插入图片描述

因此,Linux采用了分页(paging)的方式来记录对应关系。所谓的分页,就是以更大尺寸的单位页(page)来管理内存。在Linux中,通常每页大小为4KB。如果想要获取当前树莓派的内存页大小,可以使用命令:

$getconf PAGE_SIZE

得到结果,即内存分页的字节数:

4096

返回的4096代表每个内存页可以存放4096个字节,即4KB。Linux把物理内存和进程空间都分割成页。

1.3 物理内存模型

1.3.1 FLATMEM 平坦内存模型

我们先把物理内存想象成一片地址连续的存储空间,在这一大片地址连续的内存空间中,内核将这块内存空间分为一页一页的内存块 struct page 。

由于这块物理内存是连续的,物理地址也是连续的,划分出来的这一页一页的物理页必然也是连续的,并且每页的大小都是固定的,所以我们很容易想到用一个数组来组织这些连续的物理内存页 struct page 结构,其在数组中对应的下标即为 PFN 。这种内存模型就叫做平坦内存模型 FLATMEM 。

在这里插入图片描述
内核中使用了一个 mem_map 的全局数组用来组织所有划分出来的物理内存页。mem_map 全局数组的下标就是相应物理页对应的 PFN 。

Linux 早期使用的就是这种内存模型,因为在 Linux 发展的早期所需要管理的物理内存通常不大(比如几十 MB),那时的 Linux 使用平坦内存模型 FLATMEM 来管理物理内存就足够高效了。

1.3.2 DISCONTIGMEM 非连续内存模型

FLATMEM 平坦内存模型只适合管理一整块连续的物理内存,而对于多块非连续的物理内存来说使用 FLATMEM 平坦内存模型进行管理则会造成很大的内存空间浪费。

因为 FLATMEM 平坦内存模型是利用 mem_map 这样一个全局数组来组织这些被划分出来的物理页 page 的,而对于物理内存存在大量不连续的内存地址区间这种情况时,这些不连续的内存地址区间就形成了内存空洞。

由于用于组织物理页的底层数据结构是 mem_map 数组,数组的特性又要求这些物理页是连续的,所以只能为这些内存地址空洞也分配 struct page 结构用来填充数组使其连续。

而每个 struct page 结构大部分情况下需要占用 40 字节(struct page 结构在不同场景下内存占用会有所不同,这一点我们后面再说),如果物理内存中存在的大块的地址空洞,那么为这些空洞而分配的 struct page 将会占用大量的内存空间,导致巨大的浪费。

在这里插入图片描述

为了组织和管理这些不连续的物理内存,内核于是引入了 DISCONTIGMEM 非连续内存模型,用来消除这些不连续的内存地址空洞对 mem_map 的空间浪费。

在 DISCONTIGMEM 非连续内存模型中,内核将物理内存从宏观上划分成了一个一个的节点 node (微观上还是一页一页的物理页),每个 node 节点管理一块连续的物理内存。这样一来这些连续的物理内存页均被划归到了对应的 node 节点中管理,就避免了内存空洞造成的空间浪费。
在这里插入图片描述

内核中使用 struct pglist_data 表示用于管理连续物理内存的 node 节点(内核假设 node 中的物理内存是连续的),既然每个 node 节点中的物理内存是连续的,于是在每个 node 节点中还是采用 FLATMEM 平坦内存模型的方式来组织管理物理内存页。

我们可以看出 DISCONTIGMEM 非连续内存模型其实就是 FLATMEM 平坦内存模型的一种扩展,在面对大块不连续的物理内存管理时,通过将每段连续的物理内存区间划归到 node 节点中进行管理,避免了为内存地址空洞分配 struct page 结构,从而节省了内存资源的开销。

1.3.3 SPARSEMEM 稀疏内存模型

随着内存技术的发展,内核可以支持物理内存的热插拔了(后面我会介绍),这样一来物理内存的不连续就变为常态了,在上小节介绍的 DISCONTIGMEM 内存模型中,其实每个 node 中的物理内存也不一定都是连续的。

而且每个 node 中都有一套完整的内存管理系统,如果 node 数目多的话,那这个开销就大了,于是就有了对连续物理内存更细粒度的管理需求,为了能够更灵活地管理粒度更小的连续物理内存,SPARSEMEM 稀疏内存模型就此登场了。

SPARSEMEM 稀疏内存模型的核心思想就是对粒度更小的连续内存块进行精细的管理,用于管理连续内存块的单元被称作 section 。物理页大小为 4k 的情况下, section 的大小为 128M ,物理页大小为 16k 的情况下, section 的大小为 512M。

由于 section 被用作管理小粒度的连续内存块,这些小的连续物理内存在 section 中也是通过数组的方式被组织管理,每个 struct mem_section 结构体中有一个 section_mem_map 指针用于指向 section 中管理连续内存的 page 数组。

SPARSEMEM 内存模型中的这些所有的 mem_section 会被存放在一个全局的数组中,并且每个 mem_section 都可以在系统运行时改变 offline / online (下线 / 上线)状态,以便支持内存的热插拔(hotplug)功能。

在这里插入图片描述
SPARSEMEM 稀疏内存模型已经完全覆盖了前两个内存模型的所有功能,因此稀疏内存模型可被用于所有内存布局的情况。

2. 虚拟内存

2.1 分页机制

在Linux系统中,虚拟内存分页是一种将物理内存和磁盘空间结合使用的技术。它通过将进程的内存空间划分为固定大小的页(一般为4KB),并将这些页映射到物理内存或磁盘上。当进程访问一个未在物理内存中的页时,操作系统会将该页从磁盘加载到物理内存中,以满足进程的需求。

虚拟内存只是一个逻辑概念,本质上并不存在,通过获取页表获取相对的物理内存页,从而拼凑聚合出虚拟内存的数据。因此虚拟内存的分页和物理内存的分页机制是一致的。

2.2 多级页表

内存的一项主要任务,就是存储进程的相关数据。我们之前已经看到过进程空间的程序段、全局数据、栈和堆,以及这些这些存储结构在进程运行中所起到的关键作用。有趣的是,尽管进程和内存的关系如此紧密,但进程并不能直接访问内存。在Linux下,进程不能直接读写内存中地址为0x1位置的数据。进程中能访问的地址,只能是虚拟内存地址(virtual memory address)。操作系统会把虚拟内存地址翻译成真实的内存地址。这种内存管理方式,称为虚拟内存(virtual memory)。

每个进程都有自己的一套虚拟内存地址,用来给自己的进程空间编号。进程空间的数据同样以字节为单位,依次增加。从功能上说,虚拟内存地址和物理内存地址类似,都是为数据提供位置索引。进程的虚拟内存地址相互独立。因此,两个进程空间可以有相同的虚拟内存地址,如0x10001000。虚拟内存地址和物理内存地址又有一定的对应关系,如图1所示。对进程某个虚拟内存地址的操作,会被CPU翻译成对某个具体内存地址的操作。

在这里插入图片描述
应用程序来说对物理内存地址一无所知。它只可能通过虚拟内存地址来进行数据读写。程序中表达的内存地址,也都是虚拟内存地址。进程对虚拟内存地址的操作,会被操作系统翻译成对某个物理内存地址的操作。由于翻译的过程由操作系统全权负责,所以应用程序可以在全过程中对物理内存地址一无所知。

本质上说,虚拟内存地址剥夺了应用程序自由访问物理内存地址的权利。进程对物理内存的访问,必须经过操作系统的审查。因此,掌握着内存对应关系的操作系统,也掌握了应用程序访问内存的闸门。借助虚拟内存地址,操作系统可以保障进程空间的独立性。只要操作系统把两个进程的进程空间对应到不同的内存区域,就让两个进程空间成为“老死不相往来”的两个小王国。两个进程就不可能相互篡改对方的数据,进程出错的可能性就大为减少。

另一方面,有了虚拟内存地址,内存共享也变得简单。操作系统可以把同一物理内存区域对应到多个进程空间。这样,不需要任何的数据复制,多个进程就可以看到相同的数据。内核和共享库的映射,就是通过这种方式进行的。每个进程空间中,最初一部分的虚拟内存地址,都对应到物理内存中预留给内核的空间。这样,所有的进程就可以共享同一套内核数据。共享库的情况也是类似。对于任何一个共享库,计算机只需要往物理内存中加载一次,就可以通过操纵对应关系,来让多个进程共同使用。IPO中的共享内存,也有赖于虚拟内存地址。

在这里插入图片描述

操作系统维护每个进程使用的虚拟内存(包括虚拟内存页)和 物理内存(包括物理内存页)的映射关系, 俗称页表

  • Linux使用页表来管理进程的虚拟地址空间。页表是一种数据结构,它将进程的虚拟地址映射到物理内存地址。
  • Linux使用分页机制来将进程的虚拟地址空间划分为固定大小的页面。每个页面通常为4KB或者2MB大小。物理内存也被划分为相同大小的页帧。
  • 页表是一个层次化的结构,由多个表项组成。每个表项存储虚拟地址到物理地址的映射关系。Linux使用两级页表结构,即页目录和页表。页目录存储页表的基地址,而页表存储实际的虚拟地址到物理地址的映射。通过多级页表结构,Linux可以有效地管理大型的虚拟地址空间。
  • 当进程访问虚拟地址时,Linux会根据页表将虚拟地址转换为物理地址。如果该页尚未在物理内存中,则会发生缺页错误,操作系统会将缺失的页面从磁盘加载到物理内存中
  • 通过使用页表,Linux可以实现进程间的地址隔离和虚拟内存管理。每个进程都有自己独立的页表,使得进程无法访问其他进程的内存。此外,Linux可以使用交换空间来将不活跃的页面移到磁盘上,从而释放物理内存供其他活跃进程使用。

总之,Linux的页表是用于管理进程的虚拟地址空间的重要数据结构,它负责将虚拟地址映射到物理地址,并实现地址隔离和虚拟内存管理。

页表的示意图参考如下
在这里插入图片描述

2.3 内核态和用户态

2.3.1 内核态和用户态的分布

在内存资源上的使用,操作系统对用户态内核态也做了限制,每个进程创建都会分配「虚拟空间地址」(不懂可以参考我的另一篇文章“15分钟!一文帮小白搞懂操作系统之内存”),以Linux32位操作系统为例,它的寻址空间范围是 4G(2的32次方),而操作系统会把虚拟控制地址划分为两部分。

  • 一部分为内核空间:高位的 1G(从虚拟地址 0xC0000000 到 0xFFFFFFFF)由内核使用
  • 另一部分为用户空间:低位的 3G(从虚拟地址 0x00000000 到 0xBFFFFFFF)由各个进程使用。
    在这里插入图片描述

2.3.2 内核态和用户态的切换

什么情况会导致用户态到内核态切换

  • 系统调用:用户态进程主动切换到内核态的方式,用户态进程通过系统调用向操作系统申请资源完成工作,例如 fork()就是一个创建新进程的系统调用,系统调用的机制核心使用了操作系统为用户特别开放的一个中断来实现,如Linux 的 int 80h 中断,也可以称为软中断
  • 异常:当 C P U 在执行用户态的进程时,发生了一些没有预知的异常,这时当前运行进程会切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常
  • 中断:当 C P U 在执行用户态的进程时,外围设备完成用户请求的操作后,会向 C P U 发出相应的中断信号,这时 C P U 会暂停执行下一条即将要执行的指令,转到与中断信号对应的处理程序去执行,也就是切换到了内核态。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。

相信大家都听过这样的话「用户态和内核态切换的开销大」,但是它的开销大在那里呢?简单点来说有下面几点

  • 保留用户态现场(上下文、寄存器、用户栈等)
  • 复制用户态参数,用户栈切到内核栈,进入内核态
  • 额外的检查(因为内核代码对用户不信任)
  • 执行内核态代码
  • 复制内核态代码执行结果,回到用户态
  • 恢复用户态现场(上下文、寄存器、用户栈等)

在这里插入图片描述

2.3. 写时复制

分页加载机制就是linux把内存划分成很多的小块,进程中的数据按照某种对应关系(页表)放入内存中。这么做的目的是为了提高内存的使用率。可以不必让一个进程中的所有东西都连着放在一起。

连着放在一起的弊端是:如果A进程释放后,假如A进程使用的内存非常少。其他进程都无法使用A进程占的这块内存,因为它太小了放不下。这样就存在内存利用率低下,每过段时间都要检测这种进程间的碎片空间。因此才有了分页加载,每个被划分的小块非常小,进程里的数据将被分割开来放进去,哪怕到时候释放了,下个进程可以通样将自己分割成小块放入刚被释放的内存中。

在这里插入图片描述
学习过fork我们都知道是父进程创建出一个子进程,子进程作为父进程的副本, 是父进程的拷贝。

可是每次fork出的子进程难道还要把父进程的各种数据拷贝一份?有人会说不是父子进程不共享各种数据段吗?如全局变量区 ,栈区 , 堆区 。如果不拷贝那不就成共享的吗?其实有关子进程拷贝父进程的数据是这样的。
如果子进程只是对父进程的数据进行读取操作,那么子进程用的就是父进程的数据。如果子进程需要对某数据进行修改,那么在修改前,子进程才会拷贝出需要修改的这份数据,对这份备份进行修改。这就满足了父子进程的数据相互独立,互不影响的要求。这么做的初衷也是为了节省内存。

举个栗子如果一份代码中,定义了10个数据。父进程执行的部分对这10个数据全部进行修改,而子进程执行的部分只修改了一个数据,子进程明明用不到其他9个数据,那还何必让子进程拷贝全部数据,多占用9个永远使用不到的数据内存?

因此创建子进程只是将原父进程的pcb拷贝了一份。父子进程的pcb全部指向的是父进程原本就有的数据,如果子进程里对数据进行了修改,那么子进程的pcb里指向 被修改的数据的指针会指向一个自己新开辟的内存,新开辟的内存里将父进程的数据拷贝过来,然后再进行修改。这就是写时拷贝技术,顾名思义,只在写的时候才拷贝的技术。厉害厉害

在这里插入图片描述

3.常用的内存分析命令

3.1.常用命令

3.1.1 vmstat

在这里插入图片描述
这里我们主要关注swap值:
si: 交换内存使用,由磁盘调入内存;so: 交换内存使用,由内存调入磁盘。内存够用的时候,这2个值都是0,如果这2个值长期大0时,系统性能会受到影响。磁盘IO和CPU资源都会被消耗。当看到空闲内存(free)很少或接近于0时,si,so也很少(大多时候是0),那么不用担心,系统性能这时不会受到影响的。

在这里插入图片描述

3.1.2 top

在这里插入图片描述
在这里插入图片描述

DATA的含义比较确定,甚至可以用程序读取的数据量计算出来;SHR是一个潜在的可能会被共享的数字,如果只开一个程序,就没有其他进程共同使用;VIRT里面的可能性更多,比如它可能计算了被许多X的库所共享的内存;RES应该是比较准确的,但不含有交换出去的空间;但基本可以说RES是程序当前使用的内存量。

通过top –p pid方式可查看单一进程的情况,在显示界面按f,再选择要查看的内容,选择code、data和swap,显示如下:
在这里插入图片描述

3.1.3 pmap

pmap –d pid 分析zone_server
在这里插入图片描述
在这里插入图片描述
pmap 的输出中,writeable/private: 1.02G,这个就是zone_server 这个程序真正占用的物理内存,不包含shared libraries 。在这里,它只有1.02G,而ps的结果为2.1G。

linux 会把一些shared libraries 载入到内存中,这些shared libraries会被很多process load 到自己的运行环境中,同时,ps 输出的RSS 结果中,每个process 都包含了这些shared libraries,而事实上它只被load 了一次,如果单纯把ps 的结果相加,这样就重复计算了。

3.2 buffer/cache的区别

在Linux中,buffer和cache是两种用来提高文件系统性能的机制。

  • Buffer(缓冲区):Buffer是用来存放文件系统读取或写入的数据的一块内存区域。当读取文件时,操作系统会将文件的数据先读取到Buffer中,然后再从Buffer中读取数据给应用程序。当写入文件时,操作系统会将数据先写入到Buffer中,然后再将Buffer中的数据写入磁盘。Buffer的主要作用是减少磁盘IO操作的次数,提高文件读写的效率
  • Cache(缓存):Cache是用来存放磁盘中的数据的一块内存区域。当读取文件时,操作系统会将文件的数据读取到Cache中。当再次读取相同的文件时,操作系统会直接从Cache中读取数据,而不需要再次读取磁盘。Cache的主要作用是减少磁盘IO操作的时间延迟,提高文件读取的速度

区别:

  • 作用对象不同:Buffer主要用于文件系统的读写操作,而Cache主要用于加速文件的读取操作。
  • 内存使用方式不同:Buffer是用来存放应用程序读写的数据,而Cache是用来存放磁盘中的数据。
  • 数据一致性不同:Buffer中的数据在写入磁盘之前可能会进行修改,而Cache中的数据是磁盘上数据的一个快照,不会被修改。
  • 使用方式不同:应用程序可以主动操作Buffer的数据,但无法直接操作Cache的数据。

总的来说,Buffer主要用于提高文件系统写入的IO效率,而Cache主要用于提高文件的读取速度

4. 疑问和思考

4.1 为什么需要虚拟内存?

Linux需要设计虚拟内存的原因有以下几点:

  • 提供更大的内存空间:虚拟内存使得每个进程都有一个独立的地址空间,使得每个进程都能够拥有更大的内存空间。实际上,每个进程可以访问的内存大小远远超过了实际物理内存的大小。
  • 提供更好的内存管理:虚拟内存可以将内存空间划分成固定大小的页面(page),并通过页面表(page table)将虚拟页面映射到物理内存上。这样,操作系统可以更加灵活地管理内存,包括分配、回收和共享等操作。
  • 实现内存保护:虚拟内存使得每个进程都有独立的内存空间,从而实现内存保护。操作系统可以通过页面表来限制进程对内存的访问,防止进程越界访问或者非法操作。
  • 实现页面置换:如果物理内存不足,操作系统可以通过页面置换算法将不常用的页面从物理内存中换出到磁盘上,从而腾出空间供其他进程使用。
  • 实现共享内存:虚拟内存使得多个进程可以共享同一块物理内存,从而实现进程间的通信和数据共享,提高系统的效率和资源利用率。

其中内存保护是最核心的。

让我们先来看下,如果在程序中直接使用物理内存地址会发生什么情况?

假设现在没有虚拟内存地址,我们在程序中对内存的操作全都都是使用物理内存地址,在这种情况下,程序员就需要精确的知道每一个变量在内存中的具体位置,我们需要手动对物理内存进行布局,明确哪些数据存储在内存的哪些位置,除此之外我们还需要考虑为每个进程究竟要分配多少内存?内存紧张的时候该怎么办?如何避免进程与进程之间的地址冲突?等等一系列复杂且琐碎的细节。

如果我们在单进程系统中比如嵌入式设备上开发应用程序,系统中只有一个进程,这单个进程独享所有的物理资源包括内存资源。在这种情况下,上述提到的这些直接使用物理内存的问题可能还好处理一些,但是仍然具有很高的开发门槛。

然而在现代操作系统中往往支持多个进程,需要处理多进程之间的协同问题,在多进程系统中直接使用物理内存地址操作内存所带来的上述问题就变得非常复杂了。

而虚拟内存的引入正是要解决上述的问题,虚拟内存引入之后,进程的视角就会变得非常开阔,每个进程都拥有自己独立的虚拟地址空间,进程与进程之间的虚拟内存地址空间是相互隔离,互不干扰的。每个进程都认为自己独占所有内存空间,自己想干什么就干什么。

系统上还运行了哪些进程和我没有任何关系。这样一来我们就可以将多进程之间协同的相关复杂细节统统交给内核中的内存管理模块来处理,极大地解放了程序员的心智负担。这一切都是因为虚拟内存能够提供内存地址空间的隔离,极大地扩展了可用空间。

4.2 为什么要内核态和用户态?

Linux操作系统之所以引入内核态和用户态,是为了实现操作系统的安全性和稳定性。

  • 首先,内核态和用户态的划分可以实现操作系统的安全性。在内核态下,操作系统可以直接访问和控制硬件资源,拥有更高的特权级别,可以执行敏感操作,如更改硬件配置、管理进程和内存等。而用户态下的应用程序只能执行受限的操作,不能直接访问和控制硬件资源。这样可以防止用户态的应用程序意外或恶意地对系统进行破坏,保护操作系统的稳定性。
  • 其次,内核态和用户态的划分可以提高操作系统的稳定性。在内核态下,操作系统可以快速响应硬件的中断请求,处理各种异常情况,确保系统的正常运行。而用户态下的应用程序则不具备这样的能力,只能通过系统调用进入内核态来请求操作系统提供服务。这样可以防止用户态的应用程序直接操作硬件或执行危险操作,降低系统崩溃的风险,提高系统的稳定性。

因此,Linux引入内核态和用户态的划分是为了在安全性和稳定性方面达到一个平衡,保护操作系统和硬件资源的安全,同时提供稳定可靠的运行环境。

5. 参考文档

  • Linux写实拷贝技术
  • 为什么要有虚拟内存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/505871.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Image Fusion via Vision-Language Model【文献阅读】

阅读目录 文献阅读AbstractIntroduction3. Method3.1. Problem Overview3.2. Fusion via Vision-Language Model 4. Vision-Language Fusion Datasets5. Experiment5.1Infrared and Visible Image Fusion 6. Conclusion个人总结 文献阅读 原文下载:https://arxiv.or…

Qt开发 按钮类控件

Qt开发 按钮类控件 Push Button 使用 QPushButton 表示一个按钮。 QPushButton 继承自 QAbstractButton 。这个类是一个抽象类,是其他按钮的父类。 在 Qt Designer 中也能够看到这里的继承关系 QAbstractButton 中,和 QPushButton 相关性较大的属性 …

cetos7 Docker 安装 gitlab

一、gitlab 简单介绍和安装要求 官方文档:https://docs.gitlab.cn/jh/install/docker.html 1.1、gitlab 介绍 gitLab 是一个用于代码仓库管理系统的开源项目,使用git作为代码管理工具,并在此基础上搭建起来的Web服务平台,通过该平…

从0开始预训练1.4b中文大模型实践

作者:Lil2J知乎(已授权) 链接:https://zhuanlan.zhihu.com/p/684946331 简介 这篇文章主要记录了我个人对1.4b中文大模型的实践复现过程。我选择了QWEN作为基座模型,并训练了一个参数量达到1.4b的预训练模型&#xff0…

C# Socket通信从入门到精通(21)——TCP发送文件与接收文件 C#代码实现

1、前言 我们在开发上位机软件的过程中经常需要发送文件,本文就是介绍如何利用tcp客户端发送文件、tcp服务器端接收文件,也就是所谓的文件传输,而且本文介绍的方法具备以下特点: 1)可配置发送的文件夹和接收的文件夹路径: 2)可自动发送指定文件夹下的所有子目录和文件;…

备战蓝桥杯---动态规划之悬线法

Em...属于一知道就会,不知道的话比较难想。 我们先看题: 我们不妨把1抽象成一个平面上的点,因此可以变成这一幅图: 我们假设每一个点被向上牵拉了一根线: 显然,每一条悬线都有可能成为边界限制&#xff0c…

青少年CTF擂台挑战赛 2024 #Round 1 Web方向题解 WP 全

EasyMD5 题目描述:php没有难题 考点总结:脑洞题目,不如我出(狗头 只允许两个都上传pdf文件。 文件还不能太大了。burp多次发包发现要求两个pdf内容不一样 不一样时候,提示我们MD5碰撞。 科学计数法绕过 PHP的后门 …

Jenkins的安装和helloworld Pipeline

文章目录 环境安装下载安装启动初始化 PipelineUISCM(Source Control Management)准备pipeline 参考 环境 RHEL 9.3Jenkins 2.44.0.1 安装 参考 https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos 。 下载安装 [ding192 ~]$ sudo …

Linux:kubernetes(k8s)搭建mater节点(kubeadm,kubectl,kubelet)(2)

安装k8有多种方式如: minikube kubeadm 二进制安装 命令行工具 我这里就使用kubeadm进行安装 环境 3台centos7 master ip :192.168.113.120 2G运存 2内核 node1 ip :192.168.113.121 2G运存 2内核 node2 ip :192.168.1…

ctf_show笔记篇(web入门---爆破)

爆破 21:直接bp抓包跑字典,需base64加密 22:可用工具跑也可用浏览器找还可以用网上做好的域名查找去找 23:此题需跑脚本已经附上自写脚本 最后跑出来六个答案一个一个尝试得到答案为3j import hashlibm "0123456789qwert…

适配器模式在微服务的巧妙应用

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间可以一起工作。适配器模式通常用于将一个类的接口转换成客户端期望的另一种接口,从而使原本因接口不兼容而不能一起工作的类可以一起工作。 适配器模式的…

Hololens 2应用开发系列(2)——MRTK基础知识及配置文件配置(上)

Hololens 2应用开发系列(2)——MRTK基础知识及配置文件配置 一、前言二、MRTK基础知识2.1 MRTK概述2.2 MRTK运行逻辑2.3 MRTK配置文件介绍2.4 MRTK服务 三、配置文件使用3.1 总配置文件3.2 相机配置3.3 其他配置 参考文献 一、前言 在前面的文章中&…