1

libc 如何与操作系统(例如,Linux 内核)通信以管理内存?具体来说,它是如何分配内存的,又是如何释放内存的?另外,在什么情况下它会分别分配和解除分配失败?

4

4 回答 4

3

这是一个非常笼统的问题,但我想谈谈分配失败。重要的是要意识到内存实际上是由内核在第一次访问时分配的。您在调用malloc//calloc时所做的事情realloc是在进程的虚拟地址空间内保留一些地址(通过 syscalls brkmmaplibc)。

当我得到malloc或类似失败时(或当libc得到brkmmap失败时),通常是因为我用尽了进程的虚拟地址空间。当没有连续的空闲地址块,没有空间扩展现有地址时,就会发生这种情况。您可以耗尽所有可用空间或达到限制 RLIMIT_AS。当使用多个线程时,这在 32 位系统上尤其常见,因为人们有时会忘记每个线程都需要它自己的堆栈。堆栈通常会消耗几兆字节,这意味着在没有更多可用地址空间之前,您只能创建几百个线程。耗尽地址空间的一个更常见的原因可能是内存泄漏。 Libc当然会尝试重用堆上的空间(由brk系统调用获得的空间)并尝试munmmap不需要的映射。但是,它不能重用未“释放”的东西。

由于分配失败,无法从进程(或作为进程一部分的 libc)中检测到物理内存不足。是的,您可以达到“过度使用限制”,但这并不意味着物理内存已全部占用。当可用物理内存不足时,内核会调用称为 OOM 杀手(Out Of Memory Killer)的特殊任务,该任务会终止一些进程以释放内存。

关于失败deallocate,我的猜测是除非你做一些愚蠢的事情,否则它不会发生。我可以想象将程序中断(堆结束)设置在其原始位置下方(通过brk系统调用)。这当然是灾难的根源。希望libc不会这样做,也没有多大意义。但这可以看作是失败的解除分配。 munmap如果您提供一些愚蠢的论点,也可能会失败,但我想不出它失败的常规原因。这并不意味着它不存在。我们必须深入挖掘 glibc/kernel 的源代码才能找到答案。

于 2012-11-23T22:32:07.003 回答
2

1)它如何分配内存

libc 为 C 程序提供 malloc()。

通常,malloc从堆中分配内存,并根据需要使用 sbrk(2) 调整堆的大小。当分配大于 MMAP_THRESHOLD 字节的内存块时,glibc malloc() 实现使用 mmap(2) 将内存分配为私有匿名映射。MMAP_THRESHOLD 默认为 128 kB,但可以使用 mallopt(3) 进行调整。使用 mmap(2) 执行的分配不受 RLIMIT_DATA 资源限制的影响(请参阅 getrlimit(2))。

这是关于 sbrk 的。

sbrk - 更改数据段大小

2) 什么情况下会分配失败

同样来自malloc

默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。

并从proc

/proc/sys/vm/overcommit_memory
该文件包含内核虚拟内存记帐模式。值为:

0:启发式过度使用(这是默认设置)
1:总是过度使用,从不检查
2:总是检查,从不过度使用

于 2012-11-20T19:13:22.763 回答
1

大多数情况下,它使用sbrk系统调用来调整数据段的大小,从而为其分配更多内存。以这种方式分配的内存通常不会释放回操作系统,因为只有当可释放的块位于数据段的末尾时才有可能这样做。

较大的块有时通过使用mmap来分配内存,并且可以通过munmap调用再次释放该内存。

于 2012-11-20T19:13:06.307 回答
1

libc 如何与操作系统(例如,Linux 内核)通信以管理内存?

通过系统调用- 这是内核提供的低级 API。

具体来说,它是如何分配内存的,又是如何释放内存的?

类 Unix 系统提供“sbrk”系统调用

另外,在什么情况下它会分别分配和解除分配失败?

例如,当没有足够的可用内存时,分配可能会失败。释放不会失败。

于 2012-11-20T19:14:53.517 回答