进程管理(补充)

发布时间 2023-11-29 09:34:56作者: 一只飞鸟

1、进程描述符
(1)进程与线程
进程是处于执行期的程序以及相关资源的总称。线程在linux上称为轻量级进程,没有独立的地址空间,一个进程下的所有线程共享地址空间、文件系统资源、文件描述符、信号处理程序等。

(2)进程描述符task_struct
内核把进程的列表存放在叫做任务队列的双向循环链表中。链表中的每一个项都是类型为task_struct(即进程描述符的结构),它包含了一个具体进程的所有相关信息。通过slab分配器分配task_struct结构,这样方便对象复用和缓存着色。该结构体有些大,下图只是指出其中比较重要的成员部分。

state状态

TASK_RUNNING表示进程要么正在执行,要么正要准备执行。

TASK_INTERRUPTIBLE表示进程被阻塞(睡眠),直到某个条件变为真。条件一旦达成,进程的状态就被设置为TASK_RUNNING。

TASK_UNINTERRUPTIBLE的意义与TASK_INTERRUPTIBLE类似,除了不能通过接受一个信号来唤醒以外。

__TASK_STOPPED表示进程被停止执行。

__TASK_TRACED表示进程被debugger等进程监视。

EXIT_ZOMBIE表示进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息。

EXIT_DEAD表示进程的最终状态。

EXIT_ZOMBIE和EXIT_DEAD也可以存放在exit_state成员中

进程标识符

内核通过一个唯一的进程标示值或PID来标识每一个进程。

内核堆栈stack

内核通过thread_union联合体来表示进程的内核栈,其中THREAD_SIZE宏的大小为8192(首地址按照8192对齐),包含thread_info大小,实际可用栈小于8192字节(见下节分析)。

当进程从用户态切换到内核态时,进程的内核栈总是空的,所以ARM的sp寄存器指向这个栈的顶端。因此,内核能够轻易地通过sp寄存器(因为对齐,低13位置0即首地址)获得当前正在CPU上运行的进程。

进程亲属关系成员

在Linux系统中,所有进程之间都有着直接或间接地联系,每个进程都有其父进程,也可能有零个或多个子进程。拥有同一父进程的所有进程具有兄弟关系。

real_parent指向其父进程,如果创建它的父进程不再存在,则指向PID为1的init进程。

parent指向其父进程,当它终止时,必须向它的父进程发送信号。它的值通常与real_parent相同。

children表示链表的头部,链表中的所有元素都是它的子进程。

sibling用于把当前进程插入到兄弟链表中。

group_leader指向其所在进程组的领头进程

进程调度

实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大静态优先级越低。

static_prio用于保存静态优先级,可以通过nice系统调用来进行修改。

rt_priority用于保存实时优先级。

normal_prio的值取决于静态优先级和调度策略。

prio用于保存动态优先级。

policy表示进程的调度策略,目前主要有以下五种:

SCHED_NORMAL用于普通进程,通过CFS调度器实现。

SCHED_BATCH用于非交互的处理器消耗型进程

SCHED_IDLE是在系统负载很低时使用。

SCHED_FIFO(先入先出调度算法)和SCHED_RR(轮流调度算法)都是实时调度策略

进程地址空间

mm指向进程所拥有的内存描述符,而active_mm指向进程运行时所使用的内存描述符。对于普通进程而言,这两个指针变量的值相同。但是,内核线程不拥有任何内存描述符,所以它们的mm成员总是为NULL。当内核线程得以运行时,它的active_mm成员被初始化为前一个运行进程的active_mm值。

信号处理

signal指向进程的信号描述符。

sighand指向进程的信号处理程序描述符。

blocked表示被阻塞信号的掩码,real_blocked表示临时掩码。

pending存放私有挂起信号的数据结构。

sas_ss_sp是信号处理程序备用堆栈的地址,sas_ss_size表示堆栈的大小。

设备驱动程序常用notifier指向的函数来阻塞进程的某些信号(notifier_mask是这些信号的位掩码),notifier_data指的是notifier所指向的函数可能使用的数据

thread_info
在2.6以前的内核中,各个进程的task_struct存放在它们内核栈的尾端,目的是为了让那些像X86那样寄存器较少的硬件体系结构只要通过栈指针就能计算出它的位置,而避免使用额外的寄存器专门记录。现在用slab分配器动态生成task_struct,所以只需在栈底(对于向下增长的栈来说)或栈顶(向上增长的栈来说)创建一个thread_info,通过它指向该进程的task_struct。