线程id与进程id

内核标识的线程ID-LWP,在系统级别有效

  • 在Linux中,线程被称为轻量级进程(light weighted process),每一个用户态的线程,在内核中都对于一个调度实体,也称为进程描述符(task_struct结构体)
  • 没有线程之前,一个进程对于内核里的一个进程描述符,对于一个进程ID。但是引入线程概念之后,情况发送了变化,一个用户进程下管辖N个用户态线程,每个线程作为一个独立的调度实体,在内核态都有自己的进程描述符,进程和内核的描述符一下子就变成了1:N的关系,POSIX标准由要求进程内的所有线程调用getpid()函数返回相同的id,如何解决呢?
  • 所有Linux内核引入了进程组的概念
struct task_struct
{
    ...
    pid_t pid;//进程的id;getpid()获得的是进程的id
    pid_t tpid; //线程组的id

    struct task_struct *group_leader;
    ...
    struct list_head thread_group;//链表,头节点
}

多进程的进程,又被称为线程组,线程组内的每一个线程在内核中都有一个进程描述符(task_struct)与之对应。进程描述符结构体中的pid,表面上看对应的是进程id,其实不然,他对应的是线程id,进程描述符中的tgid,含义是Thread Group ID,该值对应是是用户层面的进程id

 线程id与进程id_第1张图片

现在介绍的线程id,不同于pthread_t类型的线程id,和进程id一样,线程id是pid_t类型的变量,而且是用来唯一标识线程的一个整形变量,如何查看一个线程的ID呢?

由此看出这些都是单线程,进程为1,2,3,4 线程id也为1,2,3,4 

ps命令的-L选项,会显示如下信息

  • LWP:线程ID,既grttid()相同调用的返回值
  • NLWP:线程组内线程的个数

我们来看一个多线程的情况: 

可以看出,a.out进程是多线程的,进程id为28543,进程内有2个线程,线程id分别为28543,28544

线程id与进程id_第2张图片

 

Linux系统提供了gettid系统调用来返回其线程id,可是flibc并没有将该系统调用封装起来,在开发接口来供程序员使用,如果确实学院获得线程id,可以采用如下方法:

#include
pid_t tid;
tid = syscall(SYS_gettid);
  •  从上面可以看出来,a.out的基础ID为28543,而有一个线程的id也是28543。
  • 这不是巧合,线程组内的第一个线程被称为主线程,在内核中被称为group leader,内核在创建第一个线程时,会将线程组的id的值设置称第一个线程的id
  • group_leader指针则指向自身,即主线程的进程描述符。所以线程组内存在一个线程id等于进程id,而该线程即为线程组的主线程
  • 至于线程组其他线程的ID则有内核负责分配。其线程ID总是和主线程的线程id一致,无论主线程之间创建线程,还是创建出来的线程再次创建进程,都是一样

注:线程和进程不一样,进程有父进程的概念,但是在线程组里面,所以线程都是对等关系

线程id与进程id_第3张图片

线程ID及进程地址空间布局(用户级别的线程id)

  • pthread_create函数会产生一个线程id,存放在第一个参数指向的地址中,该线程id和前面说的线程线程id不是一回事
  • 前面讲的线程id属于进程调度的范畴,因为线程是轻量级进程,是操作系统调度的最小单位,所以需要一个数值来唯一表示该线程 
  • pthread_create函数产生并标记在第一个参数指向的地址中的线程ID中,属于NPTL线程库的范畴。线程库是后续操作就是根据线程id来操作线程的
  • 线程库NPTL提供了pthread_self函数,可以获取线程自身的ID
pthread_t pthread_self(void);
  • pthread_t到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL而言,pthread_t类型的线程id是一个进程地址空间上的地址。

 

线程id与进程id_第4张图片

 

你可能感兴趣的:(操作系统)