• 线程 ID


    摘自《Linux 环境编程:从应用到内核》

      在 Linux 中,目前的线程实现是 Native POSIX Thread Library,简称 NPTL。在这种实现下,线程又被称为轻量级进程(Light Weighted Process),每一个用户态的线程,在内核中都有一个调度实体,也拥有自己的进程描述符。

    对于进程,可以使用下面的系统调用,获取进程 ID

    pid_t getpid(void);

    如:

     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <unistd.h>
     4 
     5 int main()
     6 {
     7     pid_t pid = getpid();
     8     printf("pid = %d
    ", pid);
     9     while(1);
    10     return 0;
    11 }

    打印:

    jingyg@jingyg:~/share/mytest/linux_userspace/0$ gcc -g -Wall 0_1_hello_world.c -o hello_world
    jingyg@jingyg:~/share/mytest/linux_userspace/0$ ./hello_world 
    pid = 3929

    使用 ps 命令查看进程 ID,(加 -L 选项可以显示线程信息,LWP:线程 ID(调用 gettid()系统调用的返回值),NLWP:线程组内的线程个数)

    jingyg@jingyg:~$ ps -efL
    UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
    jingyg    3929  3436  3929 99    1 03:07 pts/1    00:01:24 ./hello_world

    可以看到 PID 和 LWP 的值一样。
    多线程的进程,又被称作线程组,线程组内的第一个线程,在用户态被称作主线程(main thread)。内核在创建第一个线程时,会将线程组 ID (即进程 ID) 的值设置为第一个线程的线程 ID。

    虽然 Linux 提供了 gettid 系统调用来返回线程 ID,但是可惜的是 glibc 并没有将该系统调用封装并开放给程序员使用。如果确实需要获取线程 ID,可以采用如下方法:

    #include <sys/syscall.h>
    int TID = syscall(SYS_gettid);

     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <unistd.h>
     4 #include <sys/syscall.h>
     5 
     6 int main()
     7 {
     8     pid_t pid = getpid();
     9     printf("pid = %d
    ", pid);
    10 
    11     int tid = syscall(SYS_gettid);
    12     printf("tid = %d
    ", tid);
    13     return 0;
    14 }

    打印:

    jingyg@jingyg:~/share/mytest/linux_userspace/0$ ./hello_world 
    pid = 3995
    tid = 3995


    pthread_self()

    pthread 库中有一个 pthread_self() 接口用来获取线程 ID,但是这个 ID并不是内核中那个线程 ID

    #include <pthread.h>
    pthread_t pthread_self(void);

    pthread_t 到底是个什么样的数据结构呢?因为 POSIX 标准并没有限制 pthread_t 的数据类型,所以该类型取决于具体实现。对于 Linux 目前使用的 NPTL 实现而言,pthread_t 类型的线程 ID,本质上就是一个进程地址空间上的一个地址,而且 pthread_t 类型的线程 ID很有可能被复用:

     1 #include <stdio.h>
     2 #include <pthread.h>
     3 
     4 void* thread_work(void* param)
     5 {
     6     printf("pthread_self : %p
    ", (void*)pthread_self());
     7     return NULL;
     8 }
     9 
    10 int main()
    11 {
    12     pthread_t tid = 0;
    13     int ret = pthread_create(&tid, NULL, thread_work, NULL);
    14     ret = pthread_join(tid, NULL);
    15 
    16     ret = pthread_create(&tid, NULL, thread_work, NULL);
    17     ret = pthread_join(tid, NULL);
    18     return 0;
    19 }

    打印:(编译 pthread 相关接口时,需要加上 -lpthread 选项,如 gcc -g -Wall 0_1_hello_world.c -o hello_world -lpthread)

    jingyg@jingyg:~/share/mytest/linux_userspace/0$ ./hello_world 
    pthread_self : 0xb7525b40
    pthread_self : 0xb7525b40

    如果线程退出了,重新创建的线程很可能复用同一个 pthread_t 类型的 ID。在设计调试日志时,用 pthread_t 类型的 ID 来标识进程就不太合适了。

    采用 pid_t 类型的线程 ID 来唯一标识进程由以下优势:

    • 返回类型是 pid_t 类型,进程之间不会存在重复的线程 ID,而且不同线程之间也不会重复,在任意时刻都是全局唯一的值
    • proc 中记录了线程的相关信息,可以方便的查看 /proc/pid/task/tid 来获取线程对应的信息
    • ps 命令提供了查看线程信息的 -L 选项,可以通过输出中的 LWP 和 NLWP,来查看同一个线程组的线程个数和线程 ID 的信息

    如:

    #include <stdio.h>
    #include <pthread.h>
    #include <sys/syscall.h>
    
    void* thread_work(void* param)
    {
        int tid = syscall(SYS_gettid);
        printf("tid : %d
    ", tid);
        sleep(1000);
        return NULL;
    }
    
    int main()
    {
        pthread_t tid1 = 0;
        pthread_t tid2 = 0;
        pthread_t tid3 = 0;
        int ret = pthread_create(&tid1, NULL, thread_work, NULL);
        ret = pthread_create(&tid2, NULL, thread_work, NULL);
        ret = pthread_create(&tid3, NULL, thread_work, NULL);
    
        ret = pthread_join(tid1, NULL);
        ret = pthread_join(tid2, NULL);
        ret = pthread_join(tid3, NULL);
        return 0;
    }

    ps 命令查看线程 ID:

    jingyg@jingyg:~$ ps -efL | grep hello_world
    jingyg    4038  3436  4038  0    4 03:54 pts/1    00:00:00 ./hello_world
    jingyg    4038  3436  4039  0    4 03:54 pts/1    00:00:00 ./hello_world
    jingyg    4038  3436  4040  0    4 03:54 pts/1    00:00:00 ./hello_world
    jingyg    4038  3436  4041  0    4 03:54 pts/1    00:00:00 ./hello_world

    查看 proc:

    jingyg@jingyg:~$ ll /proc/4038/task/
    total 0
    dr-xr-xr-x 6 jingyg jingyg 0 Jul 20 03:54 ./
    dr-xr-xr-x 9 jingyg jingyg 0 Jul 20 03:54 ../
    dr-xr-xr-x 6 jingyg jingyg 0 Jul 20 03:54 4038/
    dr-xr-xr-x 6 jingyg jingyg 0 Jul 20 03:54 4039/
    dr-xr-xr-x 6 jingyg jingyg 0 Jul 20 03:54 4040/
    dr-xr-xr-x 6 jingyg jingyg 0 Jul 20 03:54 4041/
  • 相关阅读:
    类的有参方法
    WPF 中的设备无关单位
    Skelta BPM.NET 2006 初探
    Reporting Services Handscript
    C语言I博客作业03
    C语言I博客作业02
    C++类的运算符重载和转换函数结合的问题
    C++ 函数返回类成员的问题
    kaggle 利用linear regression 进行房价预测
    android Could not open: c:\。。。。\.android/avd/XXXX.ini 问题和解决方法
  • 原文地址:https://www.cnblogs.com/jingyg/p/5684377.html
Copyright © 2020-2023  润新知