C语言 堆内存和平板分配



我对heapfree list感到困惑。我有几个问题,我对 malloc 在 C 中的工作方式有自己的理解。如果我错了,请纠正我。

  • 堆内存是否组织为数据的链表(空闲列表)块?
  • 堆内存和空闲列表之间有区别吗?

我对存储分配的理解(有待改进):-当我们调用malloc时,它会在堆中分配内存,并且通过从free list中选择一个合适大小的数据块来实现,对吗?

当 malloc 返回某个内存块时,它会从空闲列表中删除,并且该内存块的物理地址会在页表中更新。

当使用free()释放内存时,数据块入回空闲列表中,并且可能,以减少碎片,与相邻块连接,并清除页表条目中的present位。

所以整个堆是一个自由列表(自由块链表)+分配的数据块。

这是存储分配的全面情况吗?

编辑:来自Linux内核开发(Robert Love)关于内存管理,平板分配的章节

"免费列表包含可用、已分配的数据块 结构。当代码需要数据结构的新实例时,它 可以从免费列表中抓取其中一个结构,而不是分配 足够的内存量,并为数据结构进行设置。 稍后,当不再需要数据结构时,它将返回到 可用列表而不是已解除分配。从这个意义上说,免费列表 充当对象缓存,缓存常用的对象类型。

自由列表被称为"可用的、已分配的数据结构块"。

  • 当它在自由列表中时,它是如何分配的?
  • 将内存块返回到空闲列表_not_与解除分配该块有何相同?
  • 板坯分配与存储分配有何不同
malloc()

页表无关;它分配虚拟地址,内核负责跟踪页面在物理RAM或磁盘上的实际存储位置。

malloc()通过brk()系统调用与内核交互,该调用要求内核为进程分配更多页面,或将页面释放回内核。 因此,实际上有两个级别的内存分配:

    内核将页面
  • 分配给进程,使这些页面不可供其他进程使用。 从内核的角度来看,页面可以位于任何地方,并且它们的位置由页表跟踪,但从进程的角度来看,它是一个连续的虚拟地址空间。 brk()操作的"程序中断"是内核允许您访问的地址(因为它们对应于分配的页面)和在您尝试访问它们时会导致分段错误的地址之间的边界。
  • malloc()分配程序数据段的可变大小部分供程序使用。 当数据段的当前大小中没有足够的可用空间时,它会使用 brk() 从内核获取更多页面,从而使数据段更大。 当它发现数据段末尾的某些空间未使用时,它会使用 brk() 将未使用的页面返回给内核,从而使数据段更小。

请注意,页面可以分配给进程(由内核),即使该进程中运行的程序实际上并未将这些页面用于任何用途。 如果您free()位于数据段中间的内存块,则free()的实现无法使用brk()来缩小数据段,因为在更高的地址中仍有其他分配的块。 因此,从内核的角度来看,页面仍然分配给您的程序,即使从malloc()的角度来看它们是"可用空间"。

你对免费列表如何工作的描述对我来说听起来是正确的,尽管我不是内存分配器如何实现的专家。 但是你从Robert Love那里发布的这句话听起来像是在谈论Linux内核中的内存分配,这与用户空间进程中malloc()的内存分配无关。 这种免费列表的工作方式可能不同。

您要做的第一件事是区分内核和程序分配。 就像@Wyzard说的,malloc使用brk(sbrk,有时是mmap)从内核中获取更多页面。 我对malloc的理解非常好,但它跟踪了你可能称之为竞技场的东西。 它调解对内存的访问,并将根据需要进行适当的系统调用以分配内存。

空闲列表是管理内存的一种方法。 每次需要内核中的更多内存时调用 mmap 或 brk 既缓慢又低效。 这两者都需要上下文切换到内核模式,并将分配数据结构来跟踪哪个进程拥有内存。 用户级别的免费列表是一种优化,不会不断请求内存并将其返回给内核。 用户程序的目标是完成其工作,而不是等待内核完成其工作。

现在回答您的其他问题:

  • 当它在自由列表中时,它是如何分配的?

将免费列表视为缓存的另一种方式。 程序将发出请求,内核将尝试按需完成请求,而不是一次全部完成。 但是,当程序使用一段内存完成时,快速路径不是将其返回到内核,而是将其放在安全的地方以便再次分配。 特别是,自由列表可以跟踪分配器可以从中提取的内存区域,但这样做的方式(具有内存保护),即用户代码不能只是选择它并开始写入所有区域。

  • 将内存块返回到空闲列表与解除分配该块有何不同

如果我们假设真正解除分配一个块需要将其返回到内核并让它更新其内部页表,那么区别实际上在于什么可以控制底层物理页面(或帧)。 实际上,解除分配内存并将内存返回到内核意味着现在内核可以从这些页面中提取,而将其返回到用户级别的自由列表意味着程序的某些部分仍然控制该内存。

  • 板坯分配与存储分配有何不同?

这开始得到一些不同的领域(我不是很熟悉)。 平板分配器是内核管理内存分配的一种方式。 据我所知,该板试图将分配分组为不同的大小,并有一个页面池来满足这些请求。 我相信常见的 x86 架构将允许以 16 字节到 4 MB 的 2 次方分配连续的物理内存(尽管我在 64 位机器上)。 我相信在这个级别有一些自由列表的概念,以及有效升级或降级分配大小以允许不同系统需求的方法。

另一方面,存储分配听起来像是确保硬盘上有空间。 我实际上没有听说过这个词,所以我只能推测。

这里你指的是两个不同的分配器,

  1. Buddy系统分配器,用于将页面分配给Zone,使用free_list来存储空闲页面,分配它们,释放后,如果可能的话,将它们组合回一个更大的连续页面大小。
  2. Slab 分配器,适用于已经分配的数据结构,如 keme_cache,kmalloc 调用。

有关堆,请参阅 C 编程中的堆内存。

对于用户空间中的 c 程序,我们在发生 malloc 的call_stack中有堆内存。这由 _break 标记,当需要更多内存时,sbrk() 会提前进行。

在 Linux 内核中,每个进程都有task_struct它有自己的堆栈和指向它使用的页面列表的指针。

相关内容

  • 没有找到相关文章

最新更新