3

我正在尝试监视我的线程的堆栈使用情况。为此,我需要知道线程堆栈的地址,而我发现这样做的唯一方法是使用pthread_attr_setstack().

我目前正在使用 mmap 来分配内存:

   pthread_attr_t ptAttr;
   pthread_t pth;
   pthread_attr_init(&ptAttr);
   void *stack = mmap(NULL, stksize, PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
   pthread_attr_setstack(&ptAttr, stack, stksize);
   pthread_create(&pth,&ptAttr,threadFunc,&info);

所以第一个问题,这是分配内存的好方法mmap吗?标志是否正确?我应该malloc改用吗?这将在没有虚拟/交换内存的低资源设备上运行。

第二个问题,这个内存会在线程死掉的时候自动释放吗?如果您不确定,有没有办法查明它是否已发布?

4

1 回答 1

7

“监控”是什么意思?如果您只想确保不会为堆栈浪费太多空间(这会阻止您在 32 位系统或具有低 ram+swap 的系统上拥有大量线程),您应该简单地使用pthread_attr_setstacksize函数而不是pthread_attr_setstack. 这样,您无需自己负责分配堆栈。pthread_attr_setguardsize如果您担心线程一次会在堆栈上分配多个页面,您还可以选择使用以确保更大的保护页面区域作为保护,但请注意这会消耗您的虚拟地址空间。

如果您真的想测量堆栈使用情况,pthread_attr_setstack可能是正确的工具,但它一点也不简单。我会用 分配内存mmap,并将其设为只读。然后将SIGSEGV处理程序安装到mprotect可写的错误页面,增加一个计数器,然后返回。这将为您提供线程接触的实际页面数的计数。而且由于信号处理程序将在故障线程中运行(这是保证的,因为它是一个同步信号),您可以将计数保存在线程本地存储变量中以在多个线程上执行计数。

但是,您实际上可能需要在调用之前使最后一页或两页可写pthread_create,因为第一次写入尝试可能会发生在父线程中,并且如果您尝试存储信号处理程序,您可能不希望信号处理程序在那里运行导致线程本地存储。

最后访问您的具体问题:

  1. 你不想MAP_SHARED。该标志用于将在进程之间共享的内存。在你的情况下它可能不会受到伤害,但它会产生误导。使用MAP_PRIVATE.

  2. 内存不会被释放,而且形式上永远不会被释放。POSIX 非常明确地指出,重用或释放给线程的堆栈是未定义的行为,因为您无法可靠地确定生存期(即使在pthread_join返回之后,从概念上讲,线程仍有可能执行其最后几条退出指令,因此仍然触摸堆栈,并且它可能会无限期地保持停滞)。我相信这在 glibc/NPTL 上是不可能的,因为他们在线程退出时使用内核生成的 futex 唤醒事件来发出信号pthread_join以原子方式与线程退出,但 NPTL 可能会缓存和重用您捐赠给这样的线程的堆栈(因为无论如何您都不允许重用/释放它们)。为了确保您必须检查来源。因此,我建议不要pthread_attr_setstack在生产代码中使用。使用pthread_attr_setstacksize. pthread_attr_setstack应该只用于开发时的黑客攻击,比如你现在可能正在做的事情。

于 2012-04-26T14:44:11.780 回答