【问题标题】:get pthread_t from thread id从线程 id 获取 pthread_t
【发布时间】:2017-09-20 13:31:50
【问题描述】:

我找不到将线程 ID (pid_t) 转换为 pthread_t 的函数,这将允许我调用 pthread_cancel()pthread_kill()

即使 pthreads 不提供一个,是否有特定于 Linux 的功能? 我认为不存在这样的功能,但我很乐意得到纠正。

背景

我很清楚,通常最好让线程通过条件变量等来管理自己的生命周期。

此用途用于测试目的。我正在尝试找到一种方法来测试应用程序在其中一个线程“死亡”时的行为。所以我真的在寻找一种方法来杀死一个线程。使用syscall(tgkill()) 会杀死进程,因此我提供了一种方法让测试人员为进程提供要杀死的线程的ID。我现在需要将该 id 转换为 pthread_t 以便我可以:

  • 使用pthread_kill(tid,0) 检查其是否存在,然后
  • 酌情致电pthread_kill()pthread_cancel()

这可能将测试带到了不必要的极端。如果我真的想这样做,那么某种模拟 pthreads 实现可能会更好。

确实,如果您真的想要强大的隔离,您通常最好使用进程而不是线程。

【问题讨论】:

  • 我不认为你可以。无论如何,这可能会对您有所帮助unix.stackexchange.com/questions/1066/…
  • pthread_kill 不会杀死线程,它会发送一个信号(它可以关闭整个进程)。无论如何,你到底为什么还要处理内核线程ID?他们甚至没有被 glibc 暴露。
  • Pthread_t 是不透明的,因此我无法指定要终止的特定线程。但是, ps 或 top -H 可以从命令行显示线程 ID。您可以设置信号掩码,以便信号仅发送到特定线程。在这种情况下 pthread_kill() 可能会被安全地处理。

标签: linux pthreads


【解决方案1】:

我认为不存在这样的功能,但我很乐意得到纠正。

作为一种解决方法,我可以创建一个将&pthread_t 映射到pid_t 的表,并确保我始终通过一个向该表添加条目的包装器调用pthread_create()。这非常有效,允许我将 OS 线程 ID 转换为 pthread_t,然后我可以使用 pthread_cancel() 终止它。这是机制的sn-p:

typedef void* (*threadFunc)(void*);

static void* start_thread(void* arg)
{
  threadFunc threadRoutine = routine_to_start;
  record_thread_start(pthread_self(),syscall(SYS_gettid));
  routine_to_start = NULL; //let creating thread know its safe to continue
  return threadRoutine(arg);
}

【讨论】:

    【解决方案2】:

    明智的转换要求在 pthread_t 和 pid_t tid 之间存在 1:1 映射,NPTL 就是这种情况,但并非总是如此,而且并非每个 pthread 平台都如此。那就是……

    两种选择:

    A) 使用LD_PRELOADdlsym 覆盖实际的pthread_create,并在那里跟踪每个pthread_t 及其对应的pid_t。要获取线程pid_t,您可以利用 pthread 私有标头对 pthread_t 进行不透明处理并访问其中的 pid_t,或者如果您想坚持使用记录在案的 API pthread_sigqueue 每个 pthread_t 线程原样创建并有一个sigaction 信号处理程序调用gettid 并将线程pid_t 传回给您,并在您的新pthread_create 和信号处理程序[1] 之间进行适当的同步。

    B) 您可以从/proc/<process pid_t>/task/ 中读取pid_t 的所有线程。然后使用SYS_rt_tgsigqueueinfo[2] syscall 实现一个新函数thread_sigqueuepthread_sigqueue 的 pid_t 变体,以便您可以向pid_t 线程发出信号,并从sigaction 信号处理程序调用@987654341 @ 以适当的同步等方式传递值。

    注意事项:

    1 - 我认为值得编写 2 个executeOnThread 变体(一个用于pthread_t,一个用于pid_t 样式线程ID)采用std::function<void()>(用于C++),或者一个void(*)(void*) 函数指针和void* 参数(对于C),以及SIGUSR1 那个线程来执行sigaction 中传递的函数,您还设置它来执行与调用线程的相关同步。能够使用 pthread_self、gettid、backtrace、getrusage 等与线程相关的 API 非常方便,而无需每次都设计自定义执行方案。

    2 - SYS_rt_tgsigqueueinfo 是一个低级系统调用,用于实现 sigqueue/pthread_sigqueue,而不是应用程序使用,但仍然是一个记录在案的 API,我们使用它来实现sigqueue 的另一个变体,所以公平游戏恕我直言。

    【讨论】:

    • 一些有趣的想法,但我认为我的简单粗暴的方法更有效。
    • 当然,对于许多可能具有许多不同依赖项的遗留项目来说,拥有一个通用的线程创建功能是不可行的,尽管有一些二进制文件。
    • 线程的线程 ID 不提供有关该线程对程序的重要性的信息——它的目的是什么,它操作什么数据,它与什么同步对象交互——所以这个问题首先是没有实际意义的。除非程序已经在跟踪线程元数据,否则搜索的信息几乎没有安全使用,在这种情况下,它应该只包含被跟踪数据之间的所需映射。
    • 或者,传统的方法是根本不做。程序通常通过其pthread_t 值(通常称为“线程标识符”,但该问题使用相互冲突的术语)来跟踪线程。内核的线程 ID 号很少被用户空间程序使用。
    • getrusage 和 backtrace 是安全信息。 /proc 暴露 pid_t 而不是 pthread_t 有时您必须处理给定的内容(pid_t tid)
    猜你喜欢
    • 2010-10-08
    • 2011-01-12
    • 2010-12-13
    • 1970-01-01
    • 2017-05-21
    • 1970-01-01
    • 2011-03-18
    • 2012-12-14
    • 1970-01-01
    相关资源
    最近更新 更多