上篇文章:Linux基础4-进程1(操作系统,进程介绍,Linux进程相关命令,getpid,fork)-CSDN博客
本章重点:
进程状态相关知识
目录
[1. 进程常见的状态](#1. 进程常见的状态)
[3. Linux操作系统是如何区分这些状态的](#3. Linux操作系统是如何区分这些状态的)
[3.1 R(运行状态)与 S(睡眠状态)](#3.1 R(运行状态)与 S(睡眠状态))
[3.2 T(停止状态)状态, t(短暂暂停)状态,D(深度睡眠)状态](#3.2 T(停止状态)状态, t(短暂暂停)状态,D(深度睡眠)状态)
[3.2.1 前后台进程](#3.2.1 前后台进程)
[3.2.2 t状态](#3.2.2 t状态)
[3.2.3 D状态](#3.2.3 D状态)
[4. Z状态与僵尸进程](#4. Z状态与僵尸进程)
[5. 孤儿进程](#5. 孤儿进程)
[6. 下章重点](#6. 下章重点)
[6.1 进程优先级](#6.1 进程优先级)
[6.2 进程的竞争性,独立性,并发,并行](#6.2 进程的竞争性,独立性,并发,并行)
1. 进程常见的状态
运行,新建,就绪,挂起,阻塞,等待,停止,挂机,死亡等
2.普遍的操作系统理解进程状态
在os中,os会管理一个运行队列
1 一个cpu会拥有一个运行队列
2 让进程进入运行状态的本质,将进程对象的task_struct(进程控制块PCB)放入运行队列
3 进程对象的PCB在进程队列中,该进程的状态才是运行状态,并不是进程在运行就是运行状态
4 进程会等待占用CPU资源,进程也会去占用外设的资源
5 操作系统通过将进程对象的PCB放到不同的执行队列来改变其状态
6 所谓的进程不同的状态,本质是在不同的执行队列中,等待某种资源
关于阻塞和挂起:
阻塞状态是当进程需要资源而处于等待时的状态。
阻塞状态的进程可能不会立即被调度,它会占用内存来等待资源。
但是,被阻塞的进程状态过多,内存资源会不够,这个时候操作系统就会把这些进程的数据和代码保存在磁盘上。
这样就能够节省一定的内存资源,将进程的这种状态称为挂起状态。
当被挂起的状态等待的资源到达时,操作系统再将磁盘的数据和代码加载到内存中继续运行。
两种状态之间的转化如下:
3. Linux操作系统是如何区分这些状态的
上图是Linux 代码中对进程状态的描述
有R(运行状态) S(睡眠状态) D(深度睡眠) T(停止状态) t(短暂停止) Z(僵尸状态) X(死亡状态) 七种状态
3.Linux中查看进程状态
3.1 R(运行状态)与 S(睡眠状态)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
while(1)
{
int a = 1;
a+=1;
}
return 0;
}
该代码是一个死循环,方便我们观察状态
适当修改代码,死循环的同时打印a值
cpp
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int a = 1;
while(1)
{
printf("%d\n",a);
a+=1;
sleep(1);
}
return 0;
}
这个由R状态变为S状态的原因:
printf输出到外设(显示器),进程需要等待显示器就绪,需要等待较长的时间(相对于cpu)
所以:99%显示的是S状态,1%显示的是R状态
3.2 T(停止状态)状态, t(短暂暂停)状态,D(深度睡眠)状态
cpp
kill -19 进程pid //可以停止这个进程
kill -18 进程pid //可以重启暂停的进程
暂停状态是阻塞状态还是挂起状态?
对于用户来说:暂停状态就是阻塞了,是否挂起我们不知道
对于OS来说:是否要挂起阻塞状态由OS自行决定(用户不需要知道阻塞状态是否需要被挂起)
继续运行暂停的进程
使用 kill -18 之后,被暂停的process重新变成了S状态。
但是却不是变回S+,这是为什么??
我们使用 ctrl c 都无法强制退出process,这是因为process变为了后台进程
3.2.1 前后台进程
状态前面带+号的是前台进程,不带+号的是后台进程
后台进程无法使用ctrl c 终止,只能使用 kill 杀死
当一个前台进程被kill -19 暂停后,使用 kill -18恢复运行就会由前台进程变为后台进程
3.2.2 t状态
t状态是暂短暂停状态,如使用gdb调试一个进程的时候,该进程会处于t
3.2.3 D状态
在D状态下的进程无法被杀死,只有通过断电,或者进程自己醒来,自己解决
4. Z状态与僵尸进程
处于Z状态的进程称为僵尸进程。
当子进程退出之后,父进程没有读取到子进程的返回状态,子进程就会变为僵尸进程。且僵尸进程会一直等待父进程读取退出状态
子进程被创建出来 --> 为了完成任务 --> 父进程需要知道子进程完成的怎么样(通过子进程的退出信息)
当子进程退出了,父亲却没有回收子进程,这是一个问题!
只要我们创建一个父子进程,让父进程不退出,也不回收子进程。再让子进程退出,就能观察到处于僵尸状态的子进程
下面代码父亲处于死循环且不会回收子进程,子进程5秒后退出。
子进程退出之后就会变为僵尸进程
cpp
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)//正常为子进程
{
//childi
printf("我是子进程,我的pid为%d,我的ppid为%d\n",getpid(),getppid());
sleep(5);
exit(1);
}
else
{
//parent
while(1)
{
printf("我是父进程,我的pid为%d,我的ppid为%d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}
僵尸进程会占用内存,又不会被回收,这样会导致内存泄漏
5. 孤儿进程
如果子进程退出,父进程不回收子进程退出信息,子进程会变为僵尸进程
如果父进程先退出,而子进程不退出会怎么样呢??
cpp
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id > 0)//正常为子进程
{
//childi
printf("我是父进程,我的pid为%d,我的ppid为%d\n",getpid(),getppid());
sleep(5);
exit(1);
}
else
{
//parent
while(1)
{
printf("我是子进程,我的pid为%d,我的ppid为%d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}
父进程比子进程先退出,子进程就会变为孤儿进程,同时被1进程收养
如果不被领养的话,子进程会变为僵尸进程,被1收养之后可以被1进程回收。这样就不会造成内存泄漏
父进程先退出
1.这种现象一定会存在 2.子进程一定会被操作系统(1进程)领养 3.如果不领养,那么子进程退出的时候,对应的僵尸进程就没人回收了
4.所以被领养的进程就是孤儿进程 5.如果是前台进程创建了子进程,如果孤儿了,那么它会自动变为后台进程