Linux 克隆调用的最小堆栈大小?
Minimal stack size for Linux clone call?
我一直在摆弄 clone
调用,我注意到不同的子线程堆栈分配有三种不同的结果。下面的演示分配了一个 n 字节大的堆栈,其中 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 之间的样本都发生了段错误,对于 24 到 583 之间的所有样本,父级成功但子级保持沉默。高于 584 的任何合理值都会导致两者都成功。
反汇编表明 child
仅使用 16 个字节的堆栈 space,另外至少还有 16 个字节用于调用 write
。但这已经超过了停止段错误所需的 24 个字节。
$ objdump -d foo
# ...
080484cb <child>:
80484cb: 55 push %ebp
80484cc: 89 e5 mov %esp,%ebp
80484ce: 83 ec 08 sub [=13=]x8,%esp
80484d1: 83 ec 04 sub [=13=]x4,%esp
80484d4: 6a 0f push [=13=]xf
80484d6: 68 50 86 04 08 push [=13=]x8048650
80484db: 6a 01 push [=13=]x1
80484dd: e8 be fe ff ff call 80483a0 <write@plt>
80484e2: 83 c4 10 add [=13=]x10,%esp
80484e5: b8 00 00 00 00 mov [=13=]x0,%eax
80484ea: c9 leave
80484eb: c3 ret
# ...
这会提示几个重叠的问题。
- 为什么
clone
不会在堆栈的 24 到 583 字节之间出现段错误?
child
如何在堆栈太少的情况下悄无声息地失败?
- 堆栈 space 有什么用?
- 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
- Why doesn't clone segfault between 24 and 583 bytes of stack?
确实如此,但是因为它是一个单独的进程,所以您看不到它。在 24 之前,出现段错误的不是 child,而是试图设置 child 的 parent。尝试使用 strace -ff 来查看这种情况。
- How does child fail silently with too little stack?
当 child 死亡时,parent 会收到通知。本例中的 parent(执行 clone()
调用的那个)不会对此通知执行任何操作。它不是 "silent" 低于 24 的原因是因为那是 parent 死亡的时候,在这种情况下你的 shell 会收到通知。
- What is all that stack space used for?
- What is the significance of 24 and 584 bytes? How do they vary on different systems and implementations?
前 24 个(还有一点)用于设置对 child
的函数调用。因为它是一个普通函数,所以在完成时它会 return 到调用函数。这意味着 clone
必须设置一个调用函数 return 到(一个完全终止 child 的函数)。
584(还有一点)apparently 是调用函数的局部变量、您的函数、write
以及任何 write
调用所需的内存量.
我写“(和一点)”的原因是因为在 stack
之前可能有一点内存可用并被 clone
或 child
滥用,当 运行没房间了。尝试在克隆后添加 free(stack)
以查看该滥用的结果。
- Can I calculate a minimum stack requirement? Should I?
一般来说你可能不应该。它需要对您的功能和使用的外部功能进行非常深入的分析。就像 "normal" 程序一样,我建议使用默认值(如果我没记错的话,linux 上是 8MB)。只有当你对内存有严格要求(或堆栈溢出问题)时,你才应该开始担心这些事情。
我一直在摆弄 clone
调用,我注意到不同的子线程堆栈分配有三种不同的结果。下面的演示分配了一个 n 字节大的堆栈,其中 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 之间的样本都发生了段错误,对于 24 到 583 之间的所有样本,父级成功但子级保持沉默。高于 584 的任何合理值都会导致两者都成功。
反汇编表明 child
仅使用 16 个字节的堆栈 space,另外至少还有 16 个字节用于调用 write
。但这已经超过了停止段错误所需的 24 个字节。
$ objdump -d foo
# ...
080484cb <child>:
80484cb: 55 push %ebp
80484cc: 89 e5 mov %esp,%ebp
80484ce: 83 ec 08 sub [=13=]x8,%esp
80484d1: 83 ec 04 sub [=13=]x4,%esp
80484d4: 6a 0f push [=13=]xf
80484d6: 68 50 86 04 08 push [=13=]x8048650
80484db: 6a 01 push [=13=]x1
80484dd: e8 be fe ff ff call 80483a0 <write@plt>
80484e2: 83 c4 10 add [=13=]x10,%esp
80484e5: b8 00 00 00 00 mov [=13=]x0,%eax
80484ea: c9 leave
80484eb: c3 ret
# ...
这会提示几个重叠的问题。
- 为什么
clone
不会在堆栈的 24 到 583 字节之间出现段错误? child
如何在堆栈太少的情况下悄无声息地失败?- 堆栈 space 有什么用?
- 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
- Why doesn't clone segfault between 24 and 583 bytes of stack?
确实如此,但是因为它是一个单独的进程,所以您看不到它。在 24 之前,出现段错误的不是 child,而是试图设置 child 的 parent。尝试使用 strace -ff 来查看这种情况。
- How does child fail silently with too little stack?
当 child 死亡时,parent 会收到通知。本例中的 parent(执行 clone()
调用的那个)不会对此通知执行任何操作。它不是 "silent" 低于 24 的原因是因为那是 parent 死亡的时候,在这种情况下你的 shell 会收到通知。
- What is all that stack space used for?
- What is the significance of 24 and 584 bytes? How do they vary on different systems and implementations?
前 24 个(还有一点)用于设置对 child
的函数调用。因为它是一个普通函数,所以在完成时它会 return 到调用函数。这意味着 clone
必须设置一个调用函数 return 到(一个完全终止 child 的函数)。
584(还有一点)apparently 是调用函数的局部变量、您的函数、write
以及任何 write
调用所需的内存量.
我写“(和一点)”的原因是因为在 stack
之前可能有一点内存可用并被 clone
或 child
滥用,当 运行没房间了。尝试在克隆后添加 free(stack)
以查看该滥用的结果。
- Can I calculate a minimum stack requirement? Should I?
一般来说你可能不应该。它需要对您的功能和使用的外部功能进行非常深入的分析。就像 "normal" 程序一样,我建议使用默认值(如果我没记错的话,linux 上是 8MB)。只有当你对内存有严格要求(或堆栈溢出问题)时,你才应该开始担心这些事情。