簡體   English   中英

線程堆棧溢出

[英]Thread stack overflow

在像我們創建任務的vxworks這樣的RTO中,指定了stacksize。 我們可以在C中編寫一個例程來檢查堆棧是否溢出或者沒有執行任務嗎?

看看你的編譯器,他們經常讓你添加prelude函數來做這個,或者他們甚至可以自己檢查它,除非你操作堆棧指針寄存器。

並檢查操作系統是否允許您安裝“guard-pages”。 將線程堆棧中的最后一頁標記為非讀取/非寫入並捕獲SIGSEGV信號並使用特定於OS / CPU的方式來確定它是否是失敗的保護頁面。 為此,您必須確保函數的堆棧幀(堆棧傳遞參數,局部變量和alloca分配的空間)始終小於頁面大小,否則您可以跳過“guard-page”這是最好的方法處理它,因為它在正常處理期間沒有運行時開銷。

你會看到這種高度的OS / CPU /編譯器依賴。 但我很確定谷歌會為所有系統找到可用的代碼和幫助程序,因為它是低級程序員(例如運行時或解釋器實現者)的一種非常常見的技術。

如果你知道堆棧有多大,如果你小心,那么是(但不是便攜)。 如果沒有其他方法可以獲得堆棧的基址,則需要在線程的main函數中記錄堆棧變量的地址; 這使您可以近似堆棧的頂部。 然后,在您的檢查功能中,您獲取局部變量的地址; 這給了你堆棧的底部。 如果頂部和底部之間的差異與您的堆棧大小有關,那么現在是時候擔心; 如果差異大於堆棧大小,則擔心已經太晚了 - 損壞已經完成(但現在你需要考慮如何清理)。

僅供參考,您可以使用checkStack()從VxWorks中的shell執行類似的操作。

您可以使用一些技術 - 通常您有一個低優先級的任務,每隔一秒左右嗅探所有其他任務的堆棧狀態。

a:在任務開始之前,確保堆棧空間已填充已知模式。 然后,您可以通過檢查模式找出剩余多少“未損壞的”堆棧。

  • 優點:讓您檢查堆棧使用的“高水印”。
  • 缺點:如果你分配堆棧內存,但由於某種原因不寫入它,這種技術可能無法檢測到溢出。

b:你可以簡單地嗅探所有其他線程的堆棧指針。

  • 缺點:這只是“采樣”堆棧指針,因此可能不會注意到溢出的短暫情況
  • 優點:快速簡便。

我建議兩者結合使用。 因為你正在使用像VxWorks TaskInfoGet()函數這樣的低級東西,所以很難使它甚至可以遠程移植。

我不知道VxWorks,但我的回憶是Green Hill的Velosity / uVelosity內核提供了執行此操作的代碼。 即使他們沒有,因為它們提供了用戶可以修改的源,並且基礎設施就在那里,所以很容易添加。

編輯:為了披露,我和他們一起做了一個夏季實習,將uVelosity移植到一個新的架構。 這就是我對線程堆棧處理的親密關系。

如果您的特定應用程序靜態分配其線程,則可以將其堆棧放置在靜態定義的區域中,並使用鏈接器映射將符號放置在這些區域的末尾。 然后,您只需要獲取當前的堆棧指針(如其他答案中所述),並將“堆棧段結束”指針與該地址進行比較。 如果每個線程都有一些位置來存儲作為其堆棧末尾提供給它的地址,那么這也適用於動態分配。

堆棧大小默認為1MB,具體取決於編譯器。 根據這些信息,您可以嘗試使用以下內容捕獲剩余的堆棧:


unsigned long remaining_stack_size() {
    char dummy;
    return 0x000fffff & (unsigned long)&dummy;
    // 0x000fffff is 1MB -1 (1048576 -1)
}

編輯:請注意,它實際上返回當前堆棧位置,這是相同的事情。

編輯(2):對於那些說我錯了的人,這里有一個概念證明:


#include <stdio.h>
#include <windows.h>

unsigned long remaining_stack_size() {
    char dummy;
    return 0x001fffff & (unsigned long)&dummy + 1; // okay, some minor adjusts
}

void recurse_to_death(unsigned long used, char *p) {
    char buf[32*1024];
    used += 32*1024;
    printf("Used: 0x%08x Remaining: 0x%08x\n", used, remaining_stack_size());
    recurse_to_death(used, buf);
}

DWORD WINAPI my_thread(void *p) {
    printf("Total stack size of this Thread: 0x%08x bytes\n", remaining_stack_size() + 72);
    recurse_to_death(0, NULL);
    return 0;
}

int main(int argc, char *argv) {
    DWORD tid;
    // CreateThread's stack size actually defaults to 1MB+64KB and does not honor lower values
    CreateThread(NULL, NULL, my_thread, NULL, NULL, NULL);
    Sleep(30000);
    return 0;
}

remaining_stack_size()預測堆棧溢出完美。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM