pthread_create在内存不足的情况下会因ENOMEM失败

3

我有一块SH4主板,以下是规格...

uname -a
Linux LINUX7109 2.6.23.17_stm23_A18B-HMP_7109-STSDK #1 PREEMPT Fri Aug 6 16:08:19 ART 2010
sh4 unknown

假设我已经使用了几乎所有的内存,只剩下9MB。

free
 total       used       free     shared    buffers     cached
Mem:         48072      42276       5796          0        172       3264
-/+ buffers/cache:      38840       9232
Swap:            0          0          0

现在,当我尝试以默认堆栈大小(8 MB)启动单个线程时,pthread_create会因为ENOMEM失败。如果我使用strace对我的测试代码进行跟踪,我可以看到失败的函数是mmap:

old_mmap(NULL, 8388608, PROT_READ|PROT_WRITE|PROT_EXEC,                       
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)

然而,当我使用ulimit -s将堆栈大小设置为较低的值时:
ulimit -s 7500

我现在可以启动10个线程。每个线程都不分配任何内容,因此仅消耗最小的开销(大约每个线程8kb,对吗?)。
所以,我的问题是:
已知mmap实际上不会占用内存,为什么当可用内存低于线程堆栈大小时pthread_create()(或mmap)会失败?
4个回答

6

VM设置/proc/sys/vm/overcommit_memory(也称为sysctl vm.overcommit_memory)控制Linux是否愿意分配比机器的RAM + swap更多的地址空间。 (当然,如果您实际尝试访问那么多内存,某些东西将会崩溃。请搜索“linux oom-killer”...)

此设置的默认值为0。我猜测在您的系统上有人将其设置为其他值。


vm/overcommit 的值为零,但我认为它与此有些相关。 - Ezequiel Garcia
是的,这个设置比我记得的要复杂一些... 显然,“0”表示“有点超额承诺”,“1”表示“大量超额承诺”,而“2”表示“很少超额承诺”。或者类似于这样的东西。我认为细节可能会因内核版本而异... - Nemo

3
在glibc下,线程的默认堆栈大小为2-10兆字节(通常为8)。你应该使用pthread_attr_setstacksize并使用结果属性对象调用pthread_create来请求一个较小堆栈的线程。

那不是我在问的,我知道我可以降低堆栈,但这并不改变当空闲内存低于线程堆栈大小时我无法创建另一个线程的事实。 - Ezequiel Garcia
你测量“可用内存”的方法是错误的。请查看/proc/meminfo中的CommitLimit(总可用)和Commit_AS(已使用量)行。 - R.. GitHub STOP HELPING ICE
我的测量方法可能是错误的。但是CommitLimit也不完全正确。从proc.txt中可以看到:“只有在启用了严格的超额提交会计(vm.overcommit_memory中的模式2)时,才会遵守此限制。”而我将overcommit_memory设置为零。无论如何,感谢提示,似乎我需要深入挖掘内核mm以找到答案。 - Ezequiel Garcia

2

mmap 消耗 地址空间

指针必须在内存中唯一地标识一个“内存”(包括mmap文件)。

32位指针只能寻址2/3GB的内存(32位=2^32=4GB,但某些地址空间被内核保留)。这个地址空间是有限的。

进程中的所有线程共享同一地址空间,但不同进程具有单独的地址空间。


实际上,由于地址空间有限,您可以启动的线程数量存在限制;然而,在这种情况下并非如此。请记住,我甚至不能启动一个单独的线程。显然,它拥有足够的地址空间(8 MB)来支持它。 - Ezequiel Garcia
@Ezequiel Garcia - 你确定吗?我不确定实现细节,但有可能相关代码会优先选择某些正在使用的地址范围,并且永远不会考虑空闲的范围。 - asveikau
@Ezequiel García,尝试使用cat /proc/<pid>/maps命令,查看内存映射。 - J-16 SDiZ

0

这是操作系统优雅地失败的唯一机会。如果实现允许此操作成功,它可能在无法返回失败代码的操作期间耗尽内存,例如堆栈增长。操作系统更喜欢让操作优雅地失败,而不是冒着必须终止完全无辜的进程的风险。


你能支持这个答案吗?我至少想知道在内核源代码中可以查找到哪里。 - Ezequiel Garcia

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