如何在Linux pthreads中设置线程名称?

67

有没有办法在 Linux 中设置线程的名称?

我的主要目的是在调试时更加方便,并且如果该名称通过例如 /proc/$PID/task/$TID/... 公开,那就更好了。


1
请问您能否给出一些示例,解释名称在调试中如何发挥作用? - user184968
10
为了更容易地识别这个线程,你是这样做的吗? - Aaron Digulla
2
线程名称在处理具有大量不同线程的程序时非常有帮助,每个线程都执行特定的任务(例如管道设置,其中每个线程对每个数据包执行某些处理任务)。我已经看到了这种需求。具有操作系统感知能力的良好调试工具也应该能够显示这些名称,目前还不清楚有多少调试器可以做到这一点。 - jakobengblom2
1
如何在Linux中为线程命名 - user9876
可能有更好的解决方案,因为默认函数不能反映线程在/proc/self/comm文件中的名称。或者它会复制上次设置的名称,这与没有名称一样无用。直接写入comm文件是允许的,并且对于所有工具(如htop和ps)都可以正常工作。 - Alexis Wilke
3个回答

126

从glibc v2.12开始,您可以使用pthread_setname_nppthread_getname_np来设置/获取线程名称。

这些接口在其他一些POSIX系统(BSD、QNX、Mac)上也有提供,但形式略有不同。

设置线程名称的方法如下:

#include <pthread.h>  // or maybe <pthread_np.h> for some OSes

// Linux
int pthread_setname_np(pthread_t thread, const char *name);

// NetBSD: name + arg work like printf(name, arg)
int pthread_setname_np(pthread_t thread, const char *name, void *arg);

// FreeBSD & OpenBSD: function name is slightly different, and has no return value
void pthread_set_name_np(pthread_t tid, const char *name);

// Mac OS X: must be set from within the thread (can't specify thread ID)
int pthread_setname_np(const char*);

而且你可以重新获取名称:

#include <pthread.h>  // or <pthread_np.h> ?

// Linux, NetBSD:
int pthread_getname_np(pthread_t th, char *buf, size_t len);
// some implementations don't have a safe buffer (see MKS/IBM below)
int pthread_getname_np(pthread_t thread, const char **name);
int pthread_getname_np(pthread_t thread, char *name);

// FreeBSD & OpenBSD: dont' seem to have getname/get_name equivalent?
// but I'd imagine there's some other mechanism to read it directly for say gdb

// Mac OS X:
int pthread_getname_np(pthread_t, char*, size_t);

正如您所看到的,它在 POSIX 系统之间并不完全可移植,但据我所知,在 Linux 上应该是一致的。除了 Mac OS X(在其中只能从线程内部执行),其他系统至少都很容易适应跨平台代码。

来源:


1
如果无法检索名称,那么在BSD上使用pthread_set_name_np()的意义是什么? - kralyk
@kralyk,是的,它在应用程序中并不是很有用,但我怀疑可以通过其他机制检索它(例如,通过gdb直接从内存中读取?)...顺便说一下,NetBSD与Linux接口兼容(并稍微扩展了printf类似的用法)。 - drfrogsplat
@drfrogsplat - 常量定义名称的最大长度在哪里?我没有看到任何相关文档。 - HEKTO
3
@kralyk 我认为在使用gdb进行调试或者在top -H中会很有用。 - Thirupathi Thangavel
我正在查看NetBSD关于pthread_setname_np()的文档,并对其中一个参数感到困惑。你能帮忙解释一下void * arg参数代表什么吗?我知道它是一个指针,但不确定这里的"argument used with name"是什么意思。我可以传递nullptr吗?为什么可以或者为什么不可以? - undefined

38

使用prctl(2)函数,并选择选项PR_SET_NAME(请参阅文档)。请注意,旧版本的文档有点令人困惑。它们说:

设置调用进程的进程名称

但是在Linux上,线程是轻量级进程(LWP),因此在这种情况下,一个线程就是一个进程。

您可以使用ps -o cmd或以下命令查看线程名称:

cat /proc/$PID/task/$TID/comm

或者在cat /proc/$PID/task/$TID/stat()之间:

4223 (kjournald) S 1 1 1 0...

或者在 GDB 中用双引号括起来的 info threads 命令:

* 1    Thread 0x7ffff7fc7700 (LWP 6575) "kjournald" 0x00007ffff78bc30d in nanosleep () at ../sysdeps/unix/syscall-template.S:84                                                                                  

3
请注意,实际的线程名称将在/proc/$PID/tasks/$TID/stat中。 - nos
即使在/proc目录列表中看不到它们,线程仍可通过/proc/$TID访问(因为基本上与进程相同)。 - ephemient
1
@nos,至少在内核版本3.2.0中,它是/proc/$PID/task/$TID/stat(tasks上没有s)。 - Andrew Wagner
2
我会把它留在这里以保证完整性,更改主线程名称可能存在潜在问题:https://lists.linuxfoundation.org/pipermail/bugme-new/2008-October/020065.html - bobah
尽管线程在 /proc/ 目录清单中未列出,但如果你访问 /proc/$TID/,仍然可以获取线程的信息。 - Craig McQueen

6
您可以通过创建一个将 pthread_t 映射到 std::string 的字典,并将 pthread_self() 的结果与您想要分配给当前线程的名称相关联,来自行实现此操作。请注意,如果这样做,您需要使用互斥锁或其他同步原语来防止多个线程同时修改字典(除非您的字典实现已经为您完成此操作)。您也可以使用线程特定变量(请参见pthread_key_createpthread_setspecificpthread_getspecificpthread_key_delete),以保存当前线程的名称;但是,如果这样做,您将无法访问其他线程的名称(而使用字典,则可以从任何线程迭代所有线程ID/名称对)。

3
请注意,此解决方案可在Linux、Mac OS X和符合单一UNIX规范的所有系统中使用(使用"prctl"的建议不可移植)。 - Michael Aaron Safyan
2
这似乎是一个非常糟糕的想法,人们不应该这样做。 - Matt Joiner
3
你想知道如何在APP外部查看它?例如在ps或top中? - elcuco
这个评论有点过时了。现在已经跨平台支持,因此dfrogsplat的回答是最好的。 - Michael Aaron Safyan
+1 - 在嵌入式Linux中,即使使用过时或异乎寻常的C库仍然有用。 - FooF
1
在 C 语言中,现在有 _Thread_local(自 C11 起)。这样编译器就可以为您完成繁重的工作。还有一个 thread_local 宏(使用与 C++11 相同的名称) - Alexis Wilke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接