理解Linux的进程,线程相关的各类ID:PID,LWP,TID,TGID

本文深入解析Linux系统中进程与线程的各种ID概念,包括PID、LWP、TID、TGID等,通过示例程序展示它们之间的关系,帮助读者理解Linux环境下资源分配与调度的基本单位。

最近实验室遇到了一个关于PID的问题,让我也跟着学习一下,查看了一下相关资料,找到一篇关于Linux进程和线程的各种ID的介绍,所以转载了过来。

作者:wipan
来源:cnblogs
地址:https://www.cnblogs.com/wipan/p/9488318.html

在Linux的top和ps命令中,默认看到最多的是pid (process ID),也许你也能看到lwp (thread ID)和tgid (thread group ID for the thread group leader)等等,而在Linux库函数和系统调用里也许你注意到了pthread id和tid等等。还有更多的ID,比如pgrp (process group ID), sid (session ID for the session leader)和 tpgid (tty process group ID for the process group leader)。概念太多可能很晕,但是只要对Linux的进程和线程的基本概念有准确的理解,这些ID的含义都迎刃而解。下面将介绍进程和线程的核心概念,并以一个示例程序来验证这些ID之间的关系。

Linux的进程和线程

Linux的进程和线程有很多异同点,可以Google下。但只要能清楚地理解一下几点,则足够理解Linux中各种ID的含义。

  • 进程是资源分配的基本单位,线程是调度的基本单位
  • 进程是资源的集合,这些资源包括内存地址空间,文件描述符等等,一个进程中的多个线程共享这些资源。
  • CPU对任务进行调度时,可调度的基本单位 (dispatchable entity)是线程。如果一个进程中没有其他线程,可以理解成这个进程中只有一个主线程,这个主进程独享进程中的所有资源。
  • 进程的个体间是完全独立的,而线程间是彼此依存,并且共享资源。多进程环境中,任何一个进程的终止,不会影响到其他非子进程。而多线程环境中,父线程终止,全部子线程被迫终止(没有了资源)。

上述第一点说明是最基础的,也是最重要的。

初步理解各种ID。基本上按照重要程度从高到低,在分割线下方的IDs不太重要。

  • pid: 进程ID。
  • lwp: 线程ID。在用户态的命令(比如ps)中常用的显示方式。
  • tid: 线程ID,等于lwp。tid在系统提供的接口函数中更常用,比如syscall(SYS_gettid)和syscall(__NR_gettid)。
  • tgid: 线程组ID,也就是线程组leader的进程ID,等于pid。
  • ------分割线------
  • pgid: 进程组ID,也就是进程组leader的进程ID。
  • pthread id: pthread库提供的ID,生效范围不在系统级别,可以忽略。
  • sid: session ID for the session leader。
  • tpgid: tty process group ID for the process group leader。

 从上面的列表看出,各种ID最后都归结到pid和lwp(tid)上。所以理解各种ID,最终归结为理解pid和lwp(tid)的联系和区别。

下面的图是一张描述父子进程,线程之间关系的图。

上图很好地描述了用户视角(user view)和内核视角(kernel view)看到线程的差别:

  • 从用户视角出发,在pid 42中产生的tid 44线程,属于tgid(线程组leader的进程ID) 42。甚至用ps和top的默认参数,你都无法看到tid 44线程。
  • 从内核视角出发,tid 42和tid 44是独立的调度单元,可以把他们视为"pid 42"和"pid 44"。

需要指出的是,有时候在Linux中进程和线程的区分也是不是十分严格的。即使线程和进程混用,pid和tid混用,根据上下文,还是可以清楚地区分对方想要表达的意思。上图中,从内核视角出发看到了pid 44,是从调度单元的角度出发,但是在top或ps命令中,你是绝对找不到一个pid为44的进程的,只能看到一个lwp(tid)为44的线程。

理解pid和lwp(tid)的示例程序

下面利用一个示例程序来进一步理解pid和lwp(tid),以及利用格式化的ps命令打印出各种ID。下面的程序在main函数中创建了2个子线程,加上main函数这个主线程,一共有3个线程。在3个线程中分别打印pthread id, pid和lwp(tid),来验证pid和lwp(tid)的关系。

threadTest.c:
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <pthread.h>

#define gettidv1() syscall(__NR_gettid) // new form
#define gettidv2() syscall(SYS_gettid)  // traditional form

void *ThreadFunc1()
{
        printf("the pthread_1 id is %ld\n", pthread_self());
        printf("the thread_1's Pid is %d\n", getpid());
        printf("The LWPID/tid of thread_1 is: %ld\n", (long int)gettidv1());
        pause();

        return 0;
}

void *ThreadFunc2()
{
        printf("the pthread_2 id is %ld\n", pthread_self());
        printf("the thread_2's Pid is %d\n", getpid());
        printf("The LWPID/tid of thread_2 is: %ld\n", (long int)gettidv1());
        pause();

        return 0;
}

int main(int argc, char *argv[])
{
        pid_t tid;
        pthread_t pthread_id;

        printf("the master thread's pthread id is %ld\n", pthread_self());
        printf("the master thread's Pid is %d\n", getpid());
        printf("The LWPID of master thread is: %ld\n", (long int)gettidv1());

        // 创建2个线程
        pthread_create(&pthread_id, NULL, ThreadFunc2, NULL);
        pthread_create(&pthread_id, NULL, ThreadFunc1, NULL);
        pause();

        return 0;
}

注意编译的时候要利用-l指定library参数。

gcc threadTest.c -o threadTest -l pthread

执行程序,结果如下:

# ./threadTest

