理解Linux中的进程状态


一台电脑一般只有一个CPU、一个磁盘(无论一台电脑有几个CPU、磁盘,数量都是远少于进程的,这里举例用一个)。

运行状态

而一个CPU同时只能调度一个进程,所以操作系统就维护起了一个运行队列runqueue,在这个队列中排队的进程就称为运行(R)状态,并不是正在被CPU调度的进程称为运行(R)状态。

#include <stdio.h>
int main()
{
    while(1);
    return 0;
}

截屏2022-12-25 15.07.22

阻塞状态

假设我们的某些进程想要访问磁盘,而由于CPU的速度非常快,磁盘的速度非常慢,那么当CPU执行完5个进程,磁盘连一个进程都没执行完,所以就会导致,CPU执行完的需要访问磁盘的进程就需要排队,所以操作系统又维护了一个等待队列waitqueue,在磁盘中排队的都进程都会在这个队列中,当进程访问完磁盘后,该进程又会重新去runqueue排队。而在这个队列中排队的进程就称为阻塞(S)状态

#include <stdio.h>
#include <unistd.h>
int main()
{
    while(1)
    {
        sleep(1);
        printf("hello world\n");
    }
    return 0;
}

截屏2022-12-25 15.17.01

这里有一个很违反直觉的现象,输出hello world怎么每次都是S状态?这是因为CPU的速度太快了!而磁盘的速度太慢了!所以当ps命令去查该进程状态的时候会有99%的几率查到的都是S状态,因为99%的时间都在等待IO。

挂起状态

有一个状态在Linux中未显式写出来,但教材中会经常提到,就是挂起状态。假设现在有很多进程都在访问磁盘,但是内存中的空间不够了,于是操作系统就会将一些在waitqueue的进程的程序代码和数据保存到磁盘上(PCB仍在waitqueue中),而这样的进程状态就称为挂起状态,该状态其实更应该被称为阻塞挂起状态,因为挂起状态可以和任意状态组合,比如运行挂起状态、就绪挂起状态、暂停挂起状态等等。

磁盘睡眠状态

假设有大量的进程在等待IO,而此时内存空间严重不足,操作系统挂起了很多进程也无济于事,这时操作系统很容易崩溃,为了防止操作系统杀掉这些在等待IO的进程(可能有重要的数据),于是在这种情况下的这种进程就会转为磁盘睡眠(D)状态,在该状态下的进程是不会被杀掉的,只能通过重启或者断电的方式。

暂停状态

#include <stdio.h>
#include <unistd.h>
int main()
{
    while(1)
    {
        sleep(1);
        printf("hello world\n");
    }
    return 0;
}

一个进程是可以被暂停的,在Linux下使用kill -19 目标进程PID命令可以使目标进程暂停,此时进程的状态会转为暂停(T)状态

截屏2022-12-25 15.34.00

kill -18 目标进程PID可以解除T状态。

截屏2022-12-25 15.36.37

这时会发现,原本的状态是S+,后来经过T状态后,变为了S。其实带有+号的状态是前台进程,不带的是后台进程,而后台进程是无法用ctrl+c信号来终止的,只能通过kill -9命令。

追踪停止状态

当我们调试程序的时候,该进程也总是处于暂停状态的,但并不是上面的使用kill命令导致的暂停状态,在Linux中该状态称为追踪停止(t)状态

截屏2022-12-25 15.43.47

僵尸状态

进程可以派生子进程,一个子进程在结束时,需要告诉父进程我的任务完成的怎么样,而父进程需要去接收该信息(终止状态),如果父进程不接收终止状态,则会导致子进程的PCB一直无法释放(代码和数据释放了),而在这段时间内,该子进程的状态就为僵尸(Z)状态,也称为僵尸进程,该进程会导致内存泄漏。

#include <stdio.h>
#include <unistd.h>
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        printf("I am child, pid = %d, ppid = %d\n", getpid(), getppid());
    }
    else if(id > 0)
    {
        //parent
        printf("I am parent, pid = %d, ppid = %d\n", getpid(), getppid());
        while(1)
        {
            //死循环,不接收子进程的终止状态
        }
    }
    else 
    {
        //error
    }
    return 0;
}

截屏2022-12-25 15.51.51

死亡状态

Linux中还有一个死亡(X)状态,处在该状态的进程就表明该进程马上就执行完了,没什么好说的。

孤儿状态

上面说到的僵尸状态是由于子进程执行完了,而父进程没有及时接收子进程的终止状态导致的,那么如果父进程先执行完,子进程后执行完的话,这时由于父进程已经执行完了,所以无法接受子进程的终止状态,但子进程的终止状态必须有人来接受,所以此时操作系统(1号Init进程)就会领养该子进程,以便能够接受子进程的终止状态,而处于被操作系统领养的进程所处的状态就称为孤儿状态, 也称为孤儿进程。

截屏2022-12-25 16.01.23

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[] = {

	/* states in TASK_REPORT: */
	"R (running)",		/* 0x00 */
	"S (sleeping)",		/* 0x01 */
	"D (disk sleep)",	/* 0x02 */
	"T (stopped)",		/* 0x04 */
	"t (tracing stop)",	/* 0x08 */
	"X (dead)",		/* 0x10 */
	"Z (zombie)",		/* 0x20 */
        
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值