【发布时间】:2012-11-21 22:41:33
【问题描述】:
我目前打算使用 clone() 实现线程,一个问题是,如果我让所有线程使用相同的内存空间,我在给定线程中调用的每个函数,当调用了相同的函数,还是我必须做一些事情来确保发生这种情况?
【问题讨论】:
标签: multithreading memory gcc clone
我目前打算使用 clone() 实现线程,一个问题是,如果我让所有线程使用相同的内存空间,我在给定线程中调用的每个函数,当调用了相同的函数,还是我必须做一些事情来确保发生这种情况?
【问题讨论】:
标签: multithreading memory gcc clone
每个线程总体上将使用相同的内存映射,但为函数调用使用不同的、单独的线程本地堆栈。当不同的线程调用了同一个函数(它本身位于同一个可执行内存位置)时,任何局部变量都不会被共享,因为它们在进入函数时/根据需要分配在堆栈上,并且每个线程都有自己的默认堆栈。
对任何静态/全局内存的引用(即,未在线程本地堆栈上分配的任何内容,例如全局变量或对堆的引用或mmap 内存区域在调用clone 后传递给线程/对线程可见,一般来说,完整的内存映射和进程上下文本身)当然是共享的,并且会受到通常的多线程问题(即共享状态的同步)的影响。
请注意,在调用clone 之前,您必须自己设置这个线程本地堆栈空间。从手册页:
child_stack参数指定堆栈的位置 子进程。由于子进程和调用进程可能共享 内存,子进程不可能在 与调用进程相同的堆栈。因此调用进程必须 为子堆栈设置内存空间并传递一个指向此的指针 空间到clone()。 堆栈在所有运行 Linux 的处理器上向下增长(HP 除外) PA 处理器),所以child_stack通常指向最高地址 为子栈设置的内存空间。
child_stack 参数是clone 的第二个参数。调用者(即父线程)有责任确保通过clone 创建的每个子线程为其堆栈接收一个单独且不重叠的内存块。
请注意,分配和设置这个线程本地堆栈内存区域一点也不简单。确保您的分配是页面对齐的(起始地址在 4K 边界上)、页面大小 (4K) 的倍数、足够大(如果您只有几个线程,则 2MB 是安全的),并且理想情况下包含“可用空间后面的守卫”部分。堆栈保护是一些没有访问权限的页面 - 不读取,不写入 - 在主堆栈区域之后保护虚拟内存地址空间的其余部分,如果线程动态超过其堆栈大小(例如,有一堆递归或具有非常大的临时缓冲区作为局部变量的函数)并尝试继续增长到堆栈保护区域,这将提前失败,因为线程将立即获得 SIGSEGV 而不是阴险的破坏。堆栈保护在技术上是可选的。您可能应该使用mmap 来分配您的堆栈,尽管posix_memalign 也可以。
说了这么多,我不得不问:为什么要尝试使用clone 来实现线程?这里有一些非常具有挑战性的问题,POSIX 线程库已经解决了这些问题(也以可移植的方式)。如果它是您想要的clone 的细粒度控制,那么检查pthread_attr_* 函数;它们几乎涵盖了所有非晦涩的用例(例如,如果您愿意,可以允许您分配自己的堆栈——从前面的讨论来看,我认为您不会)。除其他外,非常高性能的通用 Linux 实现完全包装了clone 和大量其他与线程相关的令人发指的系统调用——其中许多甚至没有 C 库包装器,必须通过 syscall 调用。这一切都取决于你想做什么。
【讨论】: