• 进程状态分析


    目录

    什么是进程:

    查看进程的方式:

    显示所有进程

    打印第一行的信息

    显示指定进程

    保存进程的目录proc

    PID

    获取pid的方式1

    获取进程的方式2--系统调用

    PPID

    验证proc的实时性

    认识当前路径

    fork() 创建子进程

    性质

    为何父进程返回的是子进程的pid,子进程返回0?

    操作系统的进程状态

    运行态

    终止态

    阻塞态

    挂起态

    Linux下的进程状态

    R--运行状态

    阻塞状态 

    S--浅睡眠状态

    D--深度睡眠

    Z和X--僵尸状态和死亡状态

    孤儿进程

    T--暂停状态

    t--追踪状态


    什么是进程:

    在 Linux 下跑起来的程序就叫做进程;

    可以和文件对比来理解,我们知道 文件是由文件内容和文件属性组成的,而进程是由自己的属性和对应的文件组成,在操作系统中,进程的数据和属性存在PCB中,在Linux中,PCB等于task_struct;

    操作系统通过对 task_struct 来管理进程,就相当于在大学,学校通过对我们的数据属性来管理我们一样;

    进程 = task_struct + 文件(文件属性+文件内容)

    查看进程的方式:

    当一段代码编译好后生成可执行程序后,当执行这个可执行程序,必然会产生一个以可执行程序同名的进程,那么进程如何查看呢?

    显示所有进程

    ps ajx

    打印第一行的信息

    ps ajx | head -1

    显示指定进程

    ps ajx | grep 进程名

    ps ajx | grep 进程名 | grep -v grep

    grep -v grep可以去掉grep的进程:

    ps ajx | head -1 && ps ajx | grep 进程名 | grep -v grep

    打印第一行信息和指定进程:

    保存进程的目录proc

    目录 proc 保存的是当前系统的实时进程信息,我们可以通过 进程对应的 PID来验证此目录

    PID

    进程的一个进程只有一个pid,pid与进程相互对应,在 proc 目录下,存有进程的pid:

    获取pid的方式1

    上述用 ps ajx 来查看进程的信息,可以获取到进程的 pid :

    获取进程的方式2--系统调用

    系统调用的方式:

    对比两种方式,我们发现同一个进程,pid不同,原因是因为当一个进程结束后,再次调用此进程,系统会重新分配空间给进程,pid也会改变;

    与创建变量,每一次运行时变量地址都不同是一个道理

    PPID

    ppid是父进程,父进程控制子进程,通过子进程来完成一个程序的执行

    可以通过上述两种方式查看,这里看一下系统调用的方式:

    验证proc的实时性

    进程既然已经存在了,那么proc中一定存在此进程的pid:

    若此时我们结束掉此进程:

    再次开始进程:

    认识当前路径

    cwd:进程当前的工作路径;

    进行文件操作时,默认文件的创建路径是当前路径,这个当前路径指的就是 cwd -> 进程的工作路径

    exe:进程对应的可执行程序的磁盘文件

    fork() 创建子进程

    我们用 ./ 来将程序运行起来,起始就是手动创建了一个子进程,除了手动创建,还可以通过系统调用来创建子进程

    性质

    调用成功,给子进程返回 0,父进程返回子进程的 pid

    有两个返回值

    调用失败,返回 -1

    子进程也会有自己的 task_struct 来存储自己的属性,其中 task_struct 内的大部分数据都是从父进程的 task_struct 中继承下来的;

    fork() 以上和父进程执行相同的代码;

    fork() 以下的代码段共享:父进程和子进程通过变量的返回值不同,执行不同的代码块,代码共享,子进程无自己的代码,但是数据独立,有自己的数据

    因此同一个变量,能打印出来两个不同的值

    为何父进程返回的是子进程的pid,子进程返回0?

    为了识别子进程,一个父进程下可能会有多个子进程,将子进程的pid返回给父进程,父进程可以快速的识别这是哪一个子进程;

    子进程返回0,可以知道自己被创建成功了,并且子进程找父进程的成本低,直接系统调用就可以了,因此简单的返回一个0就可以了。

    操作系统的进程状态

    运行态

    只要 PCB 在运行队列中,那么此进程就叫做运行态,代表已经准备好了,可以随时调度

    终止态

    进程还在,只不过永远不在运行了,随时等待被释放;为什么不是立即被释放呢?因为操作系统可能在进行其他操作,不能理解释放,所以才有了终止态

    阻塞态

    进程申请 cpu 资源进行执行操作,需要在其队列中排队;那么申请其他资源:网卡、磁盘、音响等,也是需要在对应资源的队列中排队的;

    当 cpu 执行到一个进程时,此进程需要调用其他资源,但是资源还没有准备好,那么此进程的 PCB 将会从 cpu 的运行队列转移到需要调用的资源的运行队列中等待,不会执行此进程,不会阻挠 cpu 调用其他进程,此时这个进程就是卡住的状态,叫做进程阻塞。

    挂起态

    在 cpu 运行队列中的进程,他们的代码和数据是被加载到了内存中的,当内存不够用的情况下,操作系统会将短期内不会被 cpu 调度的进程的代码和数据置换到磁盘上

    Linux下的进程状态

    R--运行状态

    以不用调用外设的死循环为例,cpu 一直在调用此进程,那么它就为运行态:

    阻塞状态 

    S--浅睡眠状态

    linux下的浅睡眠状态可以被随时唤起、可以被手动终止,因此又称为可中断睡眠

    进程要访问非磁盘的外设时,总是需要等待资源就绪的,Linux下的PCB叫做 task_struct ,task_struct会在资源的运行队列中等待,这就是浅睡眠状态:

    我们在运行,为什么是阻塞态?

    因为 cpu 处理进程的速度是非常快的,外设的访问速度是跟不上cpu的处理速度的,在调用进程的时候,大部分的时间都是在等待外设资源就绪,因此这里的进程是阻塞状态;

    D--深度睡眠

    disk sleep--深度睡眠状态,如果进程等待的外设是磁盘资源,那么此进程就是深度睡眠状态

    如何理解:

    在内存不够用,进程过多,服务器压力过大时,可能会终止掉一些进程;

    在一个进程要将大量数据写入磁盘中,在写入过程中,进程会将自己设置成深度睡眠状态,等待磁盘的写入失败或者成功的消息,防止被操作系统终止造成数据丢失。

    结论:深度睡眠可以防止被操作系统终止掉;等待磁盘资源就绪,也是阻塞状态

    Z和X--僵尸状态和死亡状态

    死亡状态:资源可以立马回收

    僵尸状态:在Linux下,当进程退出的时候,一般不会直接进入死亡状态,而是进入僵尸状态,僵尸状态存储着这个进程的执行结果,维护着退出信息,通过僵尸状态来告知操作系统此进程是否需要终止。

    因为父进程控制着子进程,父进程不回收子进程,子进程会一直保持僵尸状态,没有用的进程还不能被释放占据着空间,这是内存泄漏!!

    孤儿进程

    如果父进程提前退出,子进程还在运行,那么子进程会被一号进程领养,被领养的子进程就叫做孤儿进程:

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main()
    6. {
    7. pid_t id = fork();
    8. if(id==0)
    9. {
    10. int j = 1;
    11. while(j++)
    12. {
    13. if(j>4)
    14. {
    15. printf("我是子进程,我已经孤儿了\n");
    16. sleep(1);
    17. }
    18. else
    19. {
    20. printf("我是子进程,我马上孤儿了\n");
    21. sleep(1);
    22. }
    23. }
    24. }
    25. else
    26. {
    27. int n = 4;
    28. while(n--)
    29. {
    30. printf("我是父进程,我还有%d秒退出了\n",n);
    31. sleep(1);
    32. }
    33. exit(0);
    34. }
    35. return 0;
    36. }

    此时有一个问题,我们 Ctrl + C 不能结束掉孤儿进程,因为它变成了后台进程

     必须要用命令将其终止掉:

    T--暂停状态

    当一个进程运行时,用命令传递信号令其暂停

    kill -19 pid

    此时这个孤儿进程就是暂停状态

    1. //继续进程
    2. kill -18 pid

    t--追踪状态

    暂停状态的一种,在gdb调试中,打上断点,运行至断点处即可:

  • 相关阅读:
    【2023-09-01】vue中自定义按钮设置disabled属性后,异常触发click事件
    22-9-17学习笔记
    万字长文深度解读Java线程池,硬核源码分析
    【VC】【全局修改windows系统环境变量】 实现和原理详解
    Sqlserver并行等待CXPACKET、CXCONSUMER问题的解决思路和案例
    【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令
    postgreSQL中的高速缓存
    Android Tint着色器
    Java怎么实现word转PDF?
    腾讯视频跟爱奇艺视频共享设备ip会不会出现错误
  • 原文地址:https://blog.csdn.net/weixin_53316121/article/details/126280135