the master thread's pthread id is 140154481125184
the master thread's Pid is 20992
The LWPID of master thread is: 20992
the pthread_1 id is 140154464352000
the thread_1's Pid is 20992
The LWPID/tid of thread_1 is: 20994
the pthread_2 id is 140154472744704
the thread_2's Pid is 20992
The LWPID/tid of thread_2 is: 20993

上述结果说明pthread id是pthread库提供的ID,在系统级别没有意义。pid都是线程组leader的进程ID,即20992。而lwp(tid)则是线程ID,分别是20993和20994。

同时利用ps来查看结果,注意ps默认只打印进程级别信息,需要用-L选项来查看线程基本信息。

# ps -eo pid,tid,lwp,tgid,pgrp,sid,tpgid,args -L | awk '{if(NR==1) print $0; if($8~/threadTest/) print $0}'
  PID   TID   LWP  TGID  PGRP   SID TPGID COMMAND
20992 20992 20992 20992 20992 30481 20992 ./threadTest
20992 20993 20993 20992 20992 30481 20992 ./threadTest
20992 20994 20994 20992 20992 30481 20992 ./threadTest

从上述结果中可以看到:

  • PID=TGID: 20992
  • TID=LWP: 20993 or 20994
  • 至于SID,30481是bash shell的进程ID。

Linux用户态命令查看线程

1. top命令

默认top显示的是task数量,即进程。

可以利用敲"H",来切换成线程。如下,可以看到实际上有96个线程。也可以直接利用top -H命令来直接打印线程情况。

2. ps命令

ps的-L选项可以看到线程,通常能打印出LWP和NLWP相关信息。如下命令即可查看线程信息:

ps -eLf

3. pidstat

pidstat -t [-p pid号] 可以打印出线程之间的关系。

4. htop

要在htop中启用线程查看,开启htop,然后按<F2>来进入htop的设置菜单。选择“设置”栏下面的“显示选项”,然后开启“树状视图”和“显示自定义线程名”选项。按<F10>退出设置。
注:MAC的F2按fn+F2。

参考

Linux进程与线程的区别

### LinuxPIDLWP 的概念及其关系 #### 1. **PID(Process ID)** 在 Linux 系统中,PID 是指进程标识符(Process Identifier)。它是内核用来唯一标识一个进程的数值。每个启动的新进程都会被分配一个唯一的 PID[^2]。无论是单线程还是多线程的应用程序,其主进程都有自己的 PID。 对于一个多线程的应用来说,主线程PID 就是整个线程组的 TGID(Thread Group ID),也就是线程组领导者的进程 ID[^1]。 --- #### 2. **LWP(Lightweight Process)** LWP 表示轻量级进程(Lightweight Process)。这是一个抽象的概念,用于区分线程和传统意义上的进程。在 Linux 内核实现中,线程本质上也是作为一个特殊的进程来处理的,所有的线程进程都是作为 `task_struct` 实例存在于内核中的[^5]。 具体而言: - 每个线程对应一个 LWP。 - LWPs 受到内核统一调度和管理,就像普通的进程一样。 - TID(Thread ID 或 Task ID)是用来标识 LWP 的唯一编号[^3]。 因此,可以说 LWP 是一种更细粒度的任务单位,它允许多个线程共享同一地址空间的同时又能独立运行。 --- #### 3. **PIDLWP 的区别** | 属性 | PID | LWP | |--------------|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| | 定义 | 进程标识符,代表一个完整的任务单元 | 轻量级进程,通常指的是线程 | | 地址空间 | 拥有自己的独立地址空间 | 同一进程中所有线程共享父进程的地址空间 | | 生命周期 | 主进程结束则子进程也可能随之终止 | 如果某个线程退出,其他线程不受影响 | | 唯一性 | 整个系统范围内唯一 | 在所属的线程组内唯一 | --- #### 4. **PIDLWP 的关系** - 当使用 `clone()` 系统调用创建一个新的线程时,该线程会被赋予一个独特的 TID(Task ID),这也是它的 LWP ID。 - 对于单线程进程,其 PIDLWP ID 是一致的。 - 对于多线程环境下的每一个线程,它们会有各自的 LWP ID(即 TID),但这些线程共享同一个 PID,这个 PID 实际上是指向线程组领导者(主线程)的 TGID[^1]。 换句话说,PID 更像是一个高层次上的标识,而 LWP 则深入到了具体的执行上下文中去定义了实际的工作单元。 --- ```c #include <iostream> #include <unistd.h> #include <pthread.h> #include <sys/types.h> #include <sys/syscall.h> using namespace std; // 获取当前线程的真实TID (LWP ID) pid_t gettid() { return static_cast<pid_t>(syscall(SYS_gettid)); } void* func(void* arg) { cout << "Thread ID (pthread): " << pthread_self() << endl; cout << "Process Group ID (TGID/PID): " << getpid() << endl; // 输出的是线程ID cout << "Real Thread ID (LWP/TID): " << gettid() << endl; // 输出真实的线程ID cout << "----------------------------------------" << endl; sleep(5); } int main() { pthread_t tid[2]; pthread_create(&tid[0], nullptr, func, nullptr); sleep(2); // 让第一个线程先打印 pthread_create(&tid[1], nullptr, func, nullptr); pthread_join(tid[0], nullptr); pthread_join(tid[1], nullptr); return 0; } ``` 此代码片段展示了如何获取并对比不同类型的 ID: - `getpid()` 返回的是线程IDTGID),也即是主线程PID。 - `gettid()` 提供真正的线程级别 IDLWP/TID)。 - `pthread_self()` 给出了 POSIX 标准下线程的用户态表示形式。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值