《Linux操作系统原理分析之Linux 进程管理 2》(6)
- 4 Linux 进程管理
- 4.2 Linux 进程的状态和标识
- 4.2.1 Linux 进程的状态及转换
- 4.2.2 Linux 进程的标识
- 4.2.3 进程标识哈希表
4 Linux 进程管理
4.2 Linux 进程的状态和标识
4.2.1 Linux 进程的状态及转换
linux 中进程状态分为 5 种,每个进程在系统中所处的状态记录在它的任务结构体的成员项 state 中。进程的状态用符号常量表示,它们定义在/include/linux/sched.h
下:
状态 | 定义 |
---|---|
可运行态(运行态、就绪态) | #define TASK_RUNNING 0 |
可中断的等待态 | #define TASK_INTERRUPTIBLE 1 |
不可中断的等待态 | #define TASK_UNINTERRUPTIBLE 2 |
僵死态 | #define TASK_ZOMBLE 3 |
暂停态 | #define TASK_STOPPED 4 |
1.运行态(running)——运行
该进程称为当前进程(current process),实际上 linux 并没有该状态,而是将其归结在可运行态。系统中设置全局指针变量 current,指向当前进程。
2.可运行态(Running)——就绪
Linux 中把所有处于运行、就绪状态的进程链接成一个双向链表,称为可运行队列(run_queue)。使用
任务结构体中的两个指针:
Struct task_struct *next_run;/*指向后一个任务结构体的指针*/
Struct task_struct *prev_run;/*指向前一个任务结构体的指针*/
该链表的首结点为 init_task。系统设置全局变量 nr_running 记录处于运行、就绪态的进程数。
3.等待态(wait)——阻塞
在 linux 中将该状态进一步划分为:可中断的等待态( interruptible)和不可中断的等待状态(uninterruptible)。
👉可中断的等待态的进程可以由信号(signal)来解除其等待态,收到信号后进程进入可运行态。
👉不可中断的等待状态的进程,一般都是直接或间接在等待硬件条件,只能用特定的方式来解除其等待状态,如是用 wakeup()。
处于等待态的进程根据其等待的事件排在不同的等待队列中。Linux 中等待队列是由一个 wait_queue结构体组成的单向循环链表。该结构体定义在 include/linux/wait.h 中,如下所示:
Struct wait_queue {
Struct task_struct *task;/*指向一个等待态的进程的任务结构体*/
Struct wait_queue *next;/*指向下一个 wait_queue 结构体*/
}
注:
👉与可运行队列不同,等待队列不是直接由进程的任务结构体组成队列,而是由于任务结构体对应的wait_queue 构成。
👉每个等待队列都有一个指向该队列的队首指针,它一般是个全局指针变量。
4.暂停态(stopped)
暂停态:进程由于需要接受某种特殊处理而暂时停止运行所处的状态。通常,进程在接受到外部进程的某个信号(SIGSTOP、SIGSTP、SIGTTOU)而进入暂停态。通常正在接受调试的进程就处于暂停态。
5.僵死态(zombie)
僵死态:进程的运行已经结束,但是由于某种原因它的进程结构体仍在系统中。
4.2.2 Linux 进程的标识
Linux 中,进程的标识是系统识别进程的依据,也是进程访问设备和文件时的凭证。Linux 为每个进程设置多种标识,不同的标识的用途不同。Task_struct 中记录着进程的各种标识:
Int pid | 进程标识号 |
---|---|
Unsigned short uid ,gid | 用户标识号,组标识号 |
Unsigned short euid ,egid | 用户有效标识号,组有效标识号 |
Unsigned short suid ,sgid | 用户备份标识号,组备份标识号 |
Unsigned short fsuid ,fsgid | 用户文件标识号,组文件标识号 |
1. Pid 32 位,但是为了与 UNIX 兼容(16 位),故 linux 也使用 16 位,最大值 32767。Pid 按照进程创建的先后顺序依次赋予进程,即前面进程 PID 值加 1。当达到最大值时,重复使用已经撤销进程的 PID。
2. 设置 Uid,gid 目的:文件保护。Linux 把文件的所有用户分为 3 类:所有者、同组用户、其他用户。
3. 一般情况下 euid=uid;egid= gid;fsuid= uid;fsgid= gid;
4. Euid 和 egid:在进程企图访问特权数据或代码时,系统内核需要检查进程的有效标识 Euid 和 egid。需要其他进程服务时,这两个指将变为服务进程 uid 和 gid。
5. fsuid ,fsgid:在进程企图访问文件时,系统内核需要检查进程的文件标识 fsuid ,fsgid。需要其他进程服务时,这两个指将变为服务进程 uid 和 gid。
6. 没有将 euid 和 fsuid,egid 和 fsgid 统一,原因:防止具有访问特权后,用户对系统造成破坏。
7. Suid 和 sgid 是 POSIX 标准要求的标识。当用户执行系统调用而使其用户标识 uid 或组标识 guid 改变时,suid 和 sgid 保存原来的值,以便恢复。
4.2.3 进程标识哈希表
Linux 的进程标识哈希表提供了按照哈希算法从进程 PID 快速查找对应任务结构体的方法,实现哈希算法的哈希函数定义为带参数的宏 pid_hashfn(x),如下所示:
#define pid_hashfn(x) ((((x)>>8)^(x))&(PIDHASH_SZ-1))???
其中:参数 x 就是进程的标识 PID,计算结果的哈希值用于检索对应的任务结构体。例如 PID 为 228的哈希值是 100,PID 为 27536 的哈希值是 123,PID 为 27535 的哈希值是 100。
为了解决哈希值冲突的问题,Linux 把具有相同哈希值的 PID 对应的进程组成一个个双向循环链表。在 task_struct 中的两个成员项:
Struct task_struct * pidhash_next;/*指向后一个任务结构体的指针*/
Struct task_struct * pidhash _prev;/*指向前一个任务结构体的指针*/
1.进程标识哈希表
Linux 使用一个称为 pidhash[]的指针数组管理这些链表,称为进程标识哈希表。该表中记录各个链表首结点地址,数组元素的下标与链表的哈希值相同。
在 include/linux/sched.h 中 pidhash[]数组定义如下:
Struct task_struct * pidhash[PIDHASH_SZ];
pidhash 数组由 PIDHASH_SZ 个元素组成,每个元素是指向一个进程任务结构体的指针。
数组元素个数 PIDHASH_SZ 是系统中最多可容纳的进程数 NR_TASKS 除以 4,定义如下:
#define PIDHASH_SZ(NR_TASKS>>2)???
2.进程标识哈希表操作函数:
标识 | 说明 |
---|---|
Hash_pid() | 进程创建时,将其任务结构体插入哈希链表 |
Unhash_pid() | 进程撤销时,将其任务结构体从哈希链表中删除。 |
Find_task_by_pid() | 根据 PID 相应进程的任务结构体。 |