繁体   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