前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Linux】进程状态

【Linux】进程状态

作者头像
用户11173787
发布2024-06-24 11:22:42
1020
发布2024-06-24 11:22:42
举报
文章被收录于专栏:破晓破晓

世界上有很多操作系统,常见的有:LInux,Windows,鸿蒙,Android等。每一种操作系统表达进程状态的方式都是不一样的,但大同小异,我们不能只谈进程状态而脱离了具体的系统。接下来,我们想分析一下主流操作系统都存在的几种状态,然后具体分析LInux下的进程状态。准备好了吗?开船了!!

什么是进程状态

进程状态是指操作系统对进程的运行状态进行的描述。操作系统通过跟踪进程的状态来控制和管理它们的执行。这些状态的改变是由操作系统内核调度器决定的,根据进程需要,进程可以在这些状态之间进行切换。进程的状态取决于它需要等待的事件和资源,以及操作系统内核的调度决策。因此,不同的进程可能会处于不同的状态。

主流OS都存在的进程状态

几乎所有的OS都包括下面这几种进程状态,但可能在名称上存在着差异。

运行状态(R状态)

我们知道,当可执行程序从磁盘等外设中加载到内存时,操作系统回味每一个进程创建一个task_struuct结构体,又称PCB,来保存有关该进程的所有属性。当该进程准备就绪,可以被CPU调用时,与此同时,可能会有多个进程同时处于准备就绪状态,这些进程所属状态就是运行状态(R状态),操作系统为了管理和有效这些处于运行状态的进程,就创建了一个运行队列

这里有如下几个问题需要说明:

1.处于运行状态的进程就一定在运行吗? 不是的,被放置在运行队列中的进程所处的状态叫做运行状态,可能同时会有不止一个进程处于运行状态。但是,我们主流的计算机都是单核的,即一个CPU,只有一个运行队列,同一时间只能有一个程序在运行。 2.进程处于运行状态,就一定会占用CPU资源吗? 不是的,进程处于运行状态,其意义是程序本身准备就绪。但它没有被执行,而是一直在等待CPU资源

阻塞状态

我们知道CPU这玩意虽然傻,但是运行速度非常快,所以在运行队列里等待CPU资源会非常快。相比之下,外设(磁盘,网卡,显示器)这些设备就显得尤其慢,也就意味着等待它的资源就绪会非常慢。当一个进程等到CPU资源,可以运行时,需要从外设中读取数据,外设运行起来非常慢,如果此时其他进程正在从外设中获取数据,该进程就要等待其他进行获取完毕。CPU不允任何进程耽误它的时间,所以就将这个进程的PCB放到它所需要的外设的等待队列里,等待外设的资源就绪。此时这个进程就属于阻塞状态

整个过程都体现了先描述再组织的计算机设计原理

注意:

一个进程从运行状态变为阻塞状态,是将其PCB从运行队列中拿到外设的等待队列中。注意,这里来回挪动的是PCB,而不是该进程的代码。

挂起状态

当一个进程处于阻塞状态时,就以为着该进程在短时间内不会被调度,那么该进程所加载到内存中的代码和数据在短时间内不会被使用。但是CPU的空间是非常有限的,所以CPU必须为计算机运行腾出足够多的内存空间。在CPU看来阻塞进程的代码和数据就是占着空间而不使用,操作系统岂能容忍这种浪费内存空间的行为,所以就暂时性的将该进程的代码和数据置换到外设中(外设中有一部分空间专门用来执行这个功能);留着该进程的PCB在队列中等待,当等到资源时,再将所属的代码和数据加载到内存中,这样不就节省了一部分内存空间了嘛!!,节省出来的空间可以加载其他的进程到内存。为了节省内存,将所属代码和数据暂时性的置换到外设中的进程,我们称该进程处在挂起状态。

有同学会问:这个进程如果准备就绪了,想回来怎么办?

我们要清楚,操作系统对任何进程都是平等的,都是按规则办事。这个进程给别人让空间,别人也回给这个进程让空间!!这就叫做进程数据的换入换出。

LInux是如何做到的?

如果对进程相关概念还不太清晰的,建议先看这篇文章:Linux进程

下面的状态我们也可以再内核源代码中找到:

