我一直在撥弄clone
調用,我注意到不同子線程堆棧分配的三種不同結果。下面的演示分配一個堆棧n-bytes大,其中n作爲參數傳遞,然後嘗試克隆。Linux克隆調用的最小堆棧大小?
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之間的樣本父母成功了,但孩子卻保持沉默。任何合理的584以上的事情都會導致成功。
反彙編表明child
只使用16個字節的堆棧空間,再加上至少16個更多來呼叫write
。但是,這已經超過了停止違規所需的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 <[email protected]>
80484e2: 83 c4 10 add $0x10,%esp
80484e5: b8 00 00 00 00 mov $0x0,%eax
80484ea: c9 leave
80484eb: c3 ret
# ...
這會提示幾個重疊的問題。
- 爲什麼
clone
segfault在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
這是一個有趣的觀察,但一堆問題出現在我身上:什麼操作系統? CPU架構?編譯器? 32或64位?你有沒有嘗試過平臺上的細微差異?激進的差異?有趣的事實:30年前,我在Sun Microsystems C編譯器中發現了一個100%可重現的錯誤,*他們不相信我。*我說:「與錯誤消息交談。」 –
@PeterRowell將'uname'輸出到問題中;謝謝。我沒有在任何其他系統上嘗試過。 – nebuch