进程管理1「Data structures and PID」
进程管理系列文章将主要关注Linux内核为了描述任务「task」所使用的各种数据结构。除此之外,还会阐述内核是如何管理用于标识任务的PID以及如何创建和终止任务。该系列文章主要涉及如下内容:
- 任务涉及的三个数据结构:
struct task_struct
、struct thread_info
、struct thread_struct
- PID分配流程以及用于标识任务的相关数据结构
- 任务在整个生命周期中涉及的执行状态以及相关的API
- 指示任务重要性的优先级「priority」
- 任务创建和终止的流程
- swapper任务的初始化过程
本文主要关注前两点:三个重要的数据结构和PID。
- Target Platform: Rock960c
- ARCH: arm64
- Linux Kernel: linux-4.19.27
任务的表示形式
在通用操作系统理论中,任务「task」被定义为执行单元「unit of execution」或者被抽象成工作单元「units of work」。下面我们将看到Linux内核是如何定义和描述任务的。
任务、进程和线程三者之间的区别
从广义上看,程序「program」指的是编译产生的对象文件「compiled object file」,而进程「process」描述的是等待执行的或正在执行的程序实例「instance」。如果存在两个特定程序的实例,那就意味着系统有两个进程。进程本质上可以看作一种抽象概念,它不仅包含被执行的程序代码「program code」同时还包括其他的内容。此外一个进程还可以包含一个或多个线程。进程主要包含下列内容:
- 正在被执行的程序代码「program code」
- 正在使用的内存地址空间「memory address sapce」
- 使用的各种资源「resource」(打开的文件和目录、信号「signal」等)
线程就是一个轻量级的进程,在所属的进程中它与其他线程共享大部分资源。与进程不同的是在一个线程组中某个线程与其他线程共享一个内存地址空间,但系统中每个进程都拥有自己独立的内存地址空间。除此之外,文件、目录和信号等资源在线程之间也是可以共享的。
内核线程「kernel thread」顾名思义是由内核代码创建的线程,但是与用户态任务不同的是它只能运行在内核态并且没有内存地址空间。