代码语言:javascript
复制
/*
* 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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

可能,这些不太好懂,下面,我们用Linux系统具体阐述

1.R运行状态(running)

这是所有的操作系统都必须存在的一种进程状态。

就如同我们上面所提到的,并不是所有处于运行状态的进程此刻都在运行。

1.1见一见LInux下的R状态

可以看到,是可以存在多个运行状态的。 进程是R状态,不代表正在运行,代表可被调度。换句话说,进程只有是R状态才可被调度,其他状态要先转为R状态,才能被OS调度。

在这里插入图片描述
在这里插入图片描述

2.S睡眠状态(sleeping)

睡眠状态可以看作是一种阻塞状态 ,意味着进程在等待事件完成或在等待某种资源的就绪(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。

来看一段代码:

代码语言:javascript
复制
#include<stdio.h>

int main()
{
     while(1)
     {
       printf("hello world\n");
     }

}

my god!!,发生什么了?明明程序在运行,但为什么查出来的却是S状态呢?

我们知道:CPU运行效率非常快,外设运行效率却很慢。在这段代码中,我们使用了打印函数,需要访问我们的显示器(属于外设),很慢,等待显示器就绪需要很长的时间;也许,99%的时间都在等待,只有1%的时间在打印,所以我们查出来的极有可能是S状态。

3.T停止状态(stopped)

3.1kill命令

Linux中有一个关于进程操作的命令:kill,kill通过向进程发送特定的信号,来对进程进行特定的操作。

语法
代码语言:javascript
复制
kill [-s <信息名称或编号>][程序] 或 kill [-l <信息编号>]
参数说明
  • -l <信息编号>  若不加<信息编号>选项,则 -l 参数会列出全部的信息名称。
  • -s <信息名称或编号>  指定要送出的信息。
  • [程序]  [程序]可以是程序的PID或是PGID,也可以是工作编号。
常用信号
信号列表
代码语言:javascript
复制
# kill -l
1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX

信号释义 SIGABRT 由程序调用 abort时产生该信号。 程序异常结束。 进程终止并且产生core文件 SIGALRM timer到期, 有alarm或者setitimer 进程终止 SIGBUS 总线错误,地址没对齐等。取决于具体硬件。 结束终止并产生core文件 SIGCHLD 进程停止或者终止时,父进程会收到该信号。 忽略该信号 SIGCONT 让停止的进程继续执行 继续执行或者忽略 SIGFPE 算术运算异常,除0等。 进程终止并且产生core文件。 SIGHUP 终端关闭时产生这个信号 进程终止 SIGILL 代码中有非法指令 进程终止并产生core文件 SIGINT 终端输入了中断字符ctrl+c 进程终止 SIGIO 异步I/O,跟SIGPOLL一样。 进程终止 SIGIOT 执行I/O时产生硬件错误 进程终止并且产生core文件 SIGKILL 这个信号用户不能去捕捉它。 进程终止 SIGPIPE 往管道写时,读者已经不在了,或者往一个已断开数据流socket写数据。 进程终止 SIGPOLL 异步I/O,跟SIGIO一样。 进程终止 SIGPROF 有setitimer设置的timer到期引发 。 进程终止 SIGPWR Ups电源切换时 进程终止 SIGQUIT Ctrl+\,不同于SIGINT,这个是会产生core dump文件的。 进程终止并且产生core文件 SIGSEGV 内存非法访问,默认打印出segment fault 进程终止并且产生core文件 SIGSTOP 某个进程停止执行,该信号不能被用户捕捉。 进程暂停执行 SIGSYS 调用操作系统不认识的系统调用。 进程终止并且产生core文件 SIGTERM 有kill函数调用产生。 进程终止 SIGTRAP 有调试器使用,gdb 进程终止并且产生core文件 SIGTSTP Ctrl+z,挂起进程。 进程暂停 SIGTTIN 后台程序要从终端读取成数据时。 进程暂停 SIGTTOU 后台终端要把数据写到终端时。 进程暂停 SIGURG 一些紧急的事件,比如从网络收到带外数据。 忽略 SIGUSR1 用户自定义信号 进程终止 SIGUSR2 用户自定义信号 进程终止 SIGVTALRM 有setitimer产生。 进程终止

3.2观察T信号进程
代码语言:javascript
复制
kill -19 pid  # 停止pid进程

前台进程和后台进程

你知道吗?进程分为前台进程和后台进程。

我们来上一段代码

代码语言:javascript
复制
#include<stdio.h>

int main()
{
     while(1)
     {
       printf("hello world\n");
     }

}

我们在vscode上运行起来,发现运行状态为S+(运行状态为什么是S,请看前面的内容),其中“+”

号表示这是一个前端进程(前台进程),这时,我们在命令行中输入指令,bash没有任何的反应(好像属于失效状态)。

接着,我们先将这个进程用19号命令暂停,然后又用18号命令重新启动这个进程,发现进程状态由原来的S+变为S,即转为后台进程。后台进程的表现是进程在不断往显示器上输出的同时,我们输入指令,显示器也会给我们输出该指令操作的结果。

总结 前台进程:如果进程状态中有“+”号,这时,该进程就是前台进程。前台进程的表现:可以用Ctrl+C组合键来杀死这个进程,进程在显示器上输出结果时,我们对命令行进行任何指令操作都是无效(bash不会对指令做出反馈)。 后台进程:如果进程状态中没有“+”号,这时,该进程就是后台进程。后台进程的表现为:Ctrl+Z组合键对该进程无效,进程在显示器上输出结果时,bash也可以对我们对命令行进行的操作做出反馈。

4. 深度睡眠状态(D状态)

这种状态我们平时很难见到,多出现在高并发,高IO的情况下

下面,我们简单了解一下这个状态,为了方便描述,我用第一人称视角来叙述。

一个进程对磁盘说:"我这里有一万条数据,需要存储在你那里,怎么样,可以吗?"

磁盘答道 :"没问题,但是,我比较慢,你得等一会"

进程说:"行,你去吧,我等着"

就这样,磁盘就去储存数据去了。

这时,操作系统来了,看到这个进程在那里什么也不干,还占用着资源,对这个进程吼道:

"你干嘛呢?我现在都急死了,我把能挂起的进程都挂起了,就这内存还不够用呢,你也别占着资源不干事了"。就这样,操作系统把这个进程给杀死了。

一会儿,磁盘在写入的过程中出现了错误,于是,它就出来喊这个进程,但是怎么喊都喊不来。磁盘想着:"我还有其他的事要做,不能光守着它呀",就这样,把数据果断的舍弃了。

这时,用户去磁盘中找数据,但是没找到,就把操作系统,进程,磁盘都叫到了一起。

磁盘说:"我最无辜,我写着呢,出现错误了,我想着让进程向上汇报呢,但是进程丢了,我不可能继续等着进程吧,还有其他的进程排着队呢"。

进程说:"难道就怪我吗?我等着呢,结果被操作系统给干死了"。

操作系统说:"这么说,是我的错了,用户信任我,让我来管理整个计算机,我这也是履行我的职责,没有问题吧"

用户说:"这样吧,我以后往这种进程身上挂一块免死金牌,你看见免死金牌就别杀它了"

操作系统说:"没问题,讲好规则就行"

---------------------------------------------------------------------------------------------------------------------------------

如上,我给大家讲了一段小故事。最后操作系统决定往一类进程身上挂免死金牌,这一类进程所处的状态就是深度睡眠状态(S睡眠状态)。

处于S状态的进程操作系统是杀不死的,用户也杀不死;只能通过断电或者进程自己醒来的方式 来解决。

5.t状态(tracing stop)

这是一种特殊的暂停状态,当一个进程被gdb调试,这个进程就处在该状态

6.X状态和Z状态(僵尸状态)

X死亡状态

这个状态只是一个返回状态,所以,我们不会在任何状态列表中看到这个状态

Z状态(僵尸状态)

僵尸状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程,僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。 僵尸进程:一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出。这个子进程就是僵尸进程。任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

在谈僵尸状态之前,我们有必要先探讨一下为什么要创建进程?

我们创建进程一定是想让进程为我们完成某项事请。那么,我们要不要知道进程完成这件事情的结果呢?可能需要知道,也可能不需要知道,但是,无论上层想不想获取完成事件的结果,这项进程都必须保存这个结果,以备上层查询。

如果一个进程完成某项事件后马上退出,进入X状态,父进程还有没有机会拿到结果呢?

答案是没有机会的。所以在LInux退出时,一般不会彻底退出,而是先进入Z状态,Z状态是为了方便后续父进程获取子进程的退出结果。

我们来见一见传说中的僵尸进程

僵尸进程的危害

1.如果父进程没有回收子进程,那么子进程是不是会一直处在僵尸进程状态?是的 2.进程的退出码必须被维持下去,因为进程要告诉它的父进程任务完成的如何,而进程退出码属于进程的基本信息,被保存在PCB中,是不是如果不被回收,进程的PCB就要被一直保存在内存中?是的 3.如果一个进程创建了多个进程,而这个进程就是不回收。代价就是要维护多个PCB结构体,PCB结构体式非常庞大的,这就意味着要浪费相当多的资源。

因为僵尸进程有这么多的危害,所以我们要解决僵尸进程问题,如何解决,我们后边来谈!!

因水平有限,文中难免会有错误,敬请各位读者指正!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是进程状态
  • 主流OS都存在的进程状态
    • 运行状态(R状态)
      • 阻塞状态
        • 注意:
      • 挂起状态
      • LInux是如何做到的?
        • 1.R运行状态(running)
          • 1.1见一见LInux下的R状态
        • 2.S睡眠状态(sleeping)
          • 3.T停止状态(stopped)
            • 3.1kill命令
            • 3.2观察T信号进程
          • 前台进程和后台进程
            • 4. 深度睡眠状态(D状态)
              • 5.t状态(tracing stop)
                • 6.X状态和Z状态(僵尸状态)
                  • X死亡状态
                  • Z状态(僵尸状态)
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档