• Linux:进程的状态理解


    进程的理解

    前面对于进程的学习进行了理解,但是没有进行系统的总结

    对于用户来说,为什么要创建一个进程?
    因为要完成某种任务,而前面又知道,进程=代码和数据+内核数据结构,对于操作系统来说,内核数据结构是利用PCB来进行控制的,在Linux中,PCB具体叫做task_struct,而前面对于task_struct的源码也做了部分阅读,但并没有进行其他分析

    那么进程的这两部分内容,都会在内存中占用一定的空间,而这些空间并不是被一直占用,当到达某个程度后,进程已经完成了自己的任务,这些内存就会被释放掉,进而操作系统将会管理其他的进程,进而进行控制

    **进程的退出:**进程的退出意味着,将要释放掉自己的代码和数据

    那么现在的问题是,进程是如何将自己已经完成的目的告诉操作系统?,其实也是借助了内核数据结构的功劳,从中也能看出,操作系统要进行进程的管理,必定是要对进程的数据进行管理,也能看出,管理的本质是管理数据,借助内核数据结构,操作系统可以很轻松的对进程进行管理,至于如何管理后续进行总结

    僵尸进程

    僵尸进程的基本概念

    那么现在的问题是,操作系统确实可以在PCB中做标记,对于已经完成目的的进程,可以进行代码和数据的释放了,因此此时代码和数据就被通过某种手段释放掉了,但是此时的进程还不算被真正退出,因为还有PCB还留存在内存中,这个PCB是操作系统需要维护的,原因是这个进程的退出情况还没有被读取,还有存在的意义和价值,因此操作系统必须要对处于这种状态的进程做维护,直到父进程或者是其他控制管理的模块知道,这个进程退出了,不再需要这个进程的任何数据,这个进程才叫做真正的退出,但是如果一直不进行读取呢?

    操作系统对于这样的进程,给了它一个状态表示叫做z状态,也叫做僵尸状态,而处于僵尸状态的进程就叫做僵尸进程,而处于僵尸状态的进程如果一直没有被回收,它会保持这个状态吗,答案是肯定的,操作系统会一直维护这个PCB结构,直到被读取后才能被真正的释放,PCB的状态也会随之从z状态改变为x状态,意味着这个进程真正意义上走到头了,可以被释放了

    僵尸进程是有危害的,如果一直不对僵尸进程进行回收,它就会一直占用计算机内存中的存储空间,这样的行为就被叫做内存泄漏

    僵尸进程的演示

    下面对于僵尸进程进行演示,模拟一个会产生僵尸状态的场景

    场景的模拟也很简单,创建两个进程,一个进程是另外一个进程的子进程,如果子进程终止了,但是父进程还在运行,此时父进程就不会对子进程进行回收,此时就是僵尸进程

    那么进程代码的编写

    # 完成Makefile的编写
    cc=gcc
    src=myproc.c
    target=myproc 
    
    $(target):$(src)
    	gcc -o $@ $^
    .PHONY:clean
    clean:
    	rm -f $(target)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    实现一个程序,完成上面的功能:

    #include 
    #include 
    #include 
    
    int main()
    {
        pid_t id=fork();
        if(id<0)
        {
            return 1;
        }
        else if(id>0)
        {
            // 父进程
            while(1)
            {
                printf("i am father\n");
                sleep(1);
            }
        }
        else 
        {
            // 子进程
            int cut=5;
            while(cut--)
            {
                printf("i am child:%d\n",cut);
                sleep(1);
            }
            printf("child dead\n");
            exit(2);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    上面这个程序就是使用了前面的思想,创建一个子进程,如果返回值是负数说明创建失败,如果返回值是0说明是子进程,如果返回值是其他说明是父进程,此时让子进程死亡,但是父进程还在循环

    创建监视脚本:

    # 监视带有myproc的进程
    while :; do ps ajx |head -1 && ps ajx | grep process|grep -v grep; sleep 1;echo "###################################"; done
    
    • 1
    • 2

    此时运行结果:

    # 当程序运行5s后的结果
     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    24154  1126  1126 24154 pts/0     1126 S+    1003   0:00 ./myproc
     1126  1127  1126 24154 pts/0     1126 Z+    1003   0:00 [myproc] <defunct>
    
    • 1
    • 2
    • 3
    • 4

    从上面可以看出是符合一开始的与其结果的,子进程编号是1127,而父进程的编号是1126,此时子进程死亡后,父进程依旧在运行,没有机会回收子进程,此时子进程所处的状态就是Z,也就是所谓僵尸进程

    孤儿进程

    前面的代码中可以看出,子进程结束后,父进程没有及时回收就会进入僵尸状态,但如果子进程还没结束,父进程就结束了呢?

    #include 
    #include 
    #include 
    
    int main()
    {
        pid_t id = fork();
        if (id < 0)
        {
            return 1;
        }
        else if (id ==0)
        {
            // 子进程
            while (1)
            {
                printf("i am child\n");
                sleep(1);
            }
        }
        else
        {
            // 父进程
            int cut = 5;
            while (cut--)
            {
                printf("i am father:%d\n", cut);
                sleep(1);
            }
            printf("father dead\n");
            exit(2);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行上面的程序,同时监视结果:

     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    24154 16031 16031 24154 pts/0    16031 S+    1003   0:00 ./myproc
    16031 16032 16031 24154 pts/0    16031 S+    1003   0:00 ./myproc
    ###################################
     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        1 16032 16031 24154 pts/0    24154 S     1003   0:00 ./myproc
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从中可以看出一个现象,当父进程结束后,发生了两件事,父进程被回收了,同时这个父进程的子进程的ppid变成了1,同时程序变为后台程序运行

    从中就引入了孤儿进程的概念:当子进程的父进程直接退出了,那么子进程就要被领养,变成孤儿进程,这个ppid1的进程被叫做是systemd initd进程

    进程的优先级概念

    什么是优先级?

    优先级在实际的生产生活中也有具体的体现,比如在排队时,优先级高的可以优先获取到需要的资源,而在操作系统的层次上,对于进程也是如此,优先级高的进程,将会优先得到CPU分配的资源,而优先级高的进程还能够有优先执行的权利,配置进程的优先权对于Linux来说是很有用的,可以改善系统的性能

    同时,也可以把进程运行到指定的CPU上,这样就能改善系统的整体性能

    优先级的更改

    Linux中,进程的优先级的值是由pri(old)+nice值来决定的,也就是说,用户只能通过修改nice值来进行对于优先级值的更变,而不管如何更改,最终程序的优先级的值一定是在一个区间内的,这样是有好处的,后续进行理解

    对于优先级的理解

    程序的优先级,实际上本质上是因为资源的不足,这是很好理解的,CPU在同一个时刻只能处理很少的数据,但是在同一时刻会有很多很多的进程等待,因此如何对于这些进程进行调度是很重要的一件事,才有了优先级的概念

    进程的优先级本质上就是PCB中的一个数值,数值的范围是60-99,默认的进程优先级的值是80

    为什么要把优先级设定在一定的范围?

    这是由于要让程序被操作系统调度的时候,可以较为均衡的让每一个进程都得到调度,都能获取到资源,而不是优先级特别高的一直获取资源,而优先级较低的长时间都得不到资源,如果进程长时间都得不到资源,会产生一个问题,这个就叫做进程饥饿

    其他边缘化的概念理解

    竞争性:对于系统重的进程是有很多很多的,而CPU资源一般只有1个,因此优先级的诞生,就是要高效的完成任务,更加合理的进行相关资源的分配

    独立性:进程之间的运行是不会相互干扰的,它们需要进行独立的享受各种资源

    并行:对于需要进行比较大的数据处理时,会有多个CPU同时进行运转,而这个过程就被叫做并行的概念

    并发:对于多个进程在一个CPU上运行的时候,就一定会不断地进行进程切换,而在同一个时间内,让多个进程都得到推进,这个就被叫做并发

  • 相关阅读:
    基于java+springboot+vue实现的高校社团管理系统(文末源码+Lw+ppt)23-419
    基于JavaSwing+MySQL的清朝古代名人数据管理系统设计
    通过WARN(1,“xxx“) 来确定code的flow和打印callstack
    HTML入门篇---02列表、表格、表单标签
    Mathtype——设置正体下标和斜体下标的方法
    【Python游戏】Python各大游戏合集(5):塔防游戏、飞机大战、连连看、打地鼠、记忆翻牌 | 附带源码
    python条件语句,循环
    npm下载的包分类
    python -- PyQt5(designer)中文详细教程(一)Qt的基本功能
    2024年度“阳江市惠民保”正式发布!阳江市专属补充医疗保险全新升级
  • 原文地址:https://blog.csdn.net/qq_73899585/article/details/133796707