线程 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\n", 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\n", pid);
10 
11     int tid = syscall(SYS_gettid);
12     printf("tid = %d\n", 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\n", (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\n", 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/

 

posted @ 2016-07-19 13:15  Kjing  阅读(2298)  评论(0编辑  收藏  举报