进程状态以及两种特殊进程

进程可以处于运行、睡眠、停止、死亡和僵尸等状态。运行状态并不总意味着实际运行,可能在运行队列等待CPU。睡眠状态分为可中断和不可中断两种。停止状态由SIGSTOP和SIGCONT信号控制。僵尸状态是进程已退出但信息仍保留在进程表中,等待父进程回收。孤儿进程是父进程退出后,子进程变成由init进程接管的状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在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 */
};

1 具体状态

1.1 运行状态

  • R 运行状态(running):

    • 并不意味着进程一定在运行中,它表明进程要么是在运行中,要么在运行队列里。

    • 一个进程处于运行状态,实质上是这个进程已经被放入运行队列run_quene当中,随时可被CPU进行调度。(等待CPU)

1.2 睡眠状态

当我们完成某种任务时,任务条件不具备,需要进程进行某种等待,此时这种状态称为 S 或 D。一个外设可能被多个进程等待,此时这个队列叫做等待队列wait_queue(等待外设)。当“等待成功”,进程将被设为R状态,进入运行队列run_quene

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

  • D 磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束,进程如果处于D状态,不可被杀掉。(深度睡眠

所谓的进程,在运行的时候,有可能因为运行需要,可能会在不同的队列里。

因此,在同一个队列里,所处的状态不一定是一样的。

我们把,从运行状态的task_struct(run_queue),放到等待队列中,就叫做挂起等待(阻塞)

从等待队列,放到运行队列,被CPU调度就叫做唤醒进程!

1.3 停止状态

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

  • t (tracing stop):追踪导致的暂停状态(断点之类)。

1.4 死亡状态&僵尸状态

  • X 死亡状态(dead):这个状态只是一个返回状态,不会在任务列表里看到这个状态。
    • 回收进程资源=进程相关的内核数据结构+相关代码和数据
  • Z 僵尸状态(zombie):进入僵死状态,辨别“死亡原因”,即进程退出的信息,系统将此信息保存在task_struct里,供系统或父进程进行读取。

进程的状态信息存在task_struct(PCB)里。

image-20230425224057518

2 进程状态的意义

方便OS快速判断进程,完成特定的功能,比如调度,本质是一种分类。

3 进程状态实例

3.1 R&S&T认识

代码myproc.cc

#include<iostream>
#include<unistd.h>
int main(){
    while(true){
        std::cout << "hello world !" << std::endl;
    }
    return 0;
}

运行并查看进程状态,为S+
image-20230426131457300
查看可执行的kill,并停止目标进程
image-20230426131726464
查看停止后进程状态,为T
image-20230426132236164
使进程继续运行,kill -18,并查看进程状态,此时我们发现,进程状态为S,并且使用ctrl+c不能停止进程了
image-20230426133014414
这是因为此时进程在后台运行,此时需用kill -9(SIGKILL)杀掉进程
image-20230426133320500

  • 前台运行:直接使用 ./myproc
  • 后台运行:使用 ./myproc &,后台进程,可以在当前页面继续执行其他命令

3.2 认识Z & 僵尸进程 & 孤儿进程

僵尸进程

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

首先通过下面这个例子认识一下Z进程。

myproc.cc

#include <iostream>
#include <unistd.h>
int main(){
    
  pid_t id = fork();
    
  if(id == 0){
     //child
    while(true){
      std::cout << "I am child, running !" << std::endl;
      sleep(2);
    }
  }
  else{
    //parent
    std::cout << "father do nothing!\n" << std::endl;
    sleep(50);
  }
  return 0;
}
  • 运行./myproc,使用while :; do ps axj | head -1 && ps ajx | grep myproc | grep -v grep; sleep 1; echo "############################################"; done查看当前进程状态

    image-20230426195030189

  • 使用kill -9杀掉子进程

    image-20230426195207641

    image-20230426195242091

可以看到,因为无回收的操作,子进程成为僵尸进程

父进程如果一直不读取,那子进程就一直处于Z状态,Z状态一直不退出,PCB一直都要维护。

长时间不回收,占用系统资源,而C中定义一个结构体变量,是要在内存的某个位置进行开辟空间,此时僵尸进程的存在也有可能造成内存泄漏

如果父进程先”死“,那么子进程会如何,谁来回收它呢?

孤儿进程

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?父进程先退出,子进程就称之为“孤儿进程”

孤儿进程被1号init进程领养,由init进程回收。

myproc.cc

#include <iostream>
#include <unistd.h>
int main(){
    
  pid_t id = fork();
    
  if(id == 0){
     //child
    while(true){
      std::cout << "I am child, running !" << std::endl;
      sleep(2);
    }
  }
  else{
    //parent
    std::cout << "father do nothing!\n" << std::endl;
    sleep(10);
    exit(1);
  }
  return 0;
}
  • 开始运行。

    image-20230426200257763

  • 10秒后,父进程结束,子进程依然在。并且进程状态由S+变为S,ppid变为1,称此时的进程为孤儿进程,而”领养孤儿进程“的1号进程通常为操作系统

    image-20230426201059595

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值