我们或多或少了解到进程的状态分为:
运行,新建,就绪,挂起,阻塞,等待,停止,挂机,死亡…
首先解释一点,进程这么多状态,本质上都是用来满足不同运行场景的。
接下来我们从两个方面来理解进程的状态
1.普遍的操作系统层面理解进程的概念
总结:
1.一个cpu一个运行队列
2.让进程进队列,本质上是将进程的task_struct结构体对象放在运行队列中
3.进程pcb在运行队列,就是R,不是这个进程正在运行,才是运行状态
状态其实是进程内部的属性,放在task_struct,本质是就是int类型的数字来表示不同的状态(假如1:run 2:stop …)。
我们知道磁盘速度都是很慢的,进程或多或少会访问磁盘,但是磁盘也只有一个,那该怎么办呢?
只能是等待。
4.不要只以为,你的进程只会等待(占用)cpu资源,你的进程也可能随时随地要外设资源。
当cpu发现某个进程需要访问磁盘,就会把该进程从运行队列中剥离,如果这个磁盘现在正由其他进程使用,操作系统就会把该进程放在磁盘的等待队列里(将task_struct对象放在不同队列中),该进程会由运行状态变成阻塞状态。,当磁盘处理完进程,但是等待队列中的进程还是阻塞状态,操作系统会把该进程拿到系统中,把状态改成R(运行状态)再给cpu的运行队列中。
5.所谓的进程不同的状态,本质是进程在不同的队列中,等待某种资源。
进程处于阻塞状态,可能不会被立即调度,万一阻塞的进程有很多,内存空间不够了怎么办?
操作系统会把该进程对应的代码和数据暂时保存到磁盘上,pcb还在内存。这样就节省了一部分空间,然后把这一部分空间给别人使用。
此时的进程就处于挂起状态。
将进程的代码和数据,加载或者保存到磁盘叫做内存数据的唤入换出。
阻塞并不一定挂起,挂起一定阻塞。
进程的状态我们这里就说这么多,主要要知道,1.什么叫做运行;2.什么叫做阻塞;3.什么叫做挂起。
接下来我们从另一方面来了解进程
2.Linux是怎么做的(具体的linux操作系统的状态)
STAT 状态
#include
int main()
{
while(1)
{}
return 0;
}
R+ 运行
#include
#include
int main()
{
int a=0;
while(1)
{
printf("%d\n",a++);
sleep(1);
}
return 0;
}
S+ 休眠
printf会把内容打印到显示器上去,显示器是一个外设,外设特点就是慢,等待显示器就绪,cpu需要花较长的时间。99%都是在等IO就绪,1%再执行打印代码。
所有进程访问外设行为基本都是S(阻塞状态的一种)
kill -l //查看kill命令
kill -19 编号 //暂停进程
T 暂停(阻塞的一种)
kill -18 编号 //继续进程
这里注意一点,在睡眠的状态是S+,当我暂停进程,又继续进程之后。这个进程状态就变成S了;
状态后面带+,属于前台进程,在xshell输入任何指令没用,除了用ctrl+c,结束进程。
状态后面不带+,属于后台进程,在xshell输入指令都有效,使用ctrl+c不能结束进程,只能使用kill -9 杀死进程。
ctrl+c不能杀死后台程序
输入指令还有效
对于后台程序只能通过kill -9 杀死进程。
D 深度睡眠
讲个小故事帮助我们理解D状态。
今天我想让进程帮我向磁盘写个超大份的数据,磁盘答应给我写,那么磁盘就开始写了。写了一会操作系统发现我们电脑变得很卡,操作系统是很聪明的,它去查看,发现这个进程导致电脑变卡,如果不删去这个进程而去删别的进程就会导致其他进程有意见,所以操作系统决定给它删了,过了一会磁盘写数据遇见错误,呼叫进程,但是进程被删了,所以最终导致写入数据失败。当我发现写入数据失败,我问它们三个到底是谁的责任?
请问是操作系统的责任吗?请问是进程的责任吗?请问是磁盘的责任吗?
操作系统回答说,不是我的责任,这是我们赋予它的权力,为了保证系统正常运行。
进程回答说,不是我的责任,我是被操作系统给删掉了。
磁盘回答说,也不是我的责任,我只负责任写。
那么为了想把数据写到磁盘里,我只能给这个进程发一个免死金牌,让操作系统不能删掉它。
像在高级IO的情况,该进程就处于D(深度睡眠),在该状态的进程,无法被操作系统杀掉!只能通过断电,或者进程自己醒来才能解决
S+ 休眠,属于浅度睡眠可以被终止
t 也是暂停状态,表示该进程正在被追踪,等待指令,当调试的时候。出现该状态
X(dead)死亡状态
Z(zombie)僵尸状态
为什么会有僵尸状态?(感性的认识)
进程被创建出来,是为了完成任务。
1.要知道它完成的如何。2.不关系结果。
Linux是一个你可以不要,但我要给。
所以进程退出的时候,不能立即释放该进程对应的资源(代码和数据释放掉,PCB保留),保存一段时间,让父进程或者操作系统来进行读取,因什么原因而退出。这段时间内进程处于僵尸状态。
所谓僵尸状态就是:当进程退出的时候,如果没有对该进程回收,此时进程处于僵尸。
我们在linux下见见僵尸状态
具体做法,子进程退出,但是父进程和操作系统没有对其回收------> 创建一个子进程,让父进程不要退出,而且什么都不做,让子进程正常退出即可。
#include
#include
#include
int main()
{
pid_t id=fork();
if(id == 0)
{
//子进程
int cnt=10;
while(cnt--)
{
printf("子进程, pid : %d , ppid : %d ,ret : %d ,cnt=%d\n",getpid(),getppid(),id,cnt);
sleep(1);
}
exit(-1);
}
else if(id > 0)
{
//父进程
while(1)
{
printf("父进程, pid : %d , ppid : %d ,ret : %d\n",getpid(),getppid(),id);
sleep(3);
}
}
return 0;
}
所谓孤儿进程就是,当父进程先退出,这个子进程会被操作系统(1号进程)领养,这个子进程叫做孤儿进程。
这里要说明的是:
1.父进程先退出这种情况一定是存在的。
2.子进程会被操作系统(1号进程)领养。
3.那为什么要领养呢?
如果不领养,那么子进程退出的时候,对应的僵尸便没有人能回收了。造成了资源浪费。
4.被领养的进程----孤儿进程
5.如果是前台进程创建的子进程,变成了孤儿,会自动变成后台程序,只能被杀死。
int main()
{
pid_t id=fork();
if(id > 0)
{
//父进程
int cnt=10;
while(cnt--)
{
printf("父进程, pid : %d , ppid : %d ,ret : %d ,cnt=%d\n",getpid(),getppid(),id,cnt);
sleep(1);
}
exit(-1);
}
else if(id > 0)
{
//子进程
while(1)
{
printf("子进程, pid : %d , ppid : %d ,ret : %d\n",getpid(),getppid(),id);
sleep(3);
}
}
return 0;
}
发现当父进程退出,子进程会被操作系统(1号进程领养)。并且子进程由前台程序变成了后台程序,只能被杀死。
关于进程的状态我们就说这么多,喜欢的点赞,评论,收藏+关注。下篇继续了解更多关于进程的知识。