pthread 的默认堆栈大小

Kam*_*ath 29 c multithreading

据我了解,Linux 上 pthread 的默认堆栈大小是 16K。我在 64 位 Ubuntu 安装上得到了奇怪的结果。

$ ulimit -s
8192
Run Code Online (Sandbox Code Playgroud)

还:

pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Thread stack size = %d bytes \n", stacksize);

Prints
    Thread stack size = 8388608 bytes
Run Code Online (Sandbox Code Playgroud)

我很确定堆栈大小不是“8388608”。可能有什么问题?

jtc*_*tty 57

实际上,您的虚拟堆栈大小为8388608 字节(8 MB)。当然,很自然地得出这样的结论,这不可能是正确的,因为当 99% 的时间可能只需要几个 KB 时,每个线程为其堆栈消耗的内存量大得离谱。

好消息是您的线程只使用它实际需要的物理内存量。这是您的操作系统通过使用处理器中的硬件内存管理单元 (MMU) 获得的神奇力量之一。这是发生的事情:

  1. 操作系统通过为您的线程设置 MMU 的页表为您的堆栈分配 8 MB 的虚拟内存。这仅需要很少的 RAM 来保存页表条目。

  2. 当您的线程运行并尝试访问尚未分配物理页面的堆栈上的虚拟地址时,MMU 会触发称为“页面错误”的硬件异常。

  3. CPU 内核通过切换到特权执行模式(具有自己的堆栈)并调用内核内部的页面错误异常处理函数来响应页面错误异常。

  4. 内核为该虚拟内存页面分配一个物理 RAM 页面,然后返回给用户空间线程。

用户空间线程看不到这些工作。从它的角度来看,它只是使用堆栈,就好像内存一直在那里一样。同时,堆栈会自动增长(或不增长)以满足线程的需要。

MMU 是当今计算机系统硬件的关键部分。特别是,它负责系统中的许多“魔法”,所以我强烈建议更多地了解 MMU 的作用,以及一般的虚拟内存。此外,如果您的应用程序对性能敏感并处理大量数据,您应该了解 TLB(MMU 的页表缓存)的工作原理以及如何重构数据或算法以最大化 TLB 命中率。

  • 我(还)没有想到任何书,但这是我通过 DuckDuckGo 找到的一篇很好的小论文,它很好地解释了 TLB 的工作原理(威斯康星大学麦迪逊分校的 Comp Sci 课程的一部分,从外观上看它):https://pages.cs.wisc.edu/~remzi/OSTEP/vm-tlbs.pdf。这应该足以让您至少开始推理您的数据布局和算法可能如何影响您的 TLB 命中率。似乎是另一个问题的好话题。;) (2认同)

fdu*_*uff 22

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
Run Code Online (Sandbox Code Playgroud)

该stacksize属性应定义为创建的线程堆栈分配的最小堆栈大小(以字节为单位)。

在您的示例中,堆栈大小设置为 8388608 字节,对应于 8MB,如ulimit -s 匹配命令返回的那样。

从pthread_create()描述来看:

在Linux/x86-32 上,新线程的默认堆栈大小为2 MB。在 NPTL 线程实现下,如果程序启动时的 RLIMIT_STACK软资源限制具有“无限制”以外的任何值,则它确定新线程的默认堆栈大小。使用pthread_attr_setstacksize (3),可以在用于创建线程的 attr 参数中显式设置堆栈大小属性,以获得不同于默认值的堆栈大小。

因此线程堆栈大小可以通过上面的 set 函数或ulimit系统属性设置。对于您所指的 16k,尚不清楚您在哪个平台上看到的和/或是否为此设置了任何系统限制。

请参阅pthread_create 页面和此处以获取有关此内容的一些有趣示例。