Linux是怎样工作的笔记
  WiynMBqvCSw1 2023年11月02日 83 0

内存管理

Linux通过内核中名为内存管理系统的功能来管理系统上搭载的所有内存。除了各种进程以外,内核本身也需要使用内存。

内存相关的统计信息

可以通过free命令获取系统搭载的内存总量和已消耗的内存量。

这里对Mem: 这一行中的重要字段进行说明。需要注意的是,上面的所有数值的单位都为千字节(KB)。

total字段:系统搭载的物理内存总量。在上面的例子中约为17GB

free字段:表面上的可用内存量

buff/cache字段:缓冲区缓存与页面缓存占用的内存。当系统的可用内存量(free字段的值)减少时,可通过内核将它们释放出来

available字段:实际的可用内存量。本字段的值为free字段的值加上当内存不足时内核中可释放的内存量。“可释放的内存”指缓冲区缓存与页面缓存中的大部分内存,以及内核中除此以外的用于其他地方的部分内存.

内存不足

随着内存使用量增加,可用内存变得越来越少。当内存不足时,内存管理系统将回收内核中可释放的内存。如果内存使用量继续增加,系统就会陷入做什么都缺乏足够的内存,以至于无法运行的内存不足(Out Of Memory,OOM)状态。在进入OOM状态后,内存管理系统会运行被称为 OOM killer 的可怕功能,该功能会选出合适的进程并将其强制终止(kill掉),以释放出更多内存。

如果是个人计算机,这可能并非什么大问题;但如果是商用服务器,则完全不知道结束的是哪一个进程,这种状态非常令人困扰。虽然有办法令特定进程排除在OOM killer的选择范围之外,但是要在业务用的进程中筛选出允许强制结束的进程是非常困难的。因此,也有将服务器上的sysctl的vm.panic_on_oom参数从默认的0(在发生OOM时运行OOM killer)变更为1(在发生OOM时强制关闭系统)这样的做法。

虚拟内存

简而言之,虚拟内存使进程无法直接访问系统上搭载的内存,取而代之的是通过虚拟地址间接访问。进程可以看见的是虚拟地址,系统上搭载的内存的实际地址称为物理地址。此外,可以通过地址访问的范围称为地址空间。进程无法直接访问真实的内存,也就是说不存在直接访问物理地址的方法。

页表

通过保存在内核使用的内存中的页表,可以完成从虚拟地址到物理地址的转换。在虚拟内存中,所有内存以页为单位划分并进行管理,地址转换也以页为单位进行。在页表中,一个页面对应的数据条目称为页表项。页表项记录着虚拟地址与物理地址的对应关系。页面大小取决于CPU架构。在x86_64架构中,页面大小为4KB。

如果进程访问0 ~ 300的虚拟地址,CPU将自动参考页表的内容,将其转换为对相应的物理地址的访问,而无须经过内核的处理。

如果进程访问地址300 ~ 500,即页表不存在的地址映射,则在CPU上会发生缺页中断。缺页中断可以中止正在执行的命令,并启动内核中的缺页中断机构的处理。内核的缺页中断机构检测到非法访问,向进程发送SIGSEGV信号。接收到该信号的进程通常会被强制结束运行。

为进程分配内存的流程

【1】首先读取程序的可执行文件,计算运行程序所需的内存大小为,假设计算结果为300

【2】在物理内存上划分出大小为300的区域,将其分配给进程,并把代码和数据复制过去。Linux的物理内存分配使用的是更复杂的请求分页方法。

【4】在复制完成后,创建进程的页表,并把虚拟地址映射到物理地址。

【5】最后,从指定的地址开始运行即可。

【6】如果进程请求更多内存,内核将为其分配新的内存,创建相应的页表,然后把与新分配的内存(的物理地址)对应的虚拟地址返回给进程。mmap() 函数会通过系统调用向Linux内核请求新的内存。

利用上层进行内存分配

C语言标准库中存在一个名为malloc() 的函数,用于获取内存。在Linux中,这个函数的底层调用了mmap() 函数。

mmap() 函数是以页为单位获取内存的,而malloc() 函数是以字节为单位获取内存的。为了以字节为单位获取内存,glibc事先通过系统调用mmap() 向内核请求一大块内存区域作为内存池,当程序调用malloc() 函数时,从内存池中根据申请的内存量划分出相应大小(以字节为单位)的内存并返回给程序。在内存池中的内存消耗完后,glibc会再次调用mmap() 以申请新的内存区域。

这是运行在用户模式下的OS功能(glibc的malloc() 函数)为普通程序提供的一个典型功能。

虚拟地址解决的问题

内存碎片化

如图所示,假如能巧妙地设定进程的页表,就能将物理内存上的碎片整合成虚拟地址空间上的一片连续的内存区域。这样一来,碎片化的问题也就解决了。

访问用于其他用途的内存区域

虚拟地址空间是每个进程独有的。相应地,页表也是每个进程独有的。如图所示,进程A和进程B各自拥有独立的虚拟地址空间。得益于虚拟内存,进程根本无法访问其他进程的内存。

虚拟内存的应用

●文件映射●请求分页●利用写时复制快速创建进程●Swap●多级页表●标准大页

文件映射

进程在访问文件时,通常会在打开文件后使用read()、write() 以及lseek() 等系统调用。此外,Linux还提供了将文件区域映射到虚拟地址空间的功能。按照指定方式调用mmap() 函数,即可将文件的内容读取到内存中,然后把这个内存区域映射到虚拟地址空间。

请求分页

【场景问题分析】

对于创建进程时的内存分配,或者在创建进程后通过mmap() 系统调用进行的动态内存分配,我们是这样描述它们的流程的。

【1】内核直接从物理内存中获取需要的区域。

【2】内核设置页表,并关联虚拟地址空间与物理地址空间。但是,这种分配方式会导致内存的浪费。因为在获取的内存中,有一部分内存在获取后,甚至直到进程运行结束都不会使用

例如:●用于大规模程序中的、程序运行时未使用的功能的代码段和数据段●由glibc保留的内存池中未被用户利用的部分

为了解决这个问题,Linux利用请求分页机制来为进程分配内存。在请求分页机制中,对于虚拟地址空间内的各个页面,只有在进程初次访问页面时,才会为这个页面分配物理内存。页面的状态除了前面提到过的“未分配给进程”与“已分配给进程且已分配物理内存”这两种以外,还存在“已分配给进程但尚未分配物理内存”这种状态。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论