Linux中的sbrk/brk是如何实现的?

21

我在思考Linux内核如何实现系统调用,想知道有没有人可以给我一个关于sbrk/brk的高级视图?

我已经查看了内核代码,但是太多了,我不理解。我希望有人能给我一个摘要。

4个回答

26
在非常高的层面上,Linux内核将对进程可见的内存跟踪为几个“内存区域”(struct vm_area_struct)。还有一个表示进程整个地址空间的结构体(再次强调这是在非常高的层面上)(struct mm_struct)。每个进程(除了一些内核线程)都恰好有一个struct mm_struct,它又指向该进程可以访问的所有struct vm_area_structsys_brk系统调用(在mm/mmap.c中找到)仅会调整其中一些内存区域。 (sbrkbrk的glibc包装器)。它通过比较旧的brk地址值(在struct mm_struct中找到)和请求的值来进行调整。
最好先看一下mmap函数族,因为brk是它的一种特例。

(sbrk是brk的glibc包装器)-我一直在努力寻找sbrk的系统调用。但实际上并没有,现在有点明白了。 - automaton

19
你需要理解虚拟内存的工作原理以及MMU映射与实际RAM的关系。
实际RAM被划分为页面,传统上每个页面大小为4KB。每个进程都有自己的MMU映射,为该进程呈现一个线性内存空间(在32位Linux中为4GB)。当然,并不是所有的页面都被实际分配了。一开始,它几乎是空的,大多数地址没有与任何实际页面关联。
当进程访问未分配的地址(无论是尝试读取、写入还是执行),MMU会生成一个故障(类似于中断),并调用VM系统。如果它决定应该有一些RAM存在那里,它会选择一个未使用的RAM页面,并将其与该地址范围关联起来。
这样,内核就不用关心进程如何使用内存,进程也不用真正关心有多少RAM可用,它总是具有相同的4GB线性地址空间。
现在,brk/sbrk在略高的级别上工作: 原则上,超出这个标记的任何内存地址都是无效的,如果访问了这些地址,则进程会被杀死。用户空间库在此限制范围内管理内存分配,仅在需要时请求内核增加它。
但即使进程设置了brk到最大允许值,直到它开始访问所有这些内存地址,它都不会分配实际的RAM页面。

4

从一个超高层次的角度来看,内核分配一个可分页的内存块,修改请求该块的进程的页面表,使得内存映射到进程的虚拟地址空间中,然后返回地址。


内核是否只是在某个地方保留了一个数据结构,指示“进程x拥有内存空间y”? - samoz
1
每个进程都有自己的MMU映射,其中包括它所拥有的页面。 - Javier
不仅仅是内核 - 这是硬件所需的,并且每次发生上下文切换时都在CR3寄存器中设置。 - Ana Betts

3
Linux内核向用户进程传递内存的一个关键概念是,进程可用堆(数据段)从底部向上增长。内核不跟踪单个内存块,只跟踪连续的内存块。brk/sbrk系统调用可以扩展进程的内存量,但由进程来管理可用的内存块。
这样做的一个关键后果是,在进程地址空间中散布的未使用内存无法返回给操作系统进行其他用途。只有数据段末尾的内存可以返回给操作系统,因此靠近末尾的正在使用的内存必须向顶部移动。实际上,几乎没有分配器会这样做。因此,通常很重要的是要好好管理进程使用的最大内存量,因为这决定了剩余多少内存供其他进程使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接