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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Linux 平台使用 C++ 语言多线程实现串口 A 和串口 B 的实时互发数据,你可以按照以下步骤进行操作: 1. 首先,确保已正确连接串口 A 和串口 B 到计算机。 2. 引入相关的头文件,包括 `<iostream>`、`<thread>`、`<chrono>`、`<fcntl.h>`、`<unistd.h>`、`<termios.h>` 等。 3. 在主线程打开串口 A 和串口 B,并配置其参数。可以使用 `open()` 函数打开串口,然后使用 `tcgetattr()` 和 `tcsetattr()` 函数配置串口的波特率、数据位、停止位、校验位等参数。 4. 创建两个新的线程,分别用于串口 A 和串口 B 的实时收发数据。可以使用 `std::thread` 类来创建线程,并定义两个线程函数来处理串口的数据收发操作。 5. 在串口 A 的线程函数,使用 `read()` 函数从串口 A 读取数据,并使用 `write()` 函数将读取到的数据发送到串口 B。 6. 在串口 B 的线程函数,使用 `read()` 函数从串口 B 读取数据,并使用 `write()` 函数将读取到的数据发送到串口 A。 7. 在主线程,等待两个线程结束并关闭串口 A 和串口 B。可以使用 `std::thread::join()` 函数来等待线程结束,然后使用 `close()` 函数关闭串口。 以下是一个简单的示例代码,演示了如何在 Linux 平台使用 C++ 语言多线程实现串口 A 和串口 B 的实时互发数据: ```cpp #include <iostream> #include <thread> #include <chrono> #include <fcntl.h> #include <unistd.h> #include <termios.h> void serialThread(int fdRead, int fdWrite) { char buffer[256]; ssize_t bytesRead; while (true) { bytesRead = read(fdRead, buffer, sizeof(buffer)); if (bytesRead > 0) { // 发送数据到另一个串口 write(fdWrite, buffer, bytesRead); } else if (bytesRead < 0) { std::cerr << "读取串口数据时发生错误" << std::endl; break; } // 模拟实时处理数据的延迟 std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } int main() { const std::string serialPortA = "/dev/ttyUSB0"; // 串口设备A路径 const std::string serialPortB = "/dev/ttyUSB1"; // 串口设备B路径 int fdA = open(serialPortA.c_str(), O_RDWR | O_NOCTTY); if (fdA == -1) { std::cerr << "无法打开串口设备A: " << serialPortA << std::endl; return 1; } int fdB = open(serialPortB.c_str(), O_RDWR | O_NOCTTY); if (fdB == -1) { std::cerr << "无法打开串口设备B: " << serialPortB << std::endl; close(fdA); return 1; } struct termios ttyA, ttyB; tcgetattr(fdA, &ttyA); tcgetattr(fdB, &ttyB); // 配置串口A的参数 // ... // 配置串口B的参数 // ... tcsetattr(fdA, TCSANOW, &ttyA); tcsetattr(fdB, TCSANOW, &ttyB); std::thread threadA(serialThread, fdA, fdB); std::thread threadB(serialThread, fdB, fdA); // 主线程等待两个线程结束 threadA.join(); threadB.join(); close(fdA); close(fdB); return 0; } ``` 在这个示例,定义了两个串口设备的路径 `serialPortA` 和 `serialPortB`。请根据实际情况修改这些路径。 然后,代码打开了串口设备 A 和串口设备 B,并对它们进行了配置,包括波特率、数据位、停止位、校验位等。 接下来,创建了两个线程 `threadA` 和 `threadB`,分别用于串口 A 和串口 B 的实时收发数据。在线程函数 `serialThread` ,通过循环从一个串口读取数据,并将数据发送到另一个串口。 最后,在主线程,等待两个线程结束,并关闭串口 A 和串口 B。 请根据实际需求修改示例代码的串口设备路径和数据处理逻辑,以满足你的需求。同样,需要注意的是,这只是一个基本的示例代码,可能需要根据具体情况进行修改和扩展,如添加异常处理、超时机制等,以增强代码的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值