美文网首页
Linux 平台 C/C++ 代码中设置线程名

Linux 平台 C/C++ 代码中设置线程名

作者: hanpfei | 来源:发表于2021-12-29 09:49 被阅读0次

一般来说,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 文件中写入线程名来实现。

相关文章

网友评论

      本文标题:Linux 平台 C/C++ 代码中设置线程名

      本文链接:https://www.haomeiwen.com/subject/gyemqrtx.html