自己并没有学习过计算机操作系统这门课,而计算机操作系统里有关进程状态的知识点,是一种相对抽象的概念,它对于任何一个具体的操作系统中进程状态而言都是适配的,也使得我们理解这样的概念时很难理解,并且我也不知道这门课中都具体有哪些概念,只学到了几个,而学习的重点是Linux操作系统下进程的状态,明白了具体的状态之后,再去理解操作系统这门课中的概念时就有了映射的实际实例,会好理解很多。
进程状态本身也是PCB中的一个字段。
目录
操作系统:进程状态
新建:指的是进程刚被创建好,还没有被CPU调度的状态,事实上,Linux下并没有这种状态,而操作系统中有此状态也是为了整体更加严谨,完善。
运行:进程的task_struct被CPU调度或者在CPU的runqueue(运行队列)中等待被调度,都称为运行态(另一种说法是,被CPU调度为运行态,在运行队列中是就绪态)
阻塞:等待非CPU资源就绪的状态,例1,比如要进行文件读取,在硬盘的执行队列中等待硬盘的响应,就是阻塞状态。 例2,scanf时等待键盘的输入,也是阻塞状态,其实这个的场景还是非常多的。
挂起:类似于阻塞,情景比较少见,比较极端。当内存不足时,OS会置换长期不运行的进程的代码和数据到磁盘,而内存中只留进程的PCB,此时进程被称为挂起状态。
退出:进程结束的状态。
上方的理解可能有偏差,不严谨等等问题。更清晰全面的理解需要后期不断的学习。
Linux操作系统:进程的状态
上图是Linux内核源码中进程状态的示例,这够有说服力了吧....
R 运行状态(running)
并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。对应上方的运行态。
只运行死循环即可,但是前提是,循环内不可以有打印语句。
通过ps命令查看proc进程的状态,可以发现是R状态,即运行态。
S睡眠状态(sleeping)
意味着进程在等待非CPU资源的就绪(有时候也叫做可中断睡眠,(interruptible sleep))。
当while循环不断printf时,显示的是睡眠状态,这里可能并不是我们所预料的,因为看上去这个进程一直在运行,事实上显示睡眠态的原因是,可能这个进程等待显示器资源就绪的时间比上CPU执行的时间是50:1,也就是CPU的执行速度是非常快的,大多数时间都是等待显示器的资源就绪。(当一直查看这个进程状态时,少数会显示出R) (对应的就是上方的阻塞状态)
D磁盘休眠状态(Disk sleep)
有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
可以理解为,有些进程当在和磁盘进行数据读写时,不可以被中断,因为意外中断可能导致数据丢失等问题。
T停止状态(stopped)
可以通过发送 19号信号 SIGSTOP 给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
给进程发送19号STOP信号后,进程显示T状态,即暂停状态。
t 停止状态(tracing stop)
本质上也是暂停状态,只是如果使用gdb调试某进程,并使进程在某断点处停下,进程就处于t状态,而本质上,断点的本质就是程序在运行到某一行处时暂停了。
X死亡状态(dead)
这个状态只是一个返回状态,瞬时性非常强,你不会在任务列表里看到这个状态。它仅表示此进程已结束,告知OS,这个进程的数据可以回收了。
Z(zombie)-僵尸状态,僵尸进程
僵死状态(Zombies)是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返回代码时子进程就是一个僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程即进入Z状态。
事实上,每个进程正常退出时,都会经历很小的一段僵尸状态,这段时间内等待父进程读取这个子进程的退出代码。
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id > 0){ //parent
printf("parent[%d] is sleeping...\n", getpid());
sleep(30);
}else{
printf("child[%d] is begin Z...\n", getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
这样,在子进程退出后且父进程还在执行时,查看子进程状态,就是Z僵尸状态。
僵尸进程危害:
1. 父进程一直不读取子进程状态,子进程的Z状态就会一直维持下去。
2. Z状态一直不退出,这个进程的PCB就一直要维护。
3. 如果永远不回收,就会造成内存泄漏,因为PCB就会一直存在于内存中,不被释放。
孤儿进程
子进程退出,父进程不读取子进程的状态。子进程为僵尸进程。
父进程退出,子进程变成孤儿进程。那么,如果这个进程不被领养,则此子进程退出后就会变成僵尸进程,且永远不会被释放内存导致内存泄漏。所以,子进程必须被领养,且是被1号进程领养。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 pid_t ret = fork();
7 if(ret == 0)
8 {
9 while(1)
10 {
11 printf("child\n");
12 sleep(1);
13 }
14 }
15 else
16 {
17 int cnt = 5;
18 while(cnt)
19 {
20 printf("father:%d\n",cnt--);
21 sleep(1);
22 }
23 }
24 return 0;
25 }
可以看到,经过5s后,父进程退出,子进程被1号进程领养。 而当子进程退出后,就会由新的父进程:1号进程回收这个子进程。