C++绿色线程的栈分配 我在C++绿色线程中做了一些研究,主要是Boosi::CordONE2和类似的POSIX函数,如MakEngTeNe/SWAPValeNo,并计划在Booost:CordONIZ2的顶部实现一个C++绿色线程库。两者都需要用户代码为每个新函数/协同程序分配一个堆栈

C++绿色线程的栈分配 我在C++绿色线程中做了一些研究,主要是Boosi::CordONE2和类似的POSIX函数,如MakEngTeNe/SWAPValeNo,并计划在Booost:CordONIZ2的顶部实现一个C++绿色线程库。两者都需要用户代码为每个新函数/协同程序分配一个堆栈,c++,memory-management,green-threads,boost-coroutine,C++,Memory Management,Green Threads,Boost Coroutine,我的目标平台是x64/Linux。我希望我的绿色线程库适合一般用途,因此堆栈应根据需要扩展。合理的上限是可以的,例如10MB,如果堆栈在不需要太多内存的情况下可以收缩,那就太好了。我还没有找到合适的算法来分配堆栈 在谷歌搜索之后,我自己想出了几个选择: 使用由编译器gcc-fsplit堆栈实现的拆分堆栈,但是拆分堆栈有性能开销。由于性能原因,Go已从拆分堆栈中移出。 用mmap分配一大块内存希望内核足够聪明,可以不分配物理内存,并且只在访问堆栈时分配。在这种情况下,我们受内核的支配。 使用mma

我的目标平台是x64/Linux。我希望我的绿色线程库适合一般用途,因此堆栈应根据需要扩展。合理的上限是可以的,例如10MB,如果堆栈在不需要太多内存的情况下可以收缩,那就太好了。我还没有找到合适的算法来分配堆栈

在谷歌搜索之后,我自己想出了几个选择:

使用由编译器gcc-fsplit堆栈实现的拆分堆栈,但是拆分堆栈有性能开销。由于性能原因,Go已从拆分堆栈中移出。 用mmap分配一大块内存希望内核足够聪明,可以不分配物理内存,并且只在访问堆栈时分配。在这种情况下,我们受内核的支配。 使用mmapPROT_NONE保留较大的内存空间,并设置SIGSEGV信号处理程序。在信号处理程序中,当SIGSEGV由堆栈访问引起时,访问的内存位于保留的大内存空间内,使用mmapPROT_READ | PROT_WRITE分配所需内存。这种方法的问题在于:mmap不是异步安全的,不能在信号处理程序中调用。它仍然可以实现,但非常棘手:在程序启动期间创建另一个线程进行内存分配,并使用pipe+read/write将内存分配信息从信号处理程序发送到线程。 关于方案3,还有几个问题:

我不确定这种方法的性能开销,当内存空间由于数千次mmap调用而极度分散时,内核/CPU的性能如何? 如果在内核空间中访问未分配的内存,这种方法正确吗?e、 g.何时调用read?
对于绿色线程的堆栈分配,还有其他更好的选项吗?绿色线程堆栈在其他实现中是如何分配的,例如Go/Java?

为什么是mmap?当您使用new或malloc进行分配时,内存是未触及的,并且肯定不会映射

const int STACK_SIZE = 10 * 1024*1024;
char*p = new char[STACK_SIZE*numThreads];

p现在有足够的内存用于所需的线程。当您需要内存时,开始访问p+STACK_SIZE*i

glibc为普通C程序分配堆栈的方式是使用以下专门为此目的设计的mmap标志对区域进行mmap:

   MAP_GROWSDOWN
          Used for stacks.  Indicates to the kernel virtual memory  system
          that the mapping should extend downward in memory.
为了兼容性,您可能也应该使用MAP_堆栈。然后,您不必自己编写SIGSEGV处理程序,堆栈将自动增长。可以按此处所述设置边界

如果您想要一个有界的堆栈大小,这通常是人们在调用sigaltstack2时对信号处理程序所做的,只需发出一个普通的mmap调用


Linux内核总是映射回虚拟页面的物理页面,在第一次访问页面时捕获页面错误,可能不是在实时内核中,而是在所有其他配置中。如果您感兴趣,您可以使用/proc//pagemap接口或我编写的这个工具来验证这一点。

虽然根据POSIX,mmap不是异步安全的,但实际上它在Linux和几乎所有合理、可用的UNIX变体中都是异步安全的。@chrisdd我能问一下为什么mmap适合绿色线程吗?我不是专家,但我想知道。@chrisdsodd我还没有找到关于这个的任何手册页/链接,你介意给我一个链接吗?FWIW,我不知道Linux共享内存是否适合你的需要。但几年前我将其用于谷歌地图应用程序的高性能后端,性能非常好。@ErikAlapäGreen线程都运行在同一个内核线程上,因此它们共享相同的地址空间。这绝对不能保证被取消映射,或者被初始化为任何特定的值。使用GNU libc malloc,那么大的一部分分配最终将调用mmap。我听说MAP_GROWSDOWN可能会导致一些问题。今天仍然是这样吗?