文章目录
1.进程主要状态
进程运行状态
①内存中有一个运行队列,其中有两个指针,一个指向已经准备好且用双链表链接起来、等待被调度运行的头进程PCB地址,另一个指向尾部进程,这样便可以很好地管理好这些等待被运行的进程了;
②运行队列中存在调度器,调度器通过计算进程时间片(进程被运行的时间),挑选进程放到CPU上去运行,这样便尽可能公平地使所有进程被调度;
③一个进程把自己放到CPU上运行,是不是要执行完毕才能把自己放下来呢?答案:当然不是!例如你使用C语言写的while(1)
语句,你在显示屏上的黑框框中看到该语句一直被运行,记住不要用你的感知去揣测CPU的运行速度;实际上每个进程在CPU运行一定的时间(可能10ms左右)就会被放回内存的运行队列继续等待,直到再次被运行,因为CPU运行速度太快了,你可能感觉不到,所以才会误以为你写的进程代码一直在被运行;当你while(1)
的语句在被运行的时候,你也可以使用浏览器、QQ等进程应用,这也可以说明进程是轮流在CPU上运行的!
总结:
进程阻塞状态
①我们的操作系统会将各种外设的属性信息,使用PCB结构体描述起来组织起来进行管理,描述外设的PCB结构体中,有指向等待队列的头尾指针,对需要等待外设输入的进程进行管理!
②举个栗子:我们使用scanf\cin
语句,当这个代码进程运行起来的时候,不能直接被放在运行队列中,因为它还没有准备好,在键盘输入前,会被放到等待队列上排队,在键盘输入后,该进程才会被放到运行队列中。
③进程处于等待外设完成信息交互(信息的输入、输出等)的状态,叫做进程的阻塞状态。
进程的挂起状态
如果很多进程处于阻塞状态,那么操作系统的内存资源会严重不足,而处于阻塞状态中的代码和数据处于空闲,在保证操作系统能正常运行的情况下,会将PCB进程对应代码和数据放到磁盘上,只需要PCB结构体在等待队列中排队即可,当外设输入信息时,再将磁盘中的代码和数据唤出,唤入到内存中;操作系统置换出来的空间,又可以存放其他PCB结构体,提高了内存空间的利用率!(给面试官投简历,只需要你的简历等待筛选即可,不需要你个人到场)
处于进程PCB在等待队列中排队,代码和数据等资源被存放到磁盘中,该状态称为进程的挂起状态
2.Linux进程状态
Linux进程内核源代码
一个进程可以有几个状态(在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 */
};
①R运行状态
我们创建一个mypro.c
文件写入如下代码:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("hello prosserstate\n");
}
return 0;
}
代码运行结果为:
在另一个窗口如下指令进行监测
$ ps ajx | head -1 && ps ajx | grep myproc |grep -v grep
检测的结果为:
我们对上面的代码稍作修改,再进行监测:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
// printf("hello prosserstate\n");
}
return 0;
}
监测结果为:
状态后面有无+号的区别
①状态后面有+号,如某进程的状态为R+
,说明该进程在前台运行;状态后面无+号,如某进程状态为R
,说明该进程在后台运行;在后台运行的进程使用ctrl+C
无法终止掉,需要使用kill指令才能杀掉该进程;在运行的进程后面加上&
,进程将会在后台运行
②S睡眠状态
将myproc.c
文件代码修改为:
#include<stdio.h>
#include<unistd.h>
int main()
{
int a=0;
printf("Enter# ");
scanf("%d",&a);
printf("echo>%d\n",a);
while(1)
{
sleep(1);
//printf("hello prosserstate\n");
}
return 0;
}
键盘准备输入的时候:
在另一个窗口输入如下指令:
while :;do ps ajx | head -1;ps ajx |grep myproc |grep -v grep;sleep 1;done
键盘输入之前监测的结果为:
键盘输入数字:
键盘输入之后监测的结果为:
使用top
指令监测机器上的各进程的状态:
③D磁盘休眠状态
小故事: 在操作系统内,每天都有许许多多的进程在被运行,有一天有一个进程是要向磁盘中写入1GB的数据,因为数据量太大的同时从内存中写入磁盘的速度太慢了,所以该进程只能空闲地等待把数据写入磁盘成功或者失败,等待磁盘反馈信息给进程,而此时刚好操作系统空间资源严重不足了,阻塞状态的进程也将代码和数据放到磁盘上处于挂起状态了,但还是杯水车薪;于是操作系统便开始观察起了在操作系统中的所有进程,这是在干嘛呢?当然是准备杀进程了!很不凑巧地是发现了一个啥事都不干的进程,看到这操作系统哪里能忍,该进程当然被杀掉了!而该进程就是等待1GB数据写入磁盘的进程,当磁盘写入失败时,磁盘屁颠屁颠地跑回来想告诉进程一声:“进程,不好意思啊,写入失败了!”却发现进程不见了,无奈地只好继续忙自己手头上的工作上去了,而丢失的数据又用户最重要的数据!于是,用户一怒之下将操作系统、进程、磁盘三者通通告上了法庭,这三者都将接收正义地审判!在法庭上,法官问操作系统:“操作系统你可治罪,为什么要杀掉进程呢?”操作系统理直气壮地说:“法律(规则)赋予了我权力,让我管理底层软硬件资源,给用户良好的体验,而这个进程什么都没干,要么我杀掉他,要么用户的界面会卡到崩溃!”法官转头问向了进程:“进程,你可知罪?"进程战战兢兢地说:”法官大人,我才是受害者,我冤枉啊!我在等待磁盘写入信息完毕后给我反馈,然后将反馈的信息汇报给用户,突然就被杀掉了,我太难了!“紧接着法官问磁盘:“磁盘,你可知罪?”磁盘哭唧唧地说:“法官大人,我昼夜不停地工作,丝毫不敢有任何懈怠,可写入数据信息慢,也不能怪我啊,写入数据失败,但是人家的空间不够了,我总不能把其他数据丢了继续写入吧?我只是一个打工的,只想好好地工作,呜呜呜!”法官皱着眉毛想:“他们三个好像都没有错误,可用户那边我怎么交差呢?”法官心生一计:“可以设置一种状态,进程在等待磁盘写入数据完毕期间,该进程不能被杀掉,该状态是针对今天这种状况,那就叫这种状态为磁盘休眠状态!”法官向用户承诺:”我已经颁布了一条法令,今后这种状况不会再出现了!“庭审结束一段时间后,操作系统资源又严重不足的,刚好看见一个进程等待大量数据写入磁盘中,该要”提刀“杀掉该进程,然而该进程不慌不忙地拿出”免死金牌“(即告诉操作系统我是处于磁盘休眠状态),操作系统只好作罢,向其他进程走去!
T停止状态
使用如下代码文件:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
sleep(1);
printf("hello prosserstate\n");
}
return 0;
}
kill指令控制进程暂停:
指令认识:
kill -9 Pid //杀进程
kill -19 Pid//暂停进程
kill -18 Pid//重新启动进程
使用暂停进程之前,进程所处的状态:
使用暂停进程之后,进程所处的状态:
进程重启之后所处的状态:
gdb调试观察T状态:
在另一个窗口输入如下指令:
ps ajx | grep myproc
监测结果为:
进程S状态和T状态的区别:
④X死亡状态
⑤Z僵尸状态
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
int cnt=5;
while(cnt--)
{
printf("i am child, pid: %d ppid:%d\n",getpid(),getppid());
sleep(1);
}
// exit(0);
}
else if(id>0)
{
while(1)
{
printf("i am father, pid: %d ppid:%d\n",getpid(),getppid());
sleep(1);
}
//父进程当前没有针对子进程做任何事情
}
return 0;
}
代码运行的运行和监测结果为:
僵尸进程的危害:
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
- 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的个位置进行开辟空间,会使操作系统发生内存泄漏!
⑥孤儿进程
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
while(1)
{
printf("i am child procsser,pid: %d ppid: %d\n",getpid(),getppid());
sleep(1);
}
}
else if(id>0)
{
int cnt=5;
while(cnt--)
{
printf("i am father procsser,pid: %d ppid: %d %d\n",getpid(),getppid(),cnt);
sleep(1);
}
}
return 0;
}
代码运行的运行和监测结果为:
查看Pid为1的进程
- 问题:父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?