C ++绿色线程的堆栈分配
我在C ++绿色线程中进行了一些研究,主要是boost::coroutine2
和类似的POSIX函数,如makecontext()/swapcontext()
,并计划在boost::coroutine2
之上实现一个C ++绿色线程库。 两者都需要用户代码为每个新函数/协程分配一个堆栈。
我的目标平台是x64 / Linux。 我希望我的绿色线程库适合一般用途,所以堆栈应该根据需要进行扩展(合理的上限是很好的,例如10MB),如果堆栈可能缩小,因为太多的内存不用(不是必需的)。 我还没有想出一个合适的算法来分配堆栈。
经过一些Google搜索之后,我自己想出了一些选择:
mmap()
分配一大块内存,希望内核足够聪明,可以在物理内存未分配的情况下分配,只有在访问堆栈时才分配内存。 在这种情况下,我们处于内核的摆布之中。 mmap(PROT_NONE)
预留大量内存空间并设置SIGSEGV
信号处理程序。 在信号处理程序中,当SIGSEGV
由堆栈访问引起(被访问的内存在预留的大内存空间内)时,使用mmap(PROT_READ | PROT_WRITE)
分配所需的内存。 这是这种方法的问题: mmap()
不是异步安全的,不能在信号处理程序中调用。 它仍然可以实现,但非常棘手:在程序启动期间为存储器分配创建另一个线程,并使用pipe() + read()/write()
从信号处理程序向线程发送内存分配信息。 关于选项3的几个问题:
mmap()
调用而极度分散时,内核/ CPU的性能如何? read()
被调用时? 是否有任何其他(更好)的绿色线程堆栈分配选项? 在其他实现中如何分配绿色线程堆栈,例如Go / Java?
glibc为普通C程序分配堆栈的方式是使用为此目的而设计的以下mmap标志来映射区域:
MAP_GROWSDOWN
Used for stacks. Indicates to the kernel virtual memory system
that the mapping should extend downward in memory.
为了兼容性,你也应该使用MAP_STACK
。 然后你不必自己编写SIGSEGV处理程序,堆栈会自动增长。 可以像这里描述的那样设置边界“ulimit -s unlimited”是做什么的?
如果你想要一个有限的堆栈大小,这通常是人们为信号处理程序而做的,如果他们想要调用sigaltstack(2)
,就发出一个普通的mmap调用。
Linux内核总是映射支持虚拟页面的物理页面,当页面首次访问时(可能不是实时内核,但当然是所有其他配置)捕获页面错误。 如果您有兴趣,可以使用/proc/<pid>/pagemap
界面(或者我写的https://github.com/dwks/pagemap)来验证这一点。
为什么选择mmap? 当你使用new(或malloc)分配内存时,内存不变,而且绝对没有映射。
const int STACK_SIZE = 10 * 1024*1024;
char*p = new char[STACK_SIZE*numThreads];
p现在有足够的内存供你想要的线程使用。 当你需要内存时,开始访问p + STACK_SIZE * i
链接地址: http://www.djcxy.com/p/53189.html