探索Linux下进程状态 | 僵尸进程 | 孤儿进程


在这里插入图片描述

Linux下进程状态

任何进程在运行时都会有自己的状态

下面的状态在kernel源代码里定义:

/*
* 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运行状态(running) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。

  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

R、S状态


下来看下面这串代码的运行状态:

#include<stdio.h>    
#include<sys/types.h>    
#include<unistd.h>    
    
int main()    
{    
  while(1)    
  {    
    printf("I am a process,pid:%dn",getpid());                    
  }    
  return 0;    
} 

重复查看代码运行状态:while :; do ps ajx | head -1 && ps ajx | grep testStatus | grep -v grep; sleep 1; done

在这里插入图片描述
在这里插入图片描述
当程序运行时,显示的是S状态,S状态是休眠状态

将代码中while循环里面打印的内容去掉,再来查看状态:

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

在这里插入图片描述

在这里插入图片描述
此时代码中只有while死循环,此时是R状态,表示进程正在运行,这个很好理解。

这两个程序刚刚都是运行,为什么第一个代码是S状态(休眠状态)呢?

休眠状态本质上是程序什么都没有做,这叫做休眠状态,但是刚刚一直在打印内容,说是什么都没做,好像有点不对。原因在于:CPU的执行速度很快,比显示器设备显示快的很多,该进程大部分时间都是在显示器的等待队列里等待显示设备就绪,因此最终查看的状态是休眠状态(S状态)。当我们去掉printf后,进程始终都在运行状态里,所以最终查出来的状态是运行状态(R状态)。

休眠状态本质就是进程在等待“资源”就绪

当程序正在运行时,如果ctrl+c,进程被终止,称为可中断睡眠。

S+:意思是进程在前台运行,S是进程在后台运行

在这里插入图片描述
当进程处于后台运行时,无法通过ctrl+c终止程序,需要使用指令:kill -9


T/t状态

T状态和t状态我们可以认为这两个状态是一样的,对于一个进程,可以使用指令kill -19 进程的pid来让进程处于停止状态

T/t状态:让进程处于暂停状态。

在这里插入图片描述
让进程结束暂停状态,继续运行:kill -18 进程pid

当我们使用gdb调试打断点时,遇到断点处就暂停,此时是t状态,这种场景是被追踪暂停。

D状态

D状态:Linux系统比较特有的一种进程状态。在Linux系统层面称作浅度睡眠,S称为深度睡眠。

disk是磁盘的意思,好像是说针对于磁盘的一种状态

有这样一个场景,操作系统中有一个进程,需要将1GB的数据写入磁盘中。首先根据冯诺依曼体系,这个进程要把数据存入磁盘中,实际上是将数据存入外设。进程把数据交给磁盘,磁盘在存储时需要时间,当前进程需要等待,也就是S状态。Linux操作系统负责的是进程管理,文件系统等,整个操作系统管理系统软硬件资源,当系统中整个资源内存不足时,Linux操作系统有权杀掉进程来释放空间。进程在等待磁盘反馈,操作系统忙前忙后,忙的不可开交,看到这个进程:“你干嘛呢?我快忙死了,我快要崩溃了,一旦我崩溃你可知后果??”,操作系统生气了,一气之下把这个进程干掉了,释放了这个进程的内存资源。操作系统继续干自己的事情了,磁盘就说:“不好意思,写入失败,你(进程)跟用户说一下。哎?进程呢?你咋不见了”磁盘在想咋办呢?此时又有新的进程来了,磁盘心想:“不管了,反正之前的进程给的数据写入失败了,先写入新进程的数据吧”。这就造成一批数据丢失问题,如果这个数据很重要,那麻烦大了。用户要对操作系统、磁盘、进程批评审查了。这么一看,操作系统、进程、磁盘好像都没有错,是制度的问题,当进程在向磁盘中写入数据时谁都不能将该进程干掉。于是D状态就诞生了。

当一个进程处于D状态时,它不会响应任何请求,任何人和操作系统都无法干掉这个进程。

结束D状态方式:

  • 等待某个条件,比如数据读入完毕
  • 直接断电

僵尸进程

  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

代码:

#include<stdio.h>    
#include<sys/types.h>    
#include<unistd.h>    
    
int main()    
{    
  pid_t id=fork();    
  if(id==0)    
  {    
    int cnt=5;    
    while(cnt)    
    {    
      printf("I am child process,pid:%d,ppid:%dn",getpid(),getppid());    
      sleep(1);    
      cnt--;    
    }    
    _exit(0);    
  }    
  else    
  {    
    while(1)    
    {    
      printf("I am father process,pid:%d,ppid:%dn",getpid(),getppid());    
      sleep(1);    
    }                                                                          
  }    
  return 0;    
}  

在这里插入图片描述

已经运行完毕,但是需要维持自己的退出信息,在自己的进程task_struct会记录自己的退出信息,未来让父进程来进行读取。如果没有父进程读取,僵尸进程会一直存在。

上述代码中,子进程执行完五次后,就处于Z状态并且后面跟了一个<defunct>,该单词有不存在的意思,只不过还等待父进程来回收它的资源。处于Z状态的进程的相关资源不能被释放。只有当父进程把子进程的相关资源回收后,子进程才能变成死亡状态(X状态)。一般的,我们讲这种处于Z状态的进程叫做僵尸进程,如果父进程一直不回收,将长时间占用内存资源,造成内存泄漏。

僵尸进程危害:

  • 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护?是的!
  • 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
  • 内存泄漏?是的!

孤儿进程

僵尸进程代码中是子进程先退出,父进程一直运行

在孤儿进程中,让父进程先退出,子进程一直运行

代码:

#include<stdio.h>    
#include<sys/types.h>    
#include<unistd.h> 
 
int main()                                                                          
{                                                                                   
  pid_t id=fork();                                                                  
  if(id==0)                                                                         
  {                                                                                 
    int cnt=500;                                                                    
    while(cnt)                                                                      
    {                                                                               
      printf("I am child process,pid:%d,ppid:%dn",getpid(),getppid());             
      sleep(1);                                                                     
      cnt--;                                                                        
    }                                                                               
    _exit(0);                                                                       
  }                                                                                 
  else                                                                              
  {                                                                                 
    int cnt=5;                                                                      
    while(cnt--)    
    {                                                                                       
          
      printf("I am father process,pid:%d,ppid:%dn",getpid(),getppid());
      sleep(1);        
    }                  
  }            
  return 0;
}

在这里插入图片描述
父进程结束后,只剩下子进程,为什么父进程不会处于僵尸进程?父进程也是bash的子进程,父进程结束后,它的父进程bash会将它回收掉,并且过程很快,所以父进程不会处于僵尸状态。

当父进程结束后,它的子进程的父进程变成1号进程,即操作系统

在这里插入图片描述
将父进程是1号进程的进程叫做孤儿进程。 孤儿进程被1号i进程领养,当然要有进程回收喽。

在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>