1. 进程的定义
程序是指存储在外部存储(如硬盘)的一个可执行文件。
进程则是具有独立功能的程序关于某个数据集合上的一次运行活动
进程包括代码段和数据段,除此之外进程一般还包含打开的文件,以及要处理的信号和CPU上下文等等。
2. 进程描述符
Linux内核通过一个被称为进程描述符的
task_struct
结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在include/linux/sched.h
文件中,是进程控制块(PCB)在Linux中的具体实现。
由于
task_struct
结构体可以说是linux
内核源码中最复杂的一个结构体,所以我们这里只做浅浅的 ”窥探“。
主要字段 | 简要说明 |
---|---|
state |
表示进程状态,如可运行、可中断休眠、不可中断睡眠等 |
pid |
进程的唯一表示符 |
tpid |
线程组的领头线程的 pid ,用于区分不同的线程组 |
flags |
进程的标志信息,如是否使用超级用户权限、是否正在退出等 |
priority |
进程的优先级,包含静态优先级和动态优先级 |
policy |
进程的调度策略,如先进先出、时间片轮转等 |
mm |
指向进程的内存描述符,管理进程的地址空间 |
parent |
指向进程的父进程 |
children |
子进程列表的头部 |
sibling |
兄弟进程列表的链接 |
3. 进程的状态
3.1 互斥状态(5个)
状态 | 定义 | 描述 |
---|---|---|
TASK_RUNNING |
运行 | 进程处于可执行状态,在这个状态下的进程要么正准备被执行(就绪),要么正在等待CPU时间片调度。 |
TASK_INTERRUPTIBLE |
可中断等待 | 进程处于等待状态,其在等待某些条件成立或者接收到某些信号,进程会被唤醒变为运行状态。 |
TASK_UNINTERRUPTIBLE |
不可中断等待 | 进程处于等待状态,其在等待某些条件成立,进程会被唤醒变为运行状态,但不能被信号唤醒。 |
TASK_TRACED |
被追踪 | 进程处于被追踪状态,被debugger等进程监视 |
TASK_STOPPED |
停止 | 进程处于停止状态,进程不能被执行。一般接收到 SIGSTOP , SIGTSTP , SIGTTIN , SIGTTOU 信号进程会变成 TASK_STOPPED 状态。 |
3.2 终止状态(2个)
其实还有两个附加的进程状态既可以添加到
state
域中,又可以被添加到exit_state
域中。只有当进程终止的时候,才会达到这两种状态。
状态 | 描述 |
---|---|
EXIT_ZOMBIE |
进程的执行被终止,但是其父进程还没有使用 wait() 等系统调用来获知它的终止信息,此时进程成为僵尸进程 |
EXIT_DEAD |
进程的最终状态 |
4. 进程描述符(PID
)
pid_t pid; /* 进程标识符 */
pid_t tgid; /* 线程标识符 */
在
CONFIG_BASE_SMALL
配置为0的情况下,PID
的取值范围是0到32767,即系统重的进程最大个数为32768个
#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)
在Linux系统中,一个线程组中的所有线程使用和该线程组的领头线程(该组中的第一个轻量级进程)相同的
PID
,并被存放在tgid
成员中。只有线程组的领头线程的pid
成员才会被设置为与tgid
相同的值。注意,getpid()
系统调用返回的是当前进程的tgid
值而不是pid
值。
5. 进程内核栈
void * stack;
内核栈与线程描述符
对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中:
- 一个是内核态的进程堆栈
- 一个是紧挨着进程描述符的小数据结构
thread_info
,叫做线程描述符。
Linux 把
thread_info
(线程描述符)和内核态的线程堆栈存放在一起,这块区域通常是8192K
(占两个页框),其实地址必须是 8192 的整数倍。
内核态的进程访问处于内核数据段的栈,这个栈不同于用户态的进程所用的栈。
用户态进程所用的栈,是在进程线性地址空间中;
而内核栈是当进程从用户空间进入内核空间时,特权级发生变化,需要切换堆栈。
在新内核中,进程描述符 task_struct
结构中没有直接指向 thread_info
结构的指针,而是用一个 void*
指针类型的成员表示,然后通过类型转换来访问 thread_info
结构。
在80x86系统中, 栈起始于顶端,并朝着这个内存区开始的方向增长。从用户态刚切换到内核态以后,进程的内核栈总是空的。