赞
踩
Linux内核源代码中,进程的状态是用数字来表示的,为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里面,进程有时候也叫任务)
/* * 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 */ };
下面这张图片展示了进程状态之间的关系,现在我们完全不知道这是什么东西!我们接下来逐一分析。
下面是进程的几种状态,接下来我们将逐一介绍
目录
答:所谓的运行态,是只要在运行队列中就叫做运行态,代表我已经准备好了,随时可以被调度!
答:进程还在,只不过不运行。
答:我们要释放进程需要花费时间,有没有可能,当前操作系统很忙呢。
如果我们申请CPU资源,暂时无法得到满足,需要排队的--运行队列
那么如果我们申请其他慢设备的资源呢? --- 也是需要排队的!(tast_struct在进程排队)
当进程访问某些资源(磁盘,网卡),该资源如果暂时没有准备好,或者正在给其他进程提供服务,此时:1.当前进程要从runqueue中移除;当前进程放入对应设备的描述结构体中的等待队列!(wait_queue)
当我们的进程此时在等待外部资源的时候,该进程的代码不会被执行,此时进程处于的状态叫做进程阻塞。
阻塞是一种临时状态。
当内存不足的时候,操作系统会将该进程的代码进行辗转腾挪,如何辗转腾挪--短期内不会被调度的进程,他的代码和数据依然在内存中!就是在白白浪费空间!操作系统就会把该进程的代码和数据置换到磁盘上!此时被暂时置换到磁盘上的进程就叫做进程挂起。
此时我们将进程的状态都一一介绍了,我们使用Linux操作系统再一一查看一下
首先我们写一段简单的C语言代码获取pid
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- while(1)
- {
- printf("这是一个进程 %d\n",getpid());
- sleep(1);
- }
- return 0;
- }
我们输入指令查看
ps ajx | head -1 && ps ajx | grep 'test'|grep -v grep
发现运行的进程怎么是S呢? 这是因为printf函数太快了,而显示器显示速度太慢啦,因此这个进程看似实在死循环,其实大多数时间都在等待显示器。那么怎么才能进入运行态呢,我们屏蔽掉printf函数,只保留while循环再次进行查看发现已经处于运行态了
s对应的状态一般叫做阻塞状态!
如何把睡眠状态叫醒呢?只需要把进程状态从S睡眠态转换成R运行态即可。
我们常常把S状态叫做浅度睡眠(也叫做可中断睡眠),浅度睡眠可以被唤醒或者杀掉他。
只要存在浅度睡眠,那么一定存在深度睡眠,就是D状态
一般而言,Linux中如果我们等待的是磁盘资源,当进程等待磁盘资源拷贝写入的过程,此时进程处于阻塞状态,这种进程阻塞状态就是D状态(不可被中断),操作系统也不能杀掉该进程,只能等D状态进程自己醒来。等待磁盘给回应才能醒来。D状态和磁盘挂钩,一般不好演示得出。我们有感性的理解即可。
X(dead)状态就是所谓的死亡状态。
Z(zombie)状态是僵尸状态。Z状态是一种已经死亡的状态,但是死了之后,不要让操作系统释放它。那该状态存在的意义是什么呢?当一个Linux中的进程退出的时候,一般不会直接进入X状态(死亡,资源可以立马回收),而是进入Z状态,为什么?
因为进程被创建出来一定是要有任务完成,当进程退出的时候,我们怎么知道进程把任务给我们完成了呢?需要将进程的执行结果告知给父进程或者操作系统。
子进程退出,维护Z状态,就是为了让父进程或者操作系统来读取执行结果!父进程和操作系统通过进程等待来读取僵尸进程的信息。下图是内核源码中的退出信息。
模拟僵尸进程?
创建子进程,子进程退出了,父进程不退出,也不等待子进程,子进程退出之后所处的状态就是僵尸状态。我们写一段代码来模拟一下!
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main()
- {
- pid_t id = fork();
- if(id == 0)
- {
- //child
- int cnt = 5;
- while(cnt)
- {
- printf("我是子进程,我还剩%dS\n",cnt--);
- sleep(1);
- }
- printf("我是子进程,我已经僵尸了,等待被检测\n");
- exit(0);
- }
- else
- {
- //父
- while(1)
- {
- sleep(1);
- }
- }
- return 0;
- }
我们在命令行写一段脚本,循环打印进程状态信息
- while :; do ps ajx | head -1 && ps ajx | grep 'test'|grep -v grep;
- sleep 1; echo"#########################################################";done
我们发现一开始当子进程还没有僵尸的时候,父子进程都处于S状态
当子进程僵尸时,我们发现此时子进程的状态变成了Z状态
僵尸状态后<defunct> 表示死者,死亡之意。
长时间僵尸,有什么问题呢?
如果没有人回收子进程的僵尸,该状态会一直维护!该进程的相关资源(tast_struct)不会被释放!会造成内存泄漏,因此一般必须要求父进程进行回收。
说到了僵尸进程,我们再谈谈与之相关的孤儿进程。
在我们们刚刚的代码中,子进程先退出,而父进程一直存在,那如果父进程先退出,子进程一直存在,此时子进程的父进程已经被回收了,子进程没有了父进程,我们把这种子进程处于的状态叫做孤儿进程。
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main()
- {
- pid_t id = fork();
- if(id == 0)
- {
- //child
- int cnt = 5;
- while(1)
- {
- printf("我是子进程,我还剩%dS\n",cnt--);
- sleep(1);
- }
- printf("我是子进程,我已经僵尸了,等待被检测\n");
- exit(0);
- }
- else
- {
- //f
- int cnt = 3;
- while(cnt--)
- {
- sleep(1);
- }
- }
-
- return 0;
- }
大约执行3秒过后,我们发现父进程不见了,之间子进程了,此时子进程就是孤儿进程,那么孤儿进程要被1号进程领养。这个1号进程就是操作系统。
我们发现子进程状态原先是S+,而后面为什么变成了S呢?并且这段代码不能被ctrl +c强制终止了
其中带+号表示这个进程是前台进程,而前台进程能够被ctrl C的,而不带+的被称为后台进程,我们要终止这个代码,输入kill -9 pid即可杀掉。
在上面我们看到暂停状态有T和t两种状态,这两种状态都叫暂停状态,这两种状态没有特别大的区别,而有一种特殊情况对应着t状态。
暂停状态就是让运行的进程暂停一下,这个暂停状态可以理解为我们下载一个东西,我们让他暂停一下;在看视频的时候,暂停了一下。此时这个进程就处于暂停状态。
我们在Linux下模拟一下暂停状态,首先写一段死循环程序
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main()
- {
- while(1)
- {
- printf("hello\n");
- sleep(1);
- }
- }
当程序运行起来时,我们输入
kill -19 pid
此时我们做运行的代码被暂停了,此时进程所处的状态就是暂停状态,当我们想让其继续运行时,我们输入
kill -18 pid
而t状态时,当我们使用gdb调试代码时,打上断点后,我们r起来,我们发现了t状态,t表示tracing,表示追踪的意思,当进程被调试的时候,遇到断点所处的状态就是t状态!
(本篇完)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。