Linux克隆调用的最小堆栈大小?

时间:2016-08-14 06:06:49

标签: c linux fork clone

我一直在调查clone次调用,我注意到不同的子线程堆栈分配有三种不同的结果。以下演示分配一个堆栈 n -bytes big,其中 n 作为参数传递,然后尝试克隆。

foo.c的

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>

int child(void *arg)
{
    (void)arg;
    write(STDOUT_FILENO, "carpe momentum\n", 15);
    return 0;
}

int main(int argc, char **argv)
{
    long stacksize;
    pid_t pid;
    void *stack;

    if (argc < 2)
        return 1;

    errno = 0;
    stacksize = strtol(argv[1], NULL, 0);
    if (errno != 0)
        return 1;

    stack = malloc(stacksize);
    if (stack == NULL)
        return 1;

    pid = clone(child, stack + stacksize, 0, NULL);
    if (pid == -1)
        return 1;

    write(STDOUT_FILENO, "success\n", 8);

    return 0;
}

以下是我的观察:

$ cc -o foo foo.c
$ ./foo 0
Segmentation fault
$ ./foo 23
Segmentation fault
$ ./foo 24
success
$ ./foo 583
success
$ ./foo 584
success
carpe momentum
$ ./foo 1048576 #1024 * 1024, amount suggested by man-page example
success
carpe momentum

0到23之间的所有样本中的所有样本都是segfaulted,并且对于24到583之间的所有样本,父母成功了但是孩子保持沉默。任何合理的584以上都会导致两者都成功。

反汇编表明child仅使用16个字节的堆栈空间,再加上至少16个字节来调用write。但这已经超过了停止segfaulting所需的24个字节。

$ objdump -d foo
# ...
080484cb <child>:
 80484cb:       55                      push   %ebp
 80484cc:       89 e5                   mov    %esp,%ebp
 80484ce:       83 ec 08                sub    $0x8,%esp
 80484d1:       83 ec 04                sub    $0x4,%esp
 80484d4:       6a 0f                   push   $0xf
 80484d6:       68 50 86 04 08          push   $0x8048650
 80484db:       6a 01                   push   $0x1
 80484dd:       e8 be fe ff ff          call   80483a0 <write@plt>
 80484e2:       83 c4 10                add    $0x10,%esp
 80484e5:       b8 00 00 00 00          mov    $0x0,%eax
 80484ea:       c9                      leave  
 80484eb:       c3                      ret
# ...

这会引发几个重叠的问题。

  • 为什么没有clone在24到583字节的堆栈之间发生段错?
  • child如何以太少的堆栈无声地失败?
  • 用于堆栈空间的是什么?
  • 24和584字节有什么意义?它们如何根据不同的系统和实现而有所不同?
  • 我可以计算最低堆栈要求吗?我应该吗?

我正在使用i686 Debian系统:

$ uname -a
Linux REDACTED 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt25-2+deb8u3 (2016-07-02) i686 GNU/Linux

1 个答案:

答案 0 :(得分:2)

  
      
  • 为什么不克隆24到583字节的堆栈之间的段错误?
  •   

确实如此,但因为它是一个单独的过程,所以你不会看到它。在24岁之前,不是孩子是segfaults,而是父母试图建立孩子。尝试使用strace -ff来发现这种情况。

  
      
  • 孩子如何以太少的堆栈无声地失败?
  •   

当孩子去世时,会通知家长。这种情况下的父母(进行clone()调用的父母)对此通知不做任何事情。它不是&#34;沉默的原因&#34;低于24是因为当父母去世时,你的shell会收到通知。

  
      
  • 用于堆栈空间的是什么?
  •   
  • 24和584字节有什么意义?它们如何根据不同的系统和实现而有所不同?
  •   

前24(和一点)用于设置对child的函数调用。因为它是一个普通的函数,完成后它将返回到调用函数。这意味着clone必须设置一个返回的调用函数(一个只是干净地终止子元素的函数)。

584(有点)显然是调用函数的局部变量,函数write以及write调用所需的内存量。

我写的原因&#34;(和一点点)&#34;是因为stack之前可能会有一些内存可用,并且当用完房间时clonechild会被滥用。尝试在克隆后添加free(stack)以查看该滥用的结果。

  
      
  • 我可以计算最低堆栈要求吗?我应该吗?
  •   

一般情况下你可能不应该这样做。它需要对您的函数和使用的外部函数进行深入分析。就像&#34;正常&#34;程序,我建议去默认(如果我没记错的话,在linux上是8MB)。只有当你有严格的内存要求(或堆栈溢出问题)时,你才应该开始担心这些事情。