【问题标题】:How does a thread in NPTL exit?NPTL 中的线程如何退出?
【发布时间】:2023-03-20 20:19:01
【问题描述】:

我很好奇单个 NPTL 线程是如何退出的,从实现的角度来看。

我对glibc-2.30的实现的理解是:

  1. NPTL 线程建立在 Linux 上的轻量级进程之上,附加信息存储在用户堆栈的 pthread 对象中,以跟踪 NPTL 特定信息,例如加入/分离状态和返回的对象指针。
  2. 当一个 NPTL 线程完成后,它就永远消失了,只有用户堆栈(以及因此)pthread 对象被收集(由其他线程加入),除非它是分离的,在这种情况下空间被直接释放。
  3. _exit() syscall 杀死线程组中的所有线程。
  4. pthread_create() 传入的用户函数实际上被包装到另一个函数start_thread() 中,该函数在运行用户函数之前做了一些准备工作,然后进行了一些清理工作。

问题是:

  1. 包装函数start_thread()的末尾有如下注释和代码:

    /* We cannot call '_exit' here.  '_exit' will terminate the process.
    
     The 'exit' implementation in the kernel will signal when the
     process is really dead since 'clone' got passed the CLONE_CHILD_CLEARTID
     flag.  The 'tid' field in the TCB will be set to zero.
    
     The exit code is zero since in case all threads exit by calling
     'pthread_exit' the exit status must be 0 (zero).  */
     __exit_thread ();
    

    __exit_thread() 似乎在做系统调用_exit() 反正:

     static inline void __attribute__ ((noreturn, always_inline, unused))
     __exit_thread (void)
     {
       /* some comments here */
       while (1)
         {
           INTERNAL_SYSCALL_DECL (err);
           INTERNAL_SYSCALL (exit, err, 1, 0);
         }
     }
    

    所以我在这里感到困惑,因为它不应该真正执行系统调用_exit(),因为它会终止所有线程。

  2. pthread_exit() 应该终止一个线程,所以它应该做一些类似于包装器start_thread() 最后所做的事情,但是它调用__do_cancel(),TBH 我在追踪那个函数时迷失了方向。好像和上面的__exit_thread()没有关系,也不叫_exit()

【问题讨论】:

    标签: c linux-kernel pthreads system-calls glibc


    【解决方案1】:

    我在这里很困惑,因为它不应该真正执行 syscall _exit()

    这里的混淆源于将exit 系统调用与_exit libc 例程混合(Linux 上没有_exit 系统调用)。

    前者终止当前的 Linux 线程(如预期的那样)。

    后者(令人困惑)不执行exit 系统调用。相反,它执行exit_group 系统调用,终止所有个线程。

    thread_exit() 应该终止单个线程

    它确实是间接的。它展开当前堆栈(类似于siglongjmp),将控制转移到设置cleanup_jmp_buf 的位置。那是在start_thread

    控制权转移后,start_thread清理资源,调用__exit_thread实际终止线程。

    【讨论】:

    • 这说明了一切,谢谢!我重新访问了手册页man 2 _exit,注释部分显示“在 glibc 2.3 之前的版本中,_exit() 包装函数调用了同名的内核系统调用。从 glibc 2.3 开始,包装函数调用exit_group(2),以终止进程中的所有线程。 (原始的_exit() 系统调用仅终止调用线程。)'。这是一个过时的描述,还是在 glibc 2.3 之前有一个名为 _exit 的系统调用? man 第 2 节中有一个 _exit 页面这一事实似乎有点奇怪。
    • @QnA 我同意:我认为 _exit 应该移到第 3 部分,因为它描述了 libc 函数而不是系统调用。随时提交错误。
    猜你喜欢
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 2012-06-11
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多