我正在尝试监控线程的堆栈使用情况。要做到这一点,我需要知道线程堆栈的地址,我发现这样做的唯一方法是使用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
吗?这将在没有虚拟/交换内存的低资源设备上运行。
第二个问题,当线程死亡时,这个内存会自动释放吗?如果你不确定,有没有办法找出它是否已被释放?
答案 0 :(得分:7)
“监视器”是什么意思?如果您只是想确保堆栈没有浪费太多空间(这会阻止您在32位系统或具有低ram +交换的系统上拥有大量线程),您应该只使用{{1} }而不是pthread_attr_setstacksize
。这样您就不负责自己分配堆栈。如果你担心线程会在堆栈上一次分配多个页面,你也可以选择使用pthread_attr_setstack
来确保更大的防护页面区域作为保护,但要注意这将消耗你的虚拟地址空间。
如果您真的想衡量堆栈使用情况,pthread_attr_setguardsize
可能是正确的工具,但它根本不是直截了当的。我将使用pthread_attr_setstack
分配内存,并将其设置为只读。然后安装一个mmap
处理程序到SIGSEGV
可写的错误页面,递增一个计数器,然后返回。这将为您计算线程接触的实际页面数。并且由于信号处理程序将在错误线程中运行(这是保证的,因为它是同步信号),您可以将计数保存在线程本地存储变量中以执行多线程上的计数。
你可能实际上需要在调用mprotect
之前使最后一页或两页写入,因为第一次写入尝试可能会发生在父线程上,你可能不希望信号处理程序在那里运行如果您尝试将结果存储在线程本地存储中。
最后访问您的具体问题:
您不希望pthread_create
。该标志用于将在进程之间共享的内存。在你的情况下它可能不会受到伤害,但它会产生误导。使用MAP_SHARED
。
内存不会被释放,而且正式地,永远不会被释放。 POSIX非常明确地指出,为了永远重用或释放给予线程的堆栈,它是未定义的行为,因为你无法可靠地确定生命周期(即使在MAP_PRIVATE
返回之后,它在概念上可能仍然在执行其最后几条指令退出并因此仍然触及堆栈,并且它可能会像无限长的那样保持停滞状态。我相信这是glibc / NPTL上的不可能,因为他们在线程退出时使用内核生成的futex唤醒事件以线程退出来原子地发出信号pthread_join
,但是NPTL可以缓存并重用你捐赠给这样一个线程的堆栈(因为你不能自己重用/释放它们)。确保你必须检查来源。因此,我建议在生产代码中使用pthread_join
NOT 。使用pthread_attr_setstack
。 pthread_attr_setstacksize
应仅用于开发时间的黑客攻击,例如您现在可能正在做的事情。