当我们了解到进程是什么东西后,我们再来看看进程都会有那些状态。本篇文章会对进程的不同状态进行详解,希望会对你的理解有所帮助!
文章目录
一、了解进程的不同状态
二、详解进程的不同状态
2、1 R运行状态(running)
2、1、1 运行态是指程序在运行吗?
2、1、2 不在运行的程序就一定不是运行态吗?
2、1、3 进程处于运行状态,就一定会占用CPU资源吗?
2、2 S睡眠状态(sleeping)
2、3 D磁盘休眠状态(Disk sleep)
2、4 T停止状态(stopped)
2、4、1 前后台程序
2、5 t停止状态 (tracing stop)
2、6 X死亡状态(dead)
2、7 Z僵尸状态(Zombies)
🙋♂️ 作者:@Ggggggtm 🙋♂️
👀 专栏: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 信号让进程继续运行。 t停止状态 (tracing stop):t状态是由于进程被 GDB 或其他调试器停止。在这个状态下,进程被挂起,等待被调试器唤醒。在此状态下,进程无法从内核的角度运行,但是仍然占用系统资源(如内存和CPU),因此这种状态一般不会持续太久,很快就会被唤醒。 X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。 Z僵尸状态(Zombies):是一个比较特殊的状态。当进程退出并且父进程,没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
只有概念的话,并不能很好的理解各种状态。接下会给大家结合在Linux上的实际例子进行解释,一边更好的理解不同的状态。
二、详解进程的不同状态
2、1 R运行状态(running)
运行态具体是指:当操作系统把CPU资源分配给进程时,进程进入运行状态。在运行状态下,进程会执行它所分配的任务。当进程的时间片用完后,进程就会被挂起,等待下一次CPU调度。
我们可以看一下在Linux下的R状态。如下:
那么这里有几个问题:运行态是指程序在运行吗?不在运行的程序就一定不是运行态吗?进程处于运行状态,就一定会占用CPU资源吗?
2、1、1 运行态是指程序在运行吗?
我们知道,我们目前所使用的计算机大多都只有一个CPU,在某一时刻只能处理一个进程。我们所看到的多进程同时运行,本质是CPU通过对进程的高速切换运行实现的。
当进程在被调度执行时,那么这个进程一定是就绪的。换句话说,当进程处就绪时,才可被CPU调用运行。进程之间都是被特定的数据结构进行连接起来,多个进程处于就绪时,也是用特定数据结构将他们连接了起来。这个数据结构就是运行队列。
运行队列是指:操作系统中正在等待分配CPU时间执行的进程队列。操作系统根据进程优先级、所需资源、到达时间等因素,将进程插入不同的就绪队列中等待分配CPU时间片。通常情况下,操作系统采用按照时间片轮换算法,将CPU时间轮流分配给每个进程执行,从而实现多任务并发执行。而进程在运行队列中的顺序将决定其是否能够获得足够的资源并尽快完成任务。我们可结合下图理解:
结论:处在运行队列中的进程的状态即为运行状态(running)。但是CPU不会同时执行所有就绪的进程,而是通过时间片的轮换从而一个一个执行,知道执行完毕。
所以运行状态并不是指正在运行的进程,处于就绪状态的进程也可被称为运行状态。
2、1、2 不在运行的程序就一定不是运行态吗?
通过对上述的了解后,我们知道,不在运行的程序且在运行队列中的进程也可称其为处于运行状态。所以,不在运行的程序也可能运行态。
2、1、3 进程处于运行状态,就一定会占用CPU资源吗?
进程处于运行状态,不一定会占用CPU资源。原因是可能某进程处于运行状态,但他并没有被执行,而是就绪后一直在等待CPU资源。
2、2 S睡眠状态(sleeping)
睡眠状态是指:当进程被调用而等待某个事件的发生时,该进程就会进入睡眠状态。通俗来讲,当我们要完成某种任务的时候,任务条件不具备,需要进程进行某种等待。
进程等待时,都是在等待CPU资源吗?答案是:并不是!
进程处于睡眠状态时,等待的并不是CPU资源。在这个状态下,进程会放弃CPU资源,同时等待某个事件的发生。例如,一个进程在等待磁盘IO操作完成时,就会进入睡眠状态。当磁盘IO操作完成时,内核会将该进程唤醒,并将其转移到就绪状态,这样该进程就可以重新开始执行。
睡眠状态被认为是一种阻塞状态,因为该进程在等待发生的事件情况下不能真正地执行,所以CPU的资源也就会被阻塞。只有当该事件发生,进程才能被唤醒并继续执行。
我们看一下Linux下的 睡眠状态(sleeping)S。代码如下图:
生成myproc可执行程序,执行该程序后我们其起状态,如下图:
我们看到有R状态,也有S状态是为什么呢?原因是我们在向显示器进行输出时,显示器的IO可能被其他进程占用,此时需要等待显示器的IO的资源。当进程获取显示器的IO的资源后,该进程就会被加载到运行队列中进行运行。具体可结合下图理解:
在查看进程的状态时,看到大部分的情况都是S+的状态,这也反映出CPU的运行速度比IO的速度快很多。
2、3 D磁盘休眠状态(Disk sleep)
D磁盘休眠状态我们也称之为深度睡眠状态。这种状态是怎么形成的呢?
D磁盘休眠状态(Disk sleep)表示进程因等待磁盘I/O操作时而被阻塞,并且进程等待的操作可能会很慢,也可能永远不会完成。
举个例子,例如有一个进程需要把数据写入磁盘,且数据量很大。而写入磁盘的过程相对来说并不是很快,进程需要等待一段时间。等待的过程中,同时也占用了CPU的资源,但CPU会把时间片分给其他进程。该进程又需要磁盘读取数据完成后的返回信息(返回信息是指磁盘是否读取数据完成),所以该进程不可被操作系统终止。
在Linux系统中,有些进程可能预期会处于D状态,例如,磁盘I/O操作比较耗时的进程。而对于其他类型的进程,处于D状态通常是不正常的,需要进行诊断和解决。常用的解决方法包括重启系统、更换设备等。
2、4 T停止状态(stopped)
T停止状态(stopped)表示进程已经被挂起,不再运行,但是它尚未完成或终止。进程可以进入停止状态是由于收到一个SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号,以及调试器进程发送的SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGTRAP、SIGABRT、SIGBUS、SIGFPE、SIGSEGV、SIGPIPE、SIGALRM、SIGTERM、SIGURG、SIGXCPU、SIGXFSZ和SIGSTOP信号。
通常情况下,进程进入停止状态是由于用户手动发送的一个信号,例如利用kill指令发送信号。进程被挂起以后,它的状态可以使用命令“ps”查看。查看时显示进程的状态被标记为T,表示进程正在停止状态下运行。具体我们结合下图理解:
代码如下图:
生成myproc可执行程序,执行该程序后我们再发送信号,让其处于停止状态,如下图:
停止状态是一个暂停状态,进程可以从该状态恢复。当进程被恢复时,它将执行SIGCONT信号处理程序,并继续执行它原先的工作。 具体如下图:
2、4、1 前后台程序
细心的同学可能发现了:上述的 停止状态T 并没有带 ‘+’ 号,恢复后的睡眠状态S 也没有带 ‘+’ 号。而开始讲述的都带 ‘+’ 号了。这是为什么呢?这就涉及到Linux的前台和后台了。
Linux中运行的程序可以在前台(foreground)或者后台(background)运行。前台程序是在当前终端会话中运行的程序,而后台程序则是在系统内部运行的,没有与用户终端会话相关联。上述带 ‘+’ 号的就是在前台运行的程序,不带 ‘+’ 号的就是在后台运行的程序。
当用户在终端中输入运行一个程序的命令时,该程序默认以前台方式运行,它会在终端上打印输出并且用户必须等待程序执行完成。
如果在命令末尾加上 & 符号,就可以让程序在后台运行。具体如下图:
或者我们通过信号让程序停止,程序会自动挂到后台,恢复运行时也是在后台运行。程序在后台运行时用户可以继续在终端上执行其他命令,而程序则在后台默默运行。注意:此时的ctrl + c 并不能终止后台进程,我们通过信号对后台进程进程终止。
如果需要与后台程序交互,可以使用一些特殊的命令,如:jobs, fg等,来查看和控制程序的状态。 fg 指令就可吧后台进程转换到前台运行。例如:
$ jobs #查看后台运行的程序 $ fg %1 #将后台中的程序1转为前台运行
2、5 t停止状态 (tracing stop)
进程进入t(tracing stop) 状态是由于进程被 GDB 或其他调试器停止。在这个状态下,进程被挂起,等待被调试器唤醒。在此状态下,进程无法从内核的角度运行,但是仍然占用系统资源(如内存和CPU),因此这种状态一般不会持续太久,很快就会被唤醒。
当被调试进程被唤醒时,它会先进入运行状态(R),然后再执行 GDB 的下一条命令。在此期间,进程的状态将从t(tracing stop)改变为R(运行状态)。如果进程被控制台或其他管道接管,则可能进入 S (interruptible sleep) 状态,等待输入或者其他外部事件的触发,以便继续执行。
总的来说,进程的状态是动态变化的。在Linux系统下,进程状态不仅反映了进程的当前运行状态,同时提醒了用户或管理员进程可能遇到的问题或需要特殊处理的情况。
2、6 X死亡状态(dead)
X死亡状态(dead)是指该进程已经终止(terminated),但其进程表项(process table entry)仍存在于内核中,以便父进程可查看该进程的退出状态。
也就是说,该进程的资源(这里的资源是指进程的相关数据结构如PCB,和进程的相关代码和数据)已经被释放,但它的进程描述符还没有被完全删除,父进程需要回收该进程的内存空间。
当进程进入dead状态时,内核会向父进程发送一个SIGCHLD信号通知其子进程已终止,此时父进程可以调用wait()或waitpid()函数来回收该进程的资源。如果父进程没有及时处理该信号,则该进程的进程表项会一直保留在内核中,一直到父进程处理该信号为止。因此,如果父进程异常终止或者父进程没有调用wait()来回收该进程的资源,就会导致该进程一直处于dead状态,占用着系统资源。
2、7 Z僵尸状态(Zombies)
僵尸状态 (Zombies) 通常是指一个已经终止执行的进程,但是其父进程还没有处理它的退出状态。也就是一个进程已经处于被终止,但是其资源并没有被其父进程回收,该进程就会进入僵尸状态。
僵尸状态也容易被演示出来。我们可通过fork()创建子进程。创建子进程完成后,会有两个返回值。把子进程的PID返回父进程,0返回给子进程。我们可结合下面例子理解。
我们写一段代码,代码功能:子进程和父进程执行不同功能,其中子进程没休眠一直运行,父进程什么都不做,休眠50秒。我们需要在这50秒把子进程终止掉,但父进程并不回收终止掉的子进程资源,此时子进程就会变成僵尸状态。代码如下图:
运行结果如下图:
我们看到上述运行结果子进程确实变成了僵尸状态。
当一个进程正常结束运行后,会通过退出系统调用向其父进程发送一个退出信号,父进程通过处理该信号来获取其退出状态。但是如果父进程没有及时处理该信号,子进程会进入僵尸状态,该进程的状态将被标记为“Zombies”,并在进程表中占据一些空间。
僵尸进程不会占用CPU资源,但会占用进程表和PID,因此过多的僵尸进程会导致系统资源的浪费。为了避免僵尸进程的出现,父进程必须及时处理子进程的退出状态。这可以通过在父进程中使用 wait() 或 waitpid() 等系统调用来实现。
总之,僵尸进程是一种占用资源但无法执行任务的进程状态,对系统能够正常运行造成负面影响,因此在编写 Linux 应用程序时,应该处理好子进程的退出状态,避免出现僵尸进程。
以上内容就是进程状态的全部讲解,内容相对较多,也较为重要。我们应该理解不同状态,对我们后面的学习也有所帮助。感谢阅读ovo~