Linux中process、thread、task的认识

Linux中process、thread、task的认识

进程的术语是 process,是 Linux 最基础的抽象,另一个基础抽象是文件。

进程包括执行中的程序以及相关的资源 (包括cpu状态、打开的文件、挂起的信号、tty、内存地址空间等)。进程 = n*执行流 + 资源,n>=1。

我们面试时常问的一些关于进程和线程的概念,这里复习一下:

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

Linux 进程的特点:

  1. 通过系统调用 fork() 创建进程,fork() 会复制现有进程来创建一个全新的进程;
  2. 内核里,并不严格区分进程和线程;
  3. 从内核的角度看,调度单位是线程 (即执行流)。可以把线程看做是进程里的一条执行流,1个进程里可以有1个或者多个线程;
  4. 内核里,常把进程称为 task 或者 thread,这样描述更准确,因为许多进程就只有1条执行流;
  5. 内核通过轻量级进程 (lightweight process) 来支持多线程。1个轻量级进程就对应1个线程,轻量级进程之间可以共享打开的文件、地址空间等资源;

在Process和Thread的实现方面,Linux Kernel并没有做过多区分,而是用统一的task_struct来描述:通过 task_struct 结构体来描述一个进程,称为进程描述符 (process descriptor),它保存着支撑一个进程正常运行的所有信息。

输入图片说明

Kernel只需要在task_struct里面通过flag来区分一下这个task_struct对应的是Process和Thread就好,然后就可以针对不同的情况作不同的管理。每一个进程,即便是轻量级进程(即线程),都有1个 task_struct。

//文件:sched.h (include\linux)
    

struct task_struct {
struct thread_info thread_info;
volatile long state;
void *stack;
[...]
struct mm_struct *mm;
[...]
pid_t pid;
[...]
struct task_struct *parent;
[...]
char comm[TASK_COMM_LEN];
[...]
struct files_struct *files;
[...]
struct signal_struct *signal;
}

这是一个庞大的结构体,不仅有许多进程相关的基础字段,还有许多指向其他数据结构的指针。它包含的字段能完整地描述一个正在执行的程序,包括 cpu 状态、打开的文件、地址空间、挂起的信号、进程状态等。

img

初学者注意部分字段即可:

1、struct thread_info thread_info: 进程底层信息,平台相关,下面会详细描述。

2、long state: 进程当前的状态,下面是几个比较重要的进程状态以及它们之间的转换流程。

3、void *stack: 指向进程内核栈,。

4、struct mm_struct *mm: 与进程地址空间相关的信息都保存在一个叫内存描述符 (memory descriptor) 的结构体 (mm_struct) 中。

5、pid_t pid: 进程标识符,本质就是一个数字,是用户空间引用进程的唯一标识。

6、struct task_struct *parent: 父进程的 task_struct。

7、char comm[TASK_COMM_LEN]: 进程的名称。

8、struct files_struct *files: 打开的文件表。

9、struct signal_struct *signal: 信号处理相关。

初步理解各种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)的联系和区别。

以下图会帮助我们理解父子进程、线程之间关系呢:

img

上图很好地描述了用户视角(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的线程。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值