据我了解,Linux 上 pthread 的默认堆栈大小是 16K。我在 64 位 Ubuntu 安装中得到了奇怪的结果。
$ ulimit -s
8192
还:
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Thread stack size = %d bytes \n", stacksize);
Prints
Thread stack size = 8388608 bytes
我很确定堆栈大小不是“8388608”。可能出什么问题了?
答案1
事实上,你的虚拟的堆栈大小是8388608 字节 (8 MB)。当然,很自然地得出这样的结论:这不可能是正确的,因为对于每个线程来说,其堆栈消耗的内存量大得离谱,而 99% 的情况下,几 KB 可能就足够了。
好消息是你的线程只使用了身体的它实际需要的内存。这是操作系统通过使用处理器中的硬件内存管理单元 (MMU) 获得的神奇力量之一。发生的情况如下:
操作系统通过为线程设置 MMU 页表为堆栈分配 8 MB 虚拟内存。这需要很少的 RAM 来仅保存页表条目。
当线程运行并尝试访问堆栈上尚未分配物理页的虚拟地址时,MMU 会触发称为“页面错误”的硬件异常。
CPU内核通过切换到特权执行模式(有自己的堆栈)并调用内核内部的页错误异常处理函数来响应页错误异常。
内核将物理 RAM 页面分配给该虚拟内存页面并返回给用户空间线程。
用户空间线程看不到这些工作。从它的角度来看,它只是使用堆栈,就好像内存一直都在那里一样。同时,堆栈会自动增长(或不增长)以满足线程的需求。
MMU 是当今计算机系统硬件的关键部分。特别是,它负责系统中的许多“魔法”,因此我强烈建议您更多地了解 MMU 的功能以及一般的虚拟内存。此外,如果您的应用程序对性能敏感并处理大量数据,您应该了解 TLB(MMU 的页表缓存)如何工作以及如何重组数据或算法以最大限度地提高 TLB 命中率。
答案2
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
该
stacksize
属性应定义为创建的线程堆栈分配的最小堆栈大小(以字节为单位)。
在您的示例中,堆栈大小设置为 8388608 字节,相当于命令ulimit -s
So 返回的 8MB,因此匹配。
从pthread_create()
描述来看:
在Linux/x86-32,新线程的默认堆栈大小是2兆字节。在 NPTL 线程实现下,如果 RLIMIT_STACK程序启动时的软资源限制为“无限制”以外的任何值,然后它决定新线程的默认堆栈大小。使用pthread_attr_setstacksize(3)、可以在用于创建线程的attr参数中显式设置堆栈大小属性,以获得默认值以外的堆栈大小。
因此,线程堆栈大小可以通过上面的 set 函数或ulimit
系统属性来设置。对于您所指的 16k,尚不清楚您在哪个平台上看到过该内容和/或是否为此设置了任何系统限制。
请参阅pthread_创建页面和这里一些有趣的例子。