理解Linux的进程,线程,PID,LWP,TID,TGID
在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)的关系。
#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 ;
} 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 ;
} 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 ;
}
注意编译的时候要利用-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:
the pthread_1 id is
the thread_1's Pid is 20992
The LWPID/tid of thread_1 is:
the pthread_2 id is
the thread_2's Pid is 20992
The LWPID/tid of thread_2 is:
上述结果说明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
./threadTest
./threadTest
./threadTest
从上述结果中可以看到:
- PID=TGID: 20992
- TID=LWP: 20993 or 20994
- 至于SID,30481是bash shell的进程ID。
Linux用户态命令查看线程
top
默认top显示的是task数量,即进程。
可以利用敲"H",来切换成线程。如下,可以看到实际上有96个线程。也可以直接利用top -H命令来直接打印线程情况。
ps
ps的-L选项可以看到线程,通常能打印出LWP和NLWP相关信息。如下命令即可查看线程信息:
ps -eLf
pidstat
pidstat -t [-p pid号] 可以打印出线程之间的关系。
htop
要在htop中启用线程查看,开启htop,然后按<F2>来进入htop的设置菜单。选择“设置”栏下面的“显示选项”,然后开启“树状视图”和“显示自定义线程名”选项。按<F10>退出设置。
注:MAC的F2按fn+F2。
参考
理解Linux的进程,线程,PID,LWP,TID,TGID的更多相关文章
- Linux查看进程线程个数
1.根据进程号进行查询: # pstree -p 进程号 # top -Hp 进程号 2.根据进程名字进行查询: # pstree -p `ps -e | grep server | awk '{pr ...
- [转帖]Linux的进程线程及调度
Linux的进程线程及调度 本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10393707.html 本文为宋宝华<Linux的进程 ...
- linux根据进程号PID查找启动程序的全路径
linux根据进程号PID查找启动程序的全路径 2014-01-25 11:09 18629人阅读 评论(0) 收藏 举报 分类: Linux系统管理(29) 版权声明:本文为博主原创文章,未经博 ...
- 深入理解 Linux的进程,线程,PID,LWP,TID,TGID
转载:https://www.linuxidc.com/Linux/2019-03/157819.htm 在Linux的top和ps命令中,默认看到最多的是pid (process ID),也许你也能 ...
- Linux下线程pid和tid
#include <stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/sy ...
- Linux的进程线程及调度
本文为宋宝华<Linux的进程.线程以及调度>学习笔记. 1 进程概念 1.1 进程与线程的定义 操作系统中的经典定义: 进程:资源分配单位. 线程:调度单位. 操作系统中用PCB(Pro ...
- 深入理解linux内核-进程和程序
进程描述符task_struct task_struct { //进程基本信息 pid 进程id号 tgid 线程组id号,与线程组领头线程pid号相同 getpid()返回该值 tasks in ...
- Linux下进程线程,Nignx与php-fpm的进程线程方式
1.进程与线程区别 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流, ...
- Linux的进程/线程间通信方式总结
Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...
随机推荐
- Jekens Source Code Management None 源码管理没有Git
jekens安装完成后,在配置中Source Code Management没有Git的选项,只有none,搞了大半天,一直安装插件报错,网上找的各种文章均未能解决我的问题,多次尝试后终于解决了这个问 ...
- 网页制作中最有用的免费Ajax和JavaScript代码库
网上看到的一篇小文,挺有用的,收藏在这. 本文中,我整理了12个免费的Ajax和JavaScript代码库,可以帮助Web开发人员将应用程序提升到一个新水平. Ajax Instant Messeng ...
- Sparse Filtering简介
当前很多的特征学习(feature learning)算法需要很多的超参数(hyper-parameter)调节, Sparse Filtering则只需要一个超参数--需要学习的特征的个数, 所以非 ...
- 【leetcode 简单】 第七十六题 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作, ...
- python概念-Socket到底有多骚
Socket究竟是什么呢? 简单来说Socket就是用来完成客户端与服务器之间的通信 例如浏览器访问网页,例如网络游戏等一切基于客户端服务器来实现的C/S架构程序 Socket是基于互联网OSI七层协 ...
- python端口扫描
简易版: #author:Blood_Zero #coding:utf-8 import socket import sys PortList=[21,22,23,25,80,135] # host= ...
- 从Python到Web开发
基础部分: 1-编程基础及Python环境部署 2-Python基础语法-内存管理-运算符-程序控制 3-Python内置结构-列表 4-Python数据类型之元组-字符串 5-python的封装与结 ...
- go 切片
切片定义 切片是基于数组类型做的一层封装.它非常灵活,可以自动扩容. var a []int //定义一个int类型的空切片 切片初始化, a[start:end]创建一个包括从start到end-1 ...
- C++学习之路(十一):C++的初始化列表
结论: 1.在C++中,成员变量的初始化顺序与变量在类型中的声明顺序相同,而与他们在构造函数的初始化列表中的顺序无关. 2.构造函数分为两个阶段执行:1)初始化阶段:2)普通的计算阶段,表现为赋值操作 ...
- WebApi Owin SelfHost OAuth2 - client_credentials
参考:http://neverc.cnblogs.com/p/4970996.html