我正在对 C++ 绿色线程进行一些研究,主要是boost::coroutine2
类似的 POSIX 函数makecontext()/swapcontext()
,并计划在boost::coroutine2
. 两者都需要用户代码为每个新函数/协程分配一个堆栈。
我的目标平台是 x64/Linux。我希望我的绿色线程库适用于一般用途,因此堆栈应根据需要扩展(合理的上限很好,例如 10MB),如果未使用太多内存时堆栈可以收缩(不需要),那就太好了)。我还没有找到合适的算法来分配堆栈。
经过一番谷歌搜索,我自己想出了几个选项:
- 使用编译器实现的拆分堆栈(gcc -fsplit-stack),但拆分堆栈有性能开销。由于性能原因,Go 已经远离了拆分堆栈。
- 分配一大块内存,
mmap()
希望内核足够聪明,不分配物理内存,只在访问堆栈时分配。在这种情况下,我们受内核的支配。 - 预留一个大的内存空间
mmap(PROT_NONE)
并设置一个SIGSEGV
信号处理程序。在信号处理程序中,当SIGSEGV
由堆栈访问引起时(访问的内存在保留的大内存空间内),分配所需的内存mmap(PROT_READ | PROT_WRITE)
。这是这种方法的问题:mmap()
不是异步安全的,不能在信号处理程序中调用。它仍然可以实现,但非常棘手:在程序启动期间创建另一个线程用于内存分配,并用于pipe() + read()/write()
将内存分配信息从信号处理程序发送到线程。
关于选项 3 的更多问题:
- 我不确定这种方法的性能开销,当内存空间由于数千次
mmap()
调用而极度碎片化时,内核/CPU 的性能如何? - 如果在内核空间中访问未分配的内存,这种方法是否正确?例如,何时
read()
调用?
绿色线程的堆栈分配还有其他(更好的)选项吗?在其他实现(例如 Go/Java)中如何分配绿色线程堆栈?