目录
01.运行状态
02.睡眠状态
03.磁盘睡眠状态
04.停止状态
05.死亡状态
进程的状态会随着操作系统的调度和外部事件的发生而不断地发生转换。例如,一个新创建的进程经过初始化后会进入就绪态,等待被调度执行;当调度器分配处理器资源给进程时,进程进入运行态;如果进程发起了I/O操作,它可能会进入阻塞态等待I/O完成;当I/O完成后,它重新回到就绪态等待再次执行;当进程执行完毕或被终止时,进程进入终止态。
进程状态的合理转换是操作系统正常运行的基础,也是实现多任务并发的关键。下面我们来理解进程的各个运行状态。以下是总览:
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列 里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
01.运行状态
在理解进程状态时,首先要理解下面几个概念:
操作系统调度机制
操作系统负责计算机软硬件资源管理,其主要任务之一是有效地调度多个进程,以便它们能够并发执行。在多任务处理中,有多个进程需要与处理器进行交互,但处理器一次只能执行一个进程的指令,因此操作系统需要有一种机制来管理这些进程的执行,以便它们可以在处理器上轮流运行。运行队列就是这样的一种机制。
运行队列
运行队列是操作系统内核中的一个数据结构,用于存储处于就绪态的进程,即哪些已经准备好执行但尚未获得处理器的进程。其通常是一个先进先出队列,其中进程按照加入就绪态的顺序排列。
时间片
每个进程在处理器上的执行时间是多少呢,此时就要引入一个时间片的概念,时间片代表了每个进程在处理器上连续执行的时间。通过将处理器的执行时间分割成小的时间片段,操作系统可以轮流地为每个就绪态的进程分配处理器时间,从而实现多任务并发执行。
注意:在一般情况下,只有处于就绪态的进程才会被放入运行队列并被分配时间片,当进程编程阻塞态时就会被移出运行队列,不过在一些复杂的情况下有可能会被移到某种阻塞队列中等待资源可用。
当进程由运行队列被放到处理器中时,就有了运行态,处理器为其分配时间片,使其能够在规定的时间内执行指令,处理任务。这种状态下的进程可能会在一段时间后被操作系统挂起,以便为其他进程腾出空间。
进程挂起
进程挂起是指将一个正在运行的进程暂时停止执行,使其暂时不占用处理器资源。这种操作可以通过操作系统的特定调用或信号来实现。当一个进程被挂起时,它的执行状态被保存,以便稍后可以恢复执行。在进程被挂起期间,操作系统可能会将处理器分配给其他就绪态的进程,以实现多任务处理。
挂起进程与阻塞进程不同,因为阻塞进程通常是由于等待某些事件而无法继续执行,而挂起进程是由操作系统或用户显式地将其暂停执行。
02.睡眠状态
当进程处于睡眠状态时,它暂时不需要处理器资源,因为它在等待某些事件的发生。睡眠状态的进程可能在等待I/O操作完成、等待信号量、等待定时器等。一旦等待的事件发生,进程就会被唤醒并切换到就绪态。
下面用代码来演示进程从睡眠状态到就绪状态再到运行状态的过程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
// 创建进程失败
perror("fork failed");
exit(1);
} else if (pid == 0) {
// 子进程代码
printf("子进程睡着了...\n");
sleep(5); // 子进程睡眠5秒
printf("子进程醒了!\n");
exit(0); // 子进程结束
} else {
// 父进程代码
printf("父进程在等子进程结束...\n");
wait(NULL); // 父进程等待子进程结束
printf("父进程开始运行了...\n");
}
return 0;
}
在这个示例中:
- 父进程调用
fork()
创建子进程。 - 子进程进入睡眠状态,睡眠5秒钟。
- 在子进程睡眠期间,它处于睡眠状态,不占用处理器资源。
- 当5秒钟过去后,子进程被唤醒,结束睡眠状态,并转换为就绪态。
- 父进程调用
wait()
等待子进程结束。 - 子进程结束后,父进程继续执行。
03.磁盘睡眠状态
进程处于磁盘睡眠状态时,它正在等待磁盘I/O操作完成。这种状态通常在进程需要大量磁盘读写操作时出现,例如,文件系统操作、数据库操作等。
磁盘睡眠状态和睡眠状态都表示进程暂时无法执行,但磁盘睡眠状态特指等待磁盘 I/O 操作完成的情况,而睡眠状态则是一个更广泛的概念,包括等待各种不同类型事件发生的情况。
04.停止状态
T (stopped)和t (tracing stop)统称为停止状态。
T (stopped): 进程被停止时,它暂时停止执行指令,但仍然保留在系统中。这种状态通常由调试器暂停、SIGSTOP信号等触发。停止状态的进程可以被重新启动,继续执行。
t (tracing stop): 进程被追踪暂停时,通常是为了调试目的。在这种状态下,调试器可以查看进程的内部状态、寄存器值等,以帮助排查程序问题。
下面是一个简单的示例,说明进程被停止的情况:
假设有一个简单的 C 程序,如下所示:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("我是一个进程...\n");
sleep(1);
}
return 0;
}
在这个程序中,进程将不断打印消息,模拟持续执行。
然后,可以使用gdb调试器在程序运行过程中停止该进程:
$ gcc -g -o proc proc.c
$ gdb proc
(gdb) run
然后,在另一个终端中,找到该进程的进程 ID(PID)并发送 SIGSTOP 信号来停止它:
kill -STOP 66414
先前的终端显示以下信息表示进程被停止:
尽管进程暂时停止执行指令,但它仍存在于系统中,可以通过调试器重新启动或进行其他操作。
05.死亡状态
死亡状态通常指的是进程已经终止执行并退出,但其相关的进程描述信息仍然保留在系统中,直到其父进程对其进行清理。
进程终止
进程终止是指进程执行完毕或者由于某种错误导致其异常终止。在进程终止时,它的执行环境、资源和内存空间会被操作系统回收和释放。
僵尸进程
当一个进程终止后,其父进程需要调用 wait()
或 waitpid()
等系统调用来获取子进程的退出状态,否则子进程的进程描述信息会被保留在系统中,形成僵尸进程。僵尸进程不再执行任何操作,但其相关的进程描述信息(如进程 ID、退出状态等)仍然存在于系统的进程表中。
清理僵尸进程
父进程应当定期调用 wait()
或 waitpid()
等系统调用来清理僵尸进程,从而释放相关的进程描述信息,防止系统资源的浪费。如果父进程长时间不清理僵尸进程,那么系统可能会耗尽进程表项,导致无法创建新的进程。
处理僵尸进程
父进程可以通过 wait()
或 waitpid()
等系统调用来处理僵尸进程,获取子进程的退出状态并释放相关的资源。这样做可以确保系统中不会出现大量僵尸进程,保持系统资源的有效利用。
以上就是进程状态的有关知识了,欢迎评论区留言,觉得俺的博客对你有帮助的,可以点赞关注支持一波喔~😉