操作系统学科的进程状态

请添加图片描述

上面的就是操作系统学科中,可能会提到的进程状态!当然你还可能看到诸如:就绪挂起,阻塞挂起等概念
我们学习的是一款具体的操作系统linux 操作系统对进程状态定义实现

linux 进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态一个进程可以有几个状态(在 Linux 内核里,进程有时候也叫做任务)。
下面的状态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[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

运行状态,R

linux 中,进程控制task_struct 是用双向链表链接起来的!操作系统维护了一个运行队列,凡是在运行队列中的进程就都处于运行态!被放在操作系统维护的运行队列中的是进程的控制块,即 task_struct,当轮到某个进程的代码cpu 执行时,我们能够通过运行队列中的 task_strcut 找到该进程对应代码数据然后开始执行!
在这里插入图片描述
一个正在 cpu 上运行的进程是不是一直要等到该进程的代码执行完毕才把自己cpu 上扒下来呢?显然这是不可能的!每一个进程都有一个叫做时间片的概念,当某个进程的时间片消耗完了,就会脱离 cpu,换下一个进程到 cpu 上执行!由一个进程切换到另一个进程,叫做进程切换
linux 中进程的时间片大约是:5~800ms,这就意味着一个进程每次在 cpu 上执行的时间有限的!加上 cpu 来回地切换进程!我们能够看到多个进程在同一时间同时运行的现象!


我们能够尝试看到运行状态嘛?因为每个进程在 cpu 上执行的时间都非常短,看到这个状态也是不容易的!

可这并不符合我们的预期哇!我们想看到的是 R 状态哇!怎么办呢?我们尝试printf 去掉试试!

#include<stdio.h>

int main()
{
    while(1) ;
    return 0;
}

在这里插入图片描述
我们看到也是成功看到 R 状态啦!这是因为加上 printf 这个进程会等待显示器资源就绪,从而使得进程大部分时间处于阻塞状态!主要是 cpu 执行的速度太快了!

阻塞状态,S

操作系统中为正在运行的进程维护了运行队列!同样也会为正在阻塞的进程维护阻塞队列!一个进程处于阻塞状态的场景很多,比如等待外设资源就绪(包括,键盘网卡鼠标显示器等等)。
操作系统中的大部分进程都是处于阻塞状态的!


下面的代码中,正在等待键盘的输入,那么这个进程的状态就是阻塞状态呢!

#include<stdio.h>

int main()
{
    int a;
    scanf("%d", &amp;a);
    return 0;
}

在这里插入图片描述

深度睡眠/磁盘休眠,D

我们假设一个场景:在很久很久以前,有一个进程 A,正在向磁盘中写数据!进程 A 正在欢快的写着。突然,操作系统发现自己内存空间严重不足了!当他路过进程 A 身边时,发现 A 正在向磁盘中慢吞吞的些数据呢!操作系统看他不顺眼,直接将进程 A 给他杀掉了!(操作系统为了保证自己的正常运行,完全可能杀进程的,类比手机开很多应用程序操作系统杀后台例子)。磁盘正写着呢,突然发现这个进程没了,这数据才写道一半呢?怎么办呢?磁盘心想,不完整数据还是丢了吧!(这是大部分情况的结果!)。
这个时候上层用户发现自己非常重要的数据没了!一纸诉状,将操作系统,进程,磁盘一并告上了法庭!请听被告的辩词:

  • 操作系统:亲爱的用户,您赋予我管理软硬件资源的权力,为的就是向您提供一个安全流畅,的运行环境!当时我正处于危机时刻,如果不通过杀死进程来释放内存,我就会崩溃的!导致电脑直接挂掉!
  • 磁盘:我一直在认真完成自己的工作哇,数据写到一半,进程突然没了!我只能将残缺的数据丢掉了!因为我的设定本就是如此!如果我有罪,那么其他的磁盘是不是也同样有罪!
  • 进程:我可是受害者哇!我是被杀掉的,怎么能有罪呢?

如果您是法官大人,您觉得是谁的错呢?显然他们没有错!
从那以后,当进程正在向磁盘中写数据的时候,他的状态就会被修改D 状态,这个 D 状态就是一块免死金牌,操作系统无法杀掉这个进程!
操作系统中 D 状态的进程很短,很少,用户一般都察觉不到。我们无法演示出来 D 状态!
如果真被用户察觉到,那么操作系统肯定要挂了!

暂停状态,T(t)

我们目前认为:Tt 状态没有区别
在进程状态中的 R 状态中,我们学会了用 kill 命令杀掉一个进程!现在我们再来学习如何使用 kill 命令暂停和运行一个进程!

kill -l #列出所有信号

在这里插入图片描述

(信号部分的原理我们后期会详细讲解这里学会怎么使用就行啦)这里面两个信号SIGSTOPSIGCONT 分别用来暂停,和运行一个暂停的进程!一个进程被暂停之后,我们就能看到 T 状态的进程啦!

#include<stdio.h>
#include<unistd.h>

int main()
{
    for(int i = 0; ; i++)
    {
        printf("hello linux: %d, pid: %dn", i, getpid());
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
我们看到,在给 pid 为 1613503 的进程发送 19 号信号之后,进程就暂停下来了,查看这个进程的状态就是 T 状态!
我们在给他发送 18 号信号就能让他从暂停状态变为运行状态!
在这里插入图片描述
T 状态与 S 状态有区别T 状态可以理解为阻塞状态,只不过 T 状态可能是控制一个进程,或者等待


T/t 状态有什么运用场景嘛?我们之前是不是学习gdb,让程序在代码的某个位置停下来(调试),不就是 T/t 状态的运用嘛!
在这里插入图片描述

终止态,X

终止态的进程是真的就看不到了,一个进程死亡了,被放入垃圾回收队列中,回收资源
终止态是一个瞬时状态!

僵尸状态,Z

一个进程退出了,进程的退出信息会被维护一段时间,父进程获取了进程的退出状态之后,该进程由 Z 状态变为 X 状态
进程退出信息被维护的这一段时间,进程就处于 Z 状态!
一般情况下,进程退出的时候,如果父进程没有主动回收子进程的退出消息,子进程会一直处于 Z 状态,子进程相关资源,尤其是 task_struct 结构体不能被释放,那么子进程的 task_struct 就会一直占用内存资源,造成内存泄漏

父进程获取子进程退出信息过程称为进程等待!这个知识点后面会详解
父进程直接创建了子进程嘛,子进程要退出了,父进程肯定要关心关心撒!父进程创建子进程就是让子进程来办事的嘛!

我们可以写一个代码来验证,子进程退出的时候,父进程不获取子进程的退出状态,那么子进程就会处于僵尸状态的结论:让子进程循环打印 pidppid 三秒之后退出循环,退出循环之后结束子进程。父进程打印自己的 pid

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        int cnt = 0;
        while(1)
        {
            printf("I am child, pid: %d, ppid: %dn", getpid(), getppid());
            sleep(1);
            cnt++;
            if(cnt == 3)
                break;
        }
        exit(0);
    }
    else if(id > 0)
    {
        while(1)
        {
            printf("I am parent, pid: %dn", getpid());
            sleep(1);
        }
    }
    else
    {
        perror("fork(): ");
    }
    return 0;
}

使用监控脚本监控进程的状态:
在这里插入图片描述
我们看到在子进程退出之后,子进程的状态变成了 Z 状态,也就是这个进程变成了僵尸进程
至于怎么获取子进程的退出信息,我们会在进程等待的章节详细讲解!


你有没有想过,如果我们的父进程先退出,结果又是怎么样的呢?

孤儿进程

好的,我们就来写代码看看父进程比子进程先退出是怎么个事儿!
我们让父进程运行三秒之后退出,子进程一直运行,通过监控监本看看出现什么现象!

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id == 0) //子进程
    {
        while(1)
        {
            printf("I am child, pid: %d, ppid: %dn", getpid(), getppid());
            sleep(1);
        }
    }
    else if(id > 0)
    {
        int cnt = 0;
        while(1)
        {
            printf("I am parent, pid: %dn", getpid());
            sleep(1);
            cnt++;
            if(cnt == 3)
                break;
        }
    }
    else
    {
        perror("fork(): ");
    }
    return 0;
}

我们看到,父进程退出之后子进程的确一直再跑,只不过子进程的父进程好像发生了变化!
在这里插入图片描述
子进程的父进程变成了 1,我们来看看这个 1 号进程是啥啊!
在这里插入图片描述
一号进程是:systemd,不就是操作系统嘛!
这个现象说明父进程先于子进程退出,这样的子进程被称为孤儿进程!孤儿进程会被操作系统领养


为什么操作系统要领养他呢?
因为孤儿进程也要释放资源哇,之前是通过父进程获取子进程的退出状态之后,由操作系统释放资源!父进程提前退出了,那么就直接让操作系统释放不就行了!操作系统有这个能力做到!


bash 进程不是我们自己写的进程的父进程吗? bash 进程能领养孤儿进程嘛?
说到底就是 bash 没有这个能力,即使 bash 进程领养了子进程,但是系统中没有爷爷进程等待孙子进程的逻辑,即使领养了也没用。但是操作系统就不一样了,操作系统是所有进程的管理者能够直接在内核层面回收!


原文地址:https://blog.csdn.net/m0_73096566/article/details/134765576

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_36870.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注