首页 > 解决方案 > 如何在 Linux 上实现 pthread_detach?

问题描述

pthread_detach标记一个线程,以便当它终止时,它的资源会自动释放,而不需要父线程调用pthread_join. 它怎么能做到这一点?特别是从 Linux 的角度来看,有两个资源我特别好奇:

  1. 作为一个实现细节,我希望如果wait没有在终止的线程上执行系统调用,那么该线程将成为僵尸。我假设 pthread 库对这个问题的解决方案不涉及,因为(我认为)它仍然可以工作,无论程序指定在收到SIGCHLD时发生什么操作。SIGCHLD
  2. 线程是使用clone系统调用创建的。调用者在调用之前必须分配内存作为子线程的栈区clone在 Stack Overflow 的其他地方,建议调用者使用mmap为孩子分配堆栈。线程退出后如何取消映射堆栈?

在我看来,pthread_detach必须以某种方式为这两个问题提供解决方案,否则,产生和分离许多线程的程序最终将失去继续产生新线程的能力,即使分离的线程可能已经终止。

标签: linuxpthreads

解决方案


pthreads 库(在 Linux 上,NPTL)提供了一个围绕较低级别原语的包装器,例如clone(2). 当使用创建线程时pthread_create,传递给的函数clone是包装函数。该函数分配堆栈并将该信息以及任何其他元数据存储到一个结构中,然后调用用户提供的启动函数。当用户提供的启动函数返回时,就会进行清理。最后,调用内部函数__exit_thread进行系统调用以退出线程。

当这样一个线程被分离时,它仍然从用户提供的启动函数返回并像以前一样调用清理代码,除了堆栈和元数据作为其中的一部分被释放,因为没有人在等待这个线程完成。这通常由pthread_join.

如果线程在没有运行的情况下被杀死或退出,则清理由下一个pthread_create调用处理,该调用将调用任何尚未运行的清理处理程序。

aSIGCHLD没有发送给父级也不是wait(2)必需的原因是因为使用了CLONE_THREAD标志 to clone(2)。手册页对此标志进行了以下说明:

使用 CLONE_THREAD 创建的新线程与进行克隆调用的进程具有相同的父进程(即,如 CLONE_PARENT),因此对 getppid(2) 的调用为线程组中的所有线程返回相同的值。当 CLONE_THREAD 线程终止时,创建它的线程不会发送 SIGCHLD(或其他终止)信号;使用wait(2) 也无法获取此类线程的状态。(据说线程是分离的。)

正如您所指出的,这是发生预期 POSIX 语义所必需的。


推荐阅读