目录
- 进程状态
- 1 具体状态
- 2 进程状态的意义
- 3 进程状态实例
- 3.1 R&S&T认识
- 3.2 认识Z & 僵尸进程 & 孤儿进程
- 僵尸进程
- 孤儿进程
进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
1 具体状态
1.1 运行状态
R
运行状态(running):
并不意味着进程一定在运行中,它表明进程要么是在运行中,要么在运行队列里。
一个进程处于运行状态,实质上是这个进程已经被放入运行队列
run_quene
当中,随时可被CPU进行调度。(等待CPU)
1.2 睡眠状态
当我们完成某种任务时,任务条件不具备,需要进程进行某种等待,此时这种状态称为 S 或 D。一个外设可能被多个进程等待,此时这个队列叫做等待队列
wait_queue
(等待外设)。当“等待成功”,进程将被设为R状态,进入运行队列run_quene
。
S
睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠interruptible sleep
),可能会被杀掉。 (浅度睡眠)
D
磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束,进程如果处于D状态,不可被杀掉。(深度睡眠)所谓的进程,在运行的时候,有可能因为运行需要,可能会在不同的队列里。
因此,在同一个队列里,所处的状态不一定是一样的。
我们把,从运行状态的task_struct
(run_queue
),放到等待队列中,就叫做挂起等待(阻塞)。
从等待队列,放到运行队列,被CPU调度就叫做唤醒进程!
1.3 停止状态
T
停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
t
(tracing stop):追踪导致的暂停状态(断点之类)。
1.4 死亡状态&僵尸状态
X
死亡状态(dead):这个状态只是一个返回状态,不会在任务列表里看到这个状态。
- 回收进程资源=进程相关的内核数据结构+相关代码和数据
Z
僵尸状态(zombie):进入僵死状态,辨别“死亡原因”,即进程退出的信息,系统将此信息保存在task_struct
里,供系统或父进程进行读取。
进程的状态信息存在task_struct
(PCB
)里。
2 进程状态的意义
方便OS快速判断进程,完成特定的功能,比如调度,本质是一种分类。
3 进程状态实例
3.1 R&S&T认识
代码
myproc.cc
#include<iostream>
#include<unistd.h>
int main(){
while(true){
std::cout << "hello world !" << std::endl;
}
return 0;
}
运行并查看进程状态,为
S+
查看可执行的kill,并停止目标进程
查看停止后进程状态,为T
使进程继续运行,kill -18
,并查看进程状态,此时我们发现,进程状态为S
,并且使用ctrl+c
不能停止进程了
这是因为此时进程在后台运行,此时需用kill -9
(SIGKILL
)杀掉进程
- 前台运行:直接使用
./myproc
- 后台运行:使用
./myproc &
,后台进程,可以在当前页面继续执行其他命令
3.2 认识Z & 僵尸进程 & 孤儿进程
僵尸进程
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
首先通过下面这个例子认识一下Z进程。
myproc.cc
#include <iostream>
#include <unistd.h>
int main(){
pid_t id = fork();
if(id == 0){
//child
while(true){
std::cout << "I am child, running !" << std::endl;
sleep(2);
}
}
else{
//parent
std::cout << "father do nothing!\n" << std::endl;
sleep(50);
}
return 0;
}
运行
./myproc
,使用while :; do ps axj | head -1 && ps ajx | grep myproc | grep -v grep; sleep 1; echo "############################################"; done
查看当前进程状态使用
kill -9
杀掉子进程可以看到,因为无回收的操作,子进程成为僵尸进程。
父进程如果一直不读取,那子进程就一直处于
Z
状态,Z
状态一直不退出,PCB一直都要维护。长时间不回收,占用系统资源,而C中定义一个结构体变量,是要在内存的某个位置进行开辟空间,此时僵尸进程的存在也有可能造成内存泄漏。
如果父进程先”死“,那么子进程会如何,谁来回收它呢?
孤儿进程
父进程如果提前退出,那么子进程后退出,进入Z
之后,那该如何处理呢?父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init
进程领养,由init
进程回收。
myproc.cc
#include <iostream>
#include <unistd.h>
int main(){
pid_t id = fork();
if(id == 0){
//child
while(true){
std::cout << "I am child, running !" << std::endl;
sleep(2);
}
}
else{
//parent
std::cout << "father do nothing!\n" << std::endl;
sleep(10);
exit(1);
}
return 0;
}
开始运行。
10秒后,父进程结束,子进程依然在。并且进程状态由
S+
变为S
,ppid变为1,称此时的进程为孤儿进程,而”领养孤儿进程“的1号进程通常为操作系统。