我在考虑 Linux 内核是如何实现系统调用的,我想知道是否有人可以给我一个关于 sbrk/brk 工作原理的高级视图?
我已经查看了内核代码,但是其中有很多,我不明白。我希望有人提供摘要?
我在考虑 Linux 内核是如何实现系统调用的,我想知道是否有人可以给我一个关于 sbrk/brk 工作原理的高级视图?
我已经查看了内核代码,但是其中有很多,我不明白。我希望有人提供摘要?
在一个非常高级的视图中,Linux 内核将进程可见的内存跟踪为几个“内存区域”(struct vm_area_struct
)。还有一个结构表示(再次在非常高级的视图中)进程的整个地址空间(struct mm_struct
)。每个进程(除了一些内核线程)都只有一个struct mm_struct
,它又指向struct vm_area_struct
它可以访问的所有内存。
sys_brk
系统调用(在 中找到)mm/mmap.c
只是调整其中一些内存区域。(sbrk
是一个 glibc 包装器brk
)。它通过比较brk
地址的旧值(在内部找到struct mm_struct
)和请求的值来实现。
首先查看mmap
函数族会更简单,因为brk
它是它的一个特例。
您必须了解虚拟内存的工作原理,以及 MMU 映射与真实 RAM 的关系。
真正的 RAM 被分成页面,传统上每个页面 4kB。每个进程都有自己的 MMU 映射,它为该进程提供一个线性内存空间(在 32 位 linux 中为 4GB)。当然,并不是所有的都被实际分配。起初,它几乎是空的,即没有真正的页面与大多数地址相关联。
当进程遇到一个未分配的地址(试图读、写或执行它)时,MMU 产生一个故障(类似于中断),并调用 VM 系统。如果它决定应该有一些 RAM,它会选择一个未使用的 RAM 页并与该地址范围相关联。
这样,内核不关心进程如何使用内存,进程也不关心有多少 RAM,它总是有相同的线性 4GB 地址空间。
现在,brk/sbrk
工作在稍高的水平上:原则上,“超出”该标记的任何内存地址都是无效的,并且如果访问也不会获得 RAM 页面,则该进程将被终止。用户空间库在此限制内管理内存分配,并且仅在需要时要求内核增加它。
但是,即使一个进程通过设置brk
为允许的最大值开始,它也不会分配真正的 RAM 页面,直到它开始访问所有内存地址。
好吧,从超高层的角度来看,内核分配一个可分页的内存块,修改请求该块的进程的页表,以便将内存映射到进程的 VA 空间,然后返回地址。
linux内核如何将内存传递给用户进程的一个关键概念是可用的进程堆(数据段)从底部增长。内核不跟踪单个内存块,只跟踪一个连续的内存块。brk/sbrk 系统调用扩展了进程拥有的内存量,但由进程来管理它的可用部分。
这样做的一个关键结果是,分散在进程地址空间中的未使用的内存无法返回给操作系统以供其他用途。只有数据段最后的内存可以返回给操作系统,所以靠近末尾的正在使用的内存必须向下移动到顶部。实际上,几乎没有分配器这样做。出于这个原因,管理好进程使用的最大内存量通常很重要,因为这决定了为其他进程留下多少内存。