集美那里有教网站建设,前端培训多少钱,营销推广方案设计,上海近期新闻事件最近#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助#xff0c;所以我在此分享。点击这里跳转到网站。 目录 一、进程状态1.1运行状态1.2阻塞状态1.3挂起状态 二、具体L…最近我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念而且内容风趣幽默。我觉得它对大家可能会有所帮助所以我在此分享。点击这里跳转到网站。 目录 一、进程状态1.1运行状态1.2阻塞状态1.3挂起状态 二、具体Linux中的进程状态2.1看看Linux内核源代码怎么说2.2进程状态查看D磁盘休眠状态Disk sleepT停止状态stopped 三、僵尸进程3.1僵尸进程危害 四、孤儿进程小结 博客主页小智_x0___0x_ 欢迎关注点赞收藏✍️留言 系列专栏Linux入门到精通 代码仓库小智的代码仓库 一、进程状态
进程状态在操作系统中可以分为五大状态分别是
新建状态一个进程刚被创建把PCB和对应的代码和数据刚申请出来尚未进入就绪队列就绪状态进程已经分配到除CPU以外的所有必要资源只要再获得CPU便可立即执行进程这时的状态称为就绪状态阻塞状态也称为等待或睡眠状态一个进程正在等待某一事件发生例如请求I/O而等待I/O完成等而暂时停止运行这时即使把处理机分配给进程也无法运行故称该进程处于阻塞状态1。处于阻塞状态的进程也可能有多个通常将它们排成一个队列称为阻塞队列运行状态进程占有处理器正在运行的状态。进程已获得CPU其程序正在执行1终止状态指进程完成任务到达正常结束点或出现无法克服的错误而异常终止或被操作系统及有终止权的进程所终止时所处的状态
1.1运行状态
当多个进程需要运行时它们会竞争CPU资源。在极端情况下如果只有一个CPU那么众多进程就必须通过调度器来合理使用CPU资源确保资源的均衡使用。为了管理这些进程每个CPU都会维护一个自己的运行队列struct runqueue。这个运行队列是一个重要的数据结构用于存储当前CPU上待运行的进程。
每个进程都有一个对应的task_struct结构体这个结构体包含了进程的各种属性。而运行队列中最重要的属性是head和tail指针它们分别指向队列的头部和尾部。这意味着排队的不是进程的代码而是进程的PCB在排队。
当CPU需要运行某个进程时它会从运行队列中挑选一个进程来执行。挑选的过程是由调度器完成的它会根据一定的算法从队列中选择一个合适的进程。一旦选定了进程调度器就会将该进程从队列中移除并将其放到CPU上运行。 当多个进程处于运行队列中时它们所处的状态被称为运行状态r状态。这意味着这些进程已经准备好可以随时被调度执行。当一个进程开始运行时它将自己置于CPU上并执行。然而一个进程并不需要一直执行到完成才让出CPU。例如如果有一个进程陷入了一个无限循环中它可能会一直占用CPU导致其他进程无法得到调度和执行。
为了防止这种情况的发生操作系统引入了时间片的概念。每个进程都被分配了一个时间片它定义了该进程在CPU上的最大执行时间。一旦一个进程的执行时间超过了它的时间片操作系统会强制将其从CPU上移除并将其放回运行队列的尾部等待再次调度。
时间片的引入确保了每个进程都有机会在一段时间内得到执行从而实现了多个进程的并发执行。并发执行是指在同一时间段内多个进程的代码都会被执行。通过合理分配时间片操作系统可以确保所有进程都得到公平调度和执行从而提高了系统的整体效率和响应速度。
1.2阻塞状态
我们每个外设都有一个等待队列每个使用设备的进程如果想要使用某个设备就需要等待该设备变为就绪状态。如果设备当前不可用即处于不可读状态那么进程会自动将其PCB进程控制块加入到该设备的等待队列中。一旦设备变为可用状态即处于可读状态进程就可以从等待队列中取出PCB进入就绪状态等待CPU调度。如果设备仍然没有准备好进程就会在等待队列中等待这时我们称进程处于阻塞状态。因此当我们说让进程去某个资源中等待时实际上是将进程加入到该资源的等待队列中也就是让进程进入阻塞状态。 系统中可能有很多阻塞的进程每个都在等待资源或设备。进程间也可能互相等待。如果只有一个CPU那同一时间只能运行一个进程其他的都得等着。
1.3挂起状态
如果有很多进程都在等一个设备比如磁盘但磁盘一直没准备好这些进程就只能在内存里等着也不占用CPU。但有时候操作系统的内存可能会不够用因为每个进程都需要占用一些内存。如果一个进程只是等着没有被CPU调度那它的代码和数据其实在内存里是空闲的。为了解决这个问题操作系统可以把等待进程的代码和数据移到磁盘里去只留下一个PCB在内存里排队。这样当设备准备好了再把代码和数据从磁盘移回内存。这个过程叫做换出和换入。当代码和数据不在内存里时我们称这个进程为挂起状态。这样操作系统就可以空出一些内存给其他进程用。这个挂起状态适用于所有等待的进程。 挂起状态对用户是不可见的这是操作系统的一种行为。
上面介绍的这些属于操作系统学科的理论知识不同的操作系统可能会有不同的实现方案下面我们来深入研究研究具体的 Linux 操作系统中有哪些进程状态。
二、具体Linux中的进程状态
2.1看看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运行状态running: 并不意味着进程一定在运行中它表明进程要么是在运行中要么在运行队列里。S睡眠状态sleeping): 意味着进程在等待事件完成这里的睡眠有时候也叫做可中断睡眠interruptible sleep。D磁盘休眠状态Disk sleep有时候也叫不可中断睡眠状态uninterruptible sleep在这个状态的进程通常会等待IO的结束。T停止状态stopped 可以通过发送 SIGSTOP 信号给进程来停止T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。X死亡状态dead这个状态只是一个返回状态你不会在任务列表里看到这个状态。
2.2进程状态查看
我们先来写一段代码观察效果
int main()
{while(1){printf(hello linux\n);}return 0;
}可以看到这里并不是R状态而是S状态这是因为当程序疯狂地进行打印操作时你的设备可能并不处于可以直接写入的状态。因此你的进程有很大可能性一直处于等待状态。你可能认为你的进程正在直接向设备写入数据但事实上操作系统内部你的进程可能大部分时间都在等待I/O设备如显示器就绪。因此你的进程状态一直显示为S状态这个S状态实际上对应着我们操作系统原理中的阻塞状态。
我们再来把代码修改一下去掉printf:
int main()
{while(1);return 0;}当去掉printf语句后程序就不再涉及I/O操作而是纯粹地运行计算任务。在这种情况下进程的状态会变成R状态也就是运行状态。这是因为没有I/O等待进程可以一直占用CPU进行计算直到任务完成。所以当没有I/O操作时进程就处于运行状态也就是R状态。
查询结果中显示的表示该进程在前台运行这意味我们此时在 bash 命令行输指令是不会有任何反应的可以在输入指令的后面加上此时表示让该进程在后台运行要终止掉该进程只能通过指令kill -9 PID给进程发送终止信号。
D磁盘休眠状态Disk sleep
如果一个进程处于D状态我们也称之为disk sleep在Linux系统中它也是一种阻塞状态只不过这种状态在操作系统层面被称为深度睡眠。相对应的我们之前提到的S状态可以被称为浅度睡眠。就像人们睡觉时有深浅之分浅度睡眠可以被直接唤醒比如被程序员或其他进程唤醒。
我们再来通过一个有趣的故事来了解D状态
有一个进程要完成I/O操作此时进程呢他那朝着远端的磁盘大喊了一声磁盘啊我这有1GB的数据我把数据存到你存到磁盘的某个位置上你帮我做一下吧然后呢当磁盘听到这句话磁盘慢慢悠悠的探出来个脑袋就给进程说好的那你把数据给我吧我去帮你写于是磁盘就抱着进程的数据去做写入了磁盘进行写入时这个进程它翘着二郎腿悠哉悠哉的在这里等待此时操作系统它从旁边路过操作系统现在压力很大不知道什么原因反正整个计算机里进程变得非常多了而且每一个地方都特别吃资源内存资源严重不足当前操作系统火急火燎的在想办法当他从这个进程旁边路过的时候他瞥了一眼这个进程觉得他很不爽他把他能做的全做了包括进程所对应的代码和数据能置换全置换了所以操作系统一气之下啊就对着这个进程说你这个进程这么没眼色你没看到当前内存已经被撑得快爆了吗我已经马上快挂掉了你还在这里翘着二郎腿你还在等着呢不要等了所以操作系统直接把这个进程干掉了如果系统压力已经在内存辗转腾挪已经解决不了时操作系统就要开始动手杀进程了所以操作系统是会杀掉处于某一些他自己判定不太重要的进程 删掉了之后此时这个刚刚在写入数据的磁盘也遇到了问题当前他在写入时发现写到一半时突然发现磁盘空间不够了这个磁盘就转过头探出脑袋就对这个进程说进程啊不好意思我写入失败了唉进程呢进程怎么不见了那我数据没有写成功该怎么办呢我到底是再尝试一次呢还是我再等一等我该怎么做决策呢有的硬件直接丢掉的有的给你再试着写一下大部分都是掉直接丢掉此时数据就被丢失了丢失的数据非常重要。于是呢法官就把操作系统进程和磁盘一并带上了法庭法官先审问操作系统说你怎么能杀掉进程呢操作系统却说“请问用户有没有赋予我管理他软件资源的权利请问我杀掉进程是不是在特别极端的情况下杀掉了请问我有没有履行我操作系统的职责我的职责是保证系统不挂数据丢失和我有什么关系我就是在做我操作系统该做的事情如果你判我有罪了那么请问下次如果再碰到这样的极端情况那到底我还做不做我如果不杀最后导致操作系统挂掉第一它的数据该丢还是会丢第二可能还会影响其他进程那么这个责任谁来承担。”法官说“唉这话说的还挺有道理啊是的他只是履行了他的义务而且他确实是在极端情况下做的这个事情”于是法官又把目光转移到了磁盘身上对磁盘说“你怎么能把人家数据丢掉呢”此时磁盘就说“法官大人不要怪我在这件事情上我就是个跑腿的人家让我干啥就干啥我在写入的时候就已经告诉了对方我可能会失败我让他去等的我要给他结果的我的工作模式从来向来都是这样从我出生的时候就是这样其他磁盘也是这样如果你认为我有罪的话那是不是我的兄弟磁盘我的朋友磁盘也有问题那是不是我们把储存的逻辑全部都改下我就是个跑腿的我怎么能决定这个数据写入失败该怎么办数据被丢失是因为我还有其他事情要做因为有其他进程也要让我写入”法官听了之后觉得也有道理那么就把视角转向了受害人进程的身上反正操作系统没错磁盘也没错那就是你的问题了在法官刚看向进程的时候进程扑通一下就跪下来了对法官说“法官我可是受害人啊我是被杀掉的我就是静静的坐在那里人在家中坐锅从天上来我是被杀掉的我怎么能有错呢啊”。法官此时一想唉他们三个好像说的都挺有道理难道是我错了吗 凡是有争议的地方那么一定是因为制度设计不合理 法官最后说算了算了你们三个都回去吧我完了去改改操作系统法官就把进程的状态加了一个状态那么这个状态就是只要这个进程当前正在有写入任务交给了磁盘如果磁盘没有办法立马响应的话需要进程等待这个进程绝对不能以浅度睡眠的方式运行即S状态必须把自己设为D状态从我们的源代码方式规定D状态进程任何人都不能杀掉包括操作系统 所以故事又来了这个进程呢又喊出来磁盘啊磁盘说怎么又是你进程说没关系这次写吧这次我不怪你了所以呢磁盘抱着数据就去进行写入了那么当前这个进程翘着二郎腿在这里啊嗑着瓜子在这里等一个操作系统路过了操作系统说你这个进程话还没说完进程就亮出了一个免死金牌一边看去不要影响我我还忙着呢你现在是没资格杀我啊所以操作系统一看行啊有这回事就行反正我尽力了人家不让杀他就不让杀我去那么去杀别的进程反正对我来我做了我的工作你没有被杀死那后果自己承担所以当前进程的此时就处了一个状态叫D状态当它进行等待时进程不可被杀死这样的话就不会存在刚刚我们的这种问题了所以当磁盘把数据写完之后返回来时告诉进程进程啊你的数据写完了这个时候得到了结果之后进程再把自己的状态由D状态恢复成R状态。
我们最后再来总结一下这个小故事 在这个故事中一个进程请求磁盘保存1GB数据。磁盘接受了任务进程则等待。由于系统内存紧张操作系统决定杀掉该进程以释放资源。磁盘在写入过程中发现空间不足但进程已被杀死数据丢失。法官审问后决定给进程增加D状态保证正在进行I/O操作的进程不会被杀死。这样即使操作系统再次路过也无法杀掉处于D状态的进程避免了数据丢失。
D状态也是阻塞状态的一种
T停止状态stopped
T状态称之为暂停状态也叫做stop状态我们来举一个例子
int main()
{while(1){printf(hello linux\n);sleep(1);}return 0;}此时我们的进程还是S状态. 接下来我们给进程发送信号来暂停进程kill -19 759: 可以看到我们的进程被stop了我们再来查一下进程状态 此时进程就变成了T状态我们想让进程再次跑起来可以给进程发送18号信号kill -18 759: 可以看到程序又正常跑了起来再来查看进程状态 这里又变成了S状态但是却少了这是因为我们将进程暂停启动之后进程就会变成后台进程。此时我们想要杀掉这个进程就只能使用kill -9来杀死进程。
当一个进程处于T状态一般是不会接受信号除了特殊信号我们使用gdb调试工具调试的时候设置断点程序遇到断点停止时候就会处于t状态。 T和t的区别 在进程状态中“T”代表停止状态或常规暂停而“t”代表追踪停止。二者的主要区别在于“T”状态通常可以通过发送SIGSTOP信号给进程来使其停止而这个被暂停的进程可以通过发送SIGCONT信号来继续运行。“t”状态主要发生在进程被调试过程中遇到断点时此时进程会进入追踪停止状态。
三、僵尸进程
如果今天我们有父和子两个进程父进程一直在运行并且父进程不关心对应的子进程而子进程直接退出之时他并不是退出之后立马要将自己的所有资源全部释放因为父进程没有还没有来关心他那么此时操作系统就必须把子进程的状态一直给我维持着直到我的父进程开始关心他那么其中我们把这种已经死掉的但是当前需要由父进程来关心此时这个进程所维持的这种状态我们称之为Z状态。
下面我们再来写一段代码验证一下 子进程执行完5次打印后就处于 Z 状态,等待父进程来回收它的资源。处于 Z 状态的进程的相关资源尤其是 task_struct 结构体不能被释放。只有当父进程把子进程的相关资源回收后子进程才能变成 X死亡状态。我们将这种处于 Z 状态的进程就叫做僵尸进程如果父进程一直不来回收那这种进程会长时间占用内存资源造成内存泄漏。后面我们会介绍解决这种问题的方法(waitpid())感兴趣的老铁可以期待后续更新
3.1僵尸进程危害
进程的退出状态必须被维持下去因为他要告诉关心它的进程父进程你交给我的任务我办的怎么样了。可父进程如果一直不读取那子进程就一直处于Z状态。维护退出状态本身就是要用数据维护也属于进程基本信息所以保存在task_struct(PCB)中换句话说Z状态一直不退出PCB一直都要维护。那一个父进程创建了很多子进程就是不回收就会造成内存资源的浪费因为数据结构对象本身就要占用内存想想C中定义一个结构体变量对象是要在内存的某个位置进行开辟空间内存泄漏
四、孤儿进程
上面的例子中我们是让子进程先退出父进程一直运行接下来我们让父进程先退出子进程一直运行我们再来看看会有什么结果。
#include iostream
#include unistd.h
#include cstdlib
using namespace std;
int main()
{pid_t idfork();if(id0){int cnt 500;while(cnt){printf(i an child,pid:%d,ppid:%d,cnt:%d\n,getpid(),getppid(),cnt);cnt--;sleep(1);}printf(child quit!\n);exit(0);}else{int cnt5;while(cnt){printf(i am parent,pid:%d,ppid:%d,cnt:%d\n,getpid(),getppid(),cnt);sleep(1);cnt--;}printf(parent quit!\n);}return 0;}我们可以看到父进程在执行结束后就只剩下子进程为什么父进程不会处在 Z僵尸状态呢答案是父进程也是 bash 的子进程父进程在执行结束后它的父进程 bash 会将其回收掉并且过程非常快所以我们我们没有看到父进程处在 Z僵尸状态。其次我们发现当父进程结束后它的子进程的父进程会变成1号进程即操作系统。我们将父进程是1号进程的进程叫做孤儿进程该进程被系统领养。因为孤儿进程未来也会退出也要被释放。
小结
今天我们学习了【Linux】探索Linux进程状态 | 僵尸进程 | 孤儿进程相信大家看完有一定的收获。种一棵树的最好时间是十年前其次是现在 把握好当下合理利用时间努力奋斗相信大家一定会实现自己的目标加油创作不易辛苦各位小伙伴们动动小手三连一波~~~本文中也有不足之处欢迎各位随时私信点评指正