在Linux上的clone()系统调用需要一个参数指向堆栈为新创建的线程使用。 最显而易见的方法做,这是简单的malloc一些空间和传递,但你必须确保你malloc分配尽可能多的堆栈空间为线索将永远使用(难以预料)。
我记得使用并行线程时,我没有做到这一点,所以我很好奇它的所作所为吧。 我碰上了这个网站 ,其解释说,“最好的解决办法,由Linux pthreads实现使用时,要使用mmap分配内存,用标志指明这是分配,因为它被使用。这样一来,内存分配的内存区域堆栈,因为它是必要的,并且如果该系统无法分配额外存储器将发生分段违例。”
我曾经听说过使用MMAP唯一的上下文是映射文件到内存,确实读取MMAP手册页它需要一个文件描述符。 这怎么可能用于分配动态长度的堆栈给克隆()? 是网站只是疯了吗? ;)
在这两种情况下,不内核需要知道如何找到内存,使新的堆栈反正免费的一群,因为这东西它为用户推出新的流程来完成所有的时间? 为什么一个堆栈指针甚至需要摆在首位,以指定如果内核已经可以算出这个?
约瑟夫,在回答你的最后一个问题:
当用户创建一个“正常”的新的进程,这是由叉()完成。 在这种情况下,内核不担心在所有的人创造一个新的堆栈,因为新工艺是旧的完整副本,一直到堆栈。
如果用户使用EXEC()取代当前运行的进程,那么内核就需要创建一个新的堆栈 - 但在这种情况下,很容易的,因为它会从一张白纸开始。 EXEC()清除进程的内存空间,并重新初始化,所以内核变说“EXEC()之后,堆栈始终住在这里”。
但是,如果我们使用的clone(),那么我们可以说,新工艺将分享与老工艺(CLONE_VM)的存储空间。 在这种情况下,内核不能离开堆栈,因为它是在调用进程(如fork()的一样),因为那么我们的两个进程将对方的堆栈上跺脚。 该内核还不能仅仅把它放在一个默认位置(象exec())做,因为该位置已经采取了这种存储空间。 唯一的解决办法是允许调用进程找个地方吧,这是它做什么。
你想要的MMAP的MAP_ANONYMOUS标志。 而MAP_GROWSDOWN因为你想使用它作为一个堆栈。
就像是:
void *stack = mmap(NULL,initial_stacksize,PROT_WRITE|PROT_READ,MAP_PRIVATE|MAP_GROWSDOWN|MAP_ANONYMOUS,-1,0);
更多信息,请参见MMAP手册页。 请记住,克隆是一种低层次的概念,那你不是为了使用,除非你真的需要它所提供。 它提供了大量的控制 - 如设置它自己的堆栈 - 万一你想要做一些trickering(就像堆栈中的所有相关进程访问)。 除非你有很好的理由使用克隆,坚持用叉子或并行线程。
堆栈都没有,而且永远不能,在无限的成长空间。 和其他事物一样,他们住在进程的虚拟地址空间,并通过它们可以生长量总是通过相邻的映射内存区域的距离限制。
当人们谈论栈动态增长,他们可能意味着是两件事情之一:
- 堆栈的页面可能是写入时复制零个页面,不要做,直到执行第一次写入传抄。
- 堆叠区域的下部可能还没有被保留的(并因此不计入过程的提交电荷,即,在内核占作为保留该过程的物理内存/交换的量),直到保护页被命中,其中情况下,内核提交更多移动保护页,或者如果没有记忆中留下提交终止进程。
试图依靠MAP_GROWSDOWN
标志是不可靠的, 危险的 ,因为它不能保护你免受mmap
创建一个新的映射只是邻近的筹码,那么这将得到重挫。 (参见http://lwn.net/Articles/294001/ )对于主线程,内核自动保留堆栈大小ulimit
的堆叠下面地址空间 (未存储器 )值得并防止mmap
从分配它。 (但要注意!有些破供应商修补内核禁用此行为导致随机内存损坏!)对于其他线程,你就必须 mmap
的地址空间时创建它的线程可能需要为堆的整个范围。 有没有其他办法。 你可以让大部分最初不可写/不可读,并更改上的故障,但随后你需要信号处理程序和该解决方案是不是在POSIX线程实现接受的,因为这将在应用程序的信号处理程序干扰。 (需要注意的是,作为扩展,内核可以提供特殊MAP_
标志来传递不同的信号,而不是SIGSEGV
非法访问映射,然后线程执行能赶上并作用于这个信号,但Linux的目前有没有这样的特征。)
最后,需要注意的是, clone
系统调用并不需要一个堆栈指针参数,因为它并不需要它。 该系统调用必须从汇编代码中执行,因为需要用户空间的包装改变堆栈指针的“孩子”线程指向所需的堆栈,并避免任何内容写入父的堆栈。
事实上, clone
确实需要一个堆栈指针参数,因为它是不安全的等待返回用户空间后更改的“孩子”堆栈指针。 除非信号都受阻,信号处理程序可以了错误的堆栈上立即运行,并在某些架构堆栈指针必须是有效的,并以点带面的安全在任何时候都写。
不仅是修改堆栈指针由C是不可能的,但你也不能避免编译器会在系统调用之后,但堆栈指针被改变之前揍父堆栈的可能性。
需要注意的是clone
系统调用不承担堆栈位置的参数。 实际上,它的工作原理就像fork
。 这只是glibc的包装这需要这样的说法。
这里是一个mmaps堆栈区域&指示克隆系统调用来使用此区域为堆栈的代码。
#include sys/mman.h>
#include stdio.h>
#include string.h>
#include sched.h>
int execute_clone(void *arg)
{
printf("\nclone function Executed....Sleeping\n");
fflush(stdout);
return 0;
}
int main()
{
void *ptr;
int rc;
void *start =(void *) 0x0000010000000000;
size_t len = 0x0000000000200000;
ptr = mmap(start, len, PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED|MAP_GROWSDOWN, 0, 0);
if(ptr == (void *)-1)
{
perror("\nmmap failed");
}
rc = clone(&execute_clone, ptr + len, CLONE_VM, NULL);
if(rc <= 0)
{
perror("\nClone() failed");
}
}
MMAP不仅仅是映射文件到内存的更多。 事实上,一些malloc实现将使用mmap大型分配。 如果你读了好人网页你会发现MAP_ANONYMOUS标志,你会看到,你可以不需要在所有提供的文件描述符。
至于为什么内核不能只是“找一堆免费的记忆”,以及如果你希望有人这样做,对你的工作,无论是用叉子来代替,或者使用并行线程。
我认为堆栈向下增长,直到它不能生长,例如当它长到以前已经分配,也许故障notified.That可以看到默认的最小可用堆栈大小,如果是多余空间的存储器向下当堆栈已满,它可以向下生长,否则,系统会通知故障。