Linux进程状态
TASK_ RUNNING
– 进程是可执行的。它或者正在执行,或者在等待队列中等待被执行。(相当于 就绪态+运行态)
TASK_ INTERRUPTIBALE
(可中断)–进程正在睡眠/阻塞,等待某些条件的达成。也可能因为接收到信号而提前被唤醒(浅睡眠)。
TASK_ UNINTERRUPTIBALE
(不可被中断)–与可中断状态相同,但即使接收到信号也不会被唤醒(深睡眠)
___TASK_ TRACED--
被其他进程跟踪的进程。
_TASK_ STOPPED(停止)- -
进程停止执行。进程没有投入运行,也不能投入运行。
Linux进程状态转换图
Linux进程PCB
task_ struct 数据结构剖析
在linux中每一个进程都由task_ struct 数据结构来定义.
task_ struct就是 我们通常所说的PCB
.它是对进程控制的唯一手段也是最有效的手段.当我们调用fork()时,系统会为我们产生一个task_ struct结构。 然后从父进程那里继承一-些数据,并把新的进程插入到进程树中,以待进行进程管理。因此了解task_ struct的结 构是我们理解任务调度的关键。
task_ struct
task_ struct剖析( 1)
task struct剖析( 2 )
进程标识符
pid_ t pid
pid_ t tgid
系统调用getpid()返回什么
Linux系统允许用户使用一个叫做进程标识符的PID来标识进程,PID顺序编号,新创建进程是前一个进程的PID加1,不过PID值有一个上限,达到上限之后再开始循环使用闲置的小PID。
task_ struct剖析 (3)
进程内核栈
void *stack;
当进程通过系统调用陷入内核时,内核代码所使用的栈并不是用户空间中的栈
,而是一个内核空间的栈,也就是进程内核栈
,它用于支持系统调用中的函数调用和局部变量,还用于保存一些系统调用前的应用信息,如用户空间栈指针、系统调用参数。
task struct剖析 (4)
迸程标志
unsigned int flags;
task_ struct剖析 (5)
进程亲属关系
说明
:
parent和real_ parent一般情况下是同一个进程,只有当进程被跟踪时,parent会去指向跟踪进程。.
task_ struct剖析 (6)
Linux内核PCB组织形式
(1)thread_ info
(2)thread_ union
thread_ info 与 stack
thread_ info 与stack紧密结合的好处:
①方便快速定位到task_ struct
(1)thread_ union占 据两个连续的页框,并且让第一个页框的起始地址是2的13次方的整数倍。
(2)esp寄存器中存放的当前栈指针。
(3)屏蔽掉esp的后13位,就可以得到thread_ info结 构的地址
由下列指令完成:
这样获得的是thread_ info的基地址,由于task字段在thread_ info结构中的偏移量是0,因此,p就指向了当前进程的task_ struct。
这就是内核中经常要用的current
宏的实现。注: current宏永远指向系统当前运行的进程。
②在多处理器系统上,每个硬件处理器通过检查栈就可以获得当前正确的进程。早期Linux版本中,引入全局变量current来标识当前正在运行的进程描述符,在多处理器系统中,有必要把current定义为一个数组,每个元素对应一个可用的CPU。
Linux内核线程
(1)Linux实现线程的机制非常独特,从内核的角度来说,并没有线程的概念
。
(2)Linux把所有的线程都当做进程来实现,内核并没有准备特别的调度算法
专门为线程服务,线程和进程使用同样的结构体
(3)task_ struct
, 所以在内核看来,线程看起来像是一个普通的进程,只不过线程和其他一些进程共享某些资源
,比如:地址空间。
Linux进程和线程
Linux不区分进程和线程,统称为任务(task
)。
Linux应用层创建进程接口–fork()
。
Linux应用层创建线程接口–pthread_ create()
。
三个系统调用如何调用do_ fork
Linux进程和线程
创建参数
do_ fork()
Linux中三个特殊的进程
内核三个特殊进程
IDLE进程: PID=0
init进程: PID=1
kthread进程: PID=2
内核创建三个进程的顺序是,先创建0号,由0号创建了1、2号其中IDLE进程是静态创建的,内核中唯一个不是由kernel_ thread创建的进程。
1号init进程,是所有用户态进程的父进程。
2号kthread进程,是所有内核线程的父进程。
Linux内核线程的创建
(1)内核经常要在后台进行一些操作,这种任务可以通过内核线程完成。
(2)内核线程和普通线程的区别在于,它们只运行在内核空间,从来不会切换到用户空间去。
Linux创建内核线程的过程
内核线程的创建接口
threadfn
:新线程运行函数
data
:新线程传参
namefmt
:新线程的命名.
新创建的线程默认处于不可运行状态,需要调用wake_ up_ process()明确地唤醒它。或者,你也可以直接调用kthread_ run()直接创建一个线程并让它运行起来,相当于kthread_ create()+wake_up process()的效果。
Linux内核线程的退出
内核线程启动后就一直运行直到调用do_ exit()退出,或者内核的其他部分调用kthread_ stop()终止它。
kthread_ create实现