2016-08-14 45 views
2

我一直在撥弄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 
+0

這是一個有趣的觀察,但一堆問題出現在我身上:什麼操作系統? CPU架構?編譯器? 32或64位?你有沒有嘗試過平臺上的細微差異?激進的差異?有趣的事實:30年前,我在Sun Microsystems C編譯器中發現了一個100%可重現的錯誤,*他們不相信我。*我說:「與錯誤消息交談。」 –

+0

@PeterRowell將'uname'輸出到問題中;謝謝。我沒有在任何其他系統上嘗試過。 – nebuch

回答

2
  • 爲什麼不段錯誤堆棧之間的24個583字節克隆?

這樣做,而是因爲它是一個獨立的過程中,你看不到它。 24歲之前,不是孩子,而是父母試圖建立孩子。嘗試使用strace -ff來看看發生了什麼。

  • 孩子如何失敗,默默無聞,堆疊太少?

當孩子死亡時,通知家長。這種情況下的父母(撥打clone()的那個人)對此通知不做任何處理。它不是24以下的「無聲」的原因是因爲這是父母死亡的時候,在這種情況下你的shell會得到通知。

  • 什麼是所有的堆棧空間用於?
  • 24和584字節的意義是什麼?它們在不同的系統和實現中有何不同?

第24(和位)用於建立所述函數調用child。因爲它是一個正常的函數,所以完成後它將返回到調用函數。這意味着clone必須設置一個調用函數返回(一個只是乾淨地終止孩子)。

584(和一點)顯然是調用函數,你的函數,write和任何write調用的局部變量所需的內存量。

我寫的原因「(和位)」,是因爲可能有一點點記憶stack可用和運行的空間時clonechild濫用了。嘗試在克隆後添加free(stack)以查看濫用的結果。

  • 我可以計算最小堆棧要求嗎?我是不是該?

一般來說你應該不會。它需要對您的功能和外部功能進行非常深入的分析。就像「正常」程序一樣,我會建議使用默認值(如果我記得正確的話,在Linux上是8MB)。只有當你有嚴格的內存要求(或堆棧溢出問題)時,你纔會開始擔心這些事情。

+0

調用'write'時'child'使用的大部分584字節?還有,爲什麼要花24個字節來設置'child'而不是16? – nebuch

+0

@nebuch可能;如你所示,孩子本身並沒有太多用處。雖然「調用函數」也可以做一些額外的調用,導致這種用法。人們將不得不進行更多的調試才能找到確切的結果。 – mweerden

+0

假設我確實有嚴格的內存要求,是否有從呼叫樹或類似的東西來計算最大堆棧深度的方法? – nebuch

相關問題