一般来说,Linux 平台的 C/C++ 程序可以用 prctl()
或 pthreads 的 pthread_setname_np()
接口为一个线程设置线程名。prctl()
可以用于为当前线程设置线程名,pthread_setname_np()
则可以用于为当前进程的任意线程设置线程名。
prctl()
的函数声明如下:
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
pthread_setname_np()
的函数声明如下:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <pthread.h>
int pthread_setname_np(pthread_t thread, const char *name);
int pthread_getname_np(pthread_t thread,
char *name, size_t len);
如果想要通过 prctl()
为其它线程设置线程名,一般需要先将线程名放在某个地方,然后在目标线程中拿到线程名并设下去。最常见的还是,在线程启动之前准备好线程名,新线程启动之后,立即设置线程名。比如,像下面这样:
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <pthread.h>
char *thread_name1 = nullptr;
char *thread_name2 = nullptr;
void* thread1(void* arg) {
prctl(PR_SET_NAME, thread_name1);
while (1) {
printf("thread1\n");
sleep(1000);
}
}
void* thread2(void* arg) {
while (1) {
printf("thread2\n");
sleep(1000);
}
}
int main() {
pthread_t th1, th2;
void* retval = NULL;
thread_name1 = "THREAD1";
pthread_create(&th1, NULL, thread1, NULL);
pthread_create(&th2, NULL, thread2, NULL);
printf("main thread\n");
pthread_join(th1, &retval);
pthread_join(th2, &retval);
}
曾经项目中遇到过一个设置线程名不生效的问题,最终同事查出来是因为设置的线程名太长导致的。
glibc 的 pthread_setname_np()
函数实现 (glibc 版本 2.34,代码位于 glibc-2.34/nptl/pthread_setname.c
) 如下:
/* pthread_setname_np -- Set thread name. Linux version
Copyright (C) 2010-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, see <https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <fcntl.h>
#include <pthreadP.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <not-cancel.h>
int
__pthread_setname_np (pthread_t th, const char *name)
{
const struct pthread *pd = (const struct pthread *) th;
/* Unfortunately the kernel headers do not export the TASK_COMM_LEN
macro. So we have to define it here. */
#define TASK_COMM_LEN 16
size_t name_len = strlen (name);
if (name_len >= TASK_COMM_LEN)
return ERANGE;
if (pd == THREAD_SELF)
return __prctl (PR_SET_NAME, name) ? errno : 0;
#define FMT "/proc/self/task/%u/comm"
char fname[sizeof (FMT) + 8];
sprintf (fname, FMT, (unsigned int) pd->tid);
int fd = __open64_nocancel (fname, O_RDWR);
if (fd == -1)
return errno;
int res = 0;
ssize_t n = TEMP_FAILURE_RETRY (__write_nocancel (fd, name, name_len));
if (n < 0)
res = errno;
else if (n != name_len)
res = EIO;
__close_nocancel_nostatus (fd);
return res;
}
versioned_symbol (libc, __pthread_setname_np, pthread_setname_np,
GLIBC_2_34);
#if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_12, GLIBC_2_34)
compat_symbol (libpthread,__pthread_setname_np, pthread_setname_np,
GLIBC_2_12);
#endif
可以看到,当设置的线程名长度超过 16 个字符时,直接返回失败。当通过 pthread_setname_np()
为当前线程设置线程名时,通过调用 prctl()
实现,当给其它线程设置线程名时,则通过向 procfs 文件系统中,线程的 comm
文件中写入线程名来实现。
网友评论