进程管理的关键:Linux进程状态与常用命令解析

个人主页:chian-ocean

文章专栏:Linux

前言:

在现代操作系统中,进程是资源分配和任务调度的基本单位。作为一个多任务操作系统,Linux 必须在多个进程之间进行有效的调度和管理,这就需要对每个进程进行状态标记,从而明确进程当前的行为和系统资源的使用情况。

进程状态概念

进程是操作系统中程序执行的基本单位,它是一个正在运行的程序的实例。一个进程不仅包含程序代码,还包括其运行时的状态、资源(如内存、文件描述符)和执行信息。

为什么存在进程状态

进程状态的管理是操作系统实现多任务处理的关键。不同的进程状态能够帮助操作系统了解每个进程当前的执行状态,以便做出合理的资源调度和管理决策。简短来说,进程状态的存在有以下几个原因:

  1. 资源管理:不同的进程状态帮助操作系统高效管理CPU、内存、I/O设备等资源。比如,处于"就绪"状态的进程等待CPU时间片,而"睡眠"状态的进程等待I/O资源,可以避免无效的资源占用。
  2. 进程调度:操作系统根据进程的不同状态(如就绪、运行、阻塞等)来决定进程的执行顺序,实现公平且高效的调度。
  3. 并发控制:多任务操作系统需要有效地管理多个进程的并发执行。通过跟踪进程状态,操作系统可以协调不同进程的执行,避免冲突或竞争。
  4. 状态恢复与调度:进程在执行时可能会被挂起、等待或终止。进程状态帮助操作系统在合适的时机恢复、暂停或终结进程,确保系统正常运行。

linux几种进程状态

  • R - Running(运行中):该进程正在使用CPU,或者在就绪队列中等待CPU调度。

  • S - Sleeping(睡眠中):进程处于睡眠状态,通常表示它正在等待某些事件的完成或资源的释放,例如等待I/O操作(磁盘读写、网络数据、用户输入等)。

  • D - Uninterruptible-Sleep(不可中断的睡眠状态):进程在等待某些硬件事件,无法被信号中断。

  • X - Dead(死亡):这个状态在某些版本的ps工具中表示进程已经死亡(已被终止)。

  • T - Stopped(停止):进程已被停止,通常是通过信号(如SIGSTOP)暂停或挂起的状态。

  • Z - Zombie(僵尸):进程已经结束执行,但仍然保留在进程表中,等待父进程读取其退出状态。

进程状态

R状态

R状态 表示进程的"运行"状态,具体来说,是指进程正在运行或者准备运行的状态。这个状态包括了两种可能的情况:进程正在执行(在CPU上运行),或者进程处于就绪状态,等待操作系统调度器分配CPU资源。

R状态的详细解释:

含义:进程被操作系统的调度器选中并分配了CPU时间片,进程正在执行其指令。

常见场景

  • 进程获得了CPU时间片,正在执行其代码。
  • 该状态下的进程正在占用CPU资源,进行计算任务、处理数据或执行其他操作

时间片(Time Slice)是操作系统在进行进程调度时所分配给每个进程的执行时间。每个时间片是一个固定的时间段,操作系统通过轮流分配时间片给各个进程,从而实现进程的多任务并发执行

demo:

cpp 复制代码
# 执行代码
 #include<iostream>
 #include<unistd.h>
 #include <sys/types.h>
 using namespace std;
 
 int main()
 {
    while(true)
     {
         cout<<" I am a prrocess"    <<"pid: " <<getpid()<<endl;     
         sleep(1);
     }
     return 0;
 }

利用ps查看进程

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process

在进程状态属性为什么是S(睡眠状态),我们在执行进程的时候,为什么不是R(运行状态)

  • S状态表示进程正在等待某些外部事件的发生,通常是I/O操作。

执行流程:

  • 等输入流写入到缓冲区。

  • 操作系统将数据从缓冲区写入终端设备。

  • 在写入过程中,cout命令可能会进入S状态,等待I/O操作(比如将数据从内存写入终端屏幕)。

  • 当I/O操作完成后,cout命令恢复执行并退出。

**简而言之:**由于CPU运行速度太快,一直在等硬件设备,所以是S状态

cpp 复制代码
//执行代码
 #include<iostream>
 #include<unistd.h>
 #include <sys/types.h>
 using namespace std;
 
 int main()
 {
    while(true);
     return 0;
 }

再次利用ps查看进程

  • 这次没有IO操作就不存在等待的IO的过程,所以进程就状态就是R(运行状态)

S状态

在Linux操作系统中,进程的S状态 代表进程处于睡眠状态(Sleep) ,通常也称为可中断睡眠状态(Interruptible Sleep) 。处于S状态的进程正在等待某个事件或资源的完成,例如等待I/O操作、等待信号或者等待锁释放等。该状态下的进程不会占用CPU资源,直到它的等待条件得到满足,并被唤醒。

S状态的详细解释:

含义

  • S 表示进程正在等待某个事件或资源的完成,进入了睡眠状态。这是一个可中断的睡眠状态,意味着进程可以被外部信号中断。
  • 在S状态下,进程会进入阻塞队列并挂起,直到所等待的条件满足(如I/O操作完成、资源可用等)或进程接收到信号(例如终止信号、暂停信号等)而被唤醒。
  • S状态通常发生在进程发起I/O操作(如读取文件、网络数据等)时,或者等待其他进程、信号或资源

常见场景:

  • 等待I/O操作(磁盘I/O、网络I/O等)
  • 等待外部信号、事件或进程间通信
  • 等待资源(如内存、锁、硬件设备等)
  • 等待系统调用完成(如read()write()等)

demo:

cpp 复制代码
// 执行代码
 #include<iostream>
 #include<unistd.h>
 #include <sys/types.h>
 using namespace std;
 
 int main()
 {
    while(true)
     {
         cout<<" I am a prrocess"    <<"pid: " <<getpid()<<endl;     
         sleep(1);
     }
     return 0;
 }
  • 这本质上就是一种等待IO的行为。

执行代码,ps监控

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process

等待资源:

cpp 复制代码
// 执行代码
#include<iostream>
#include<unistd.h>
#include <sys/types.h>
#include<string>
using namespace std;
 
int main()
{
   string s;
   while(true)
    {
       cin >> s;
    }
    return 0;
}

执行代码,ps监控

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process
  • 在此处依旧是S状态,无论是等待系统资源还是外部资源都是等待某种资源。
  • CPU在执行进程的时候由于缺少资源,让进程在wait_queue(等待队列)中等待的这个过程就是一种(S)睡眠状态。

D状态

在Linux操作系统中,D状态 (不可中断睡眠状态,Uninterruptible Sleep)表示进程处于阻塞状态 ,但与S状态不同,进程处于D状态时不能被外部信号中断。这通常发生在进程等待某些硬件资源或进行阻塞I/O操作时。进程会一直处于D状态,直到资源或事件准备就绪为止。

**含义:**是进程在等待某些特定资源(通常是硬件资源或底层I/O设备)时的状态。

D状态的特点

  • 不可中断 :与S状态(可中断睡眠状态)不同,D状态下的进程不能被信号中断,无法通过外部操作(如发送SIGKILL)来强制终止。
  • 通常是硬件等待:进程通常会在等待底层硬件设备(如磁盘、网络、文件系统等)响应时进入D状态。例如,进程可能正在等待磁盘设备返回数据,或者在等待硬件设备准备好进行某个操作。
  • 阻塞I/O操作:进程在执行一些可能导致长时间等待的I/O操作时,可能会进入D状态,直到I/O操作完成。例如,读取磁盘上的大文件,或等待网络硬件返回数据。

3. 进程进入D状态的常见场景

  • 等待硬件I/O操作完成:
    • 磁盘I/O:进程在进行磁盘读写时,如果操作系统没有足够的缓冲区或磁盘设备忙碌,进程可能会进入D状态,直到磁盘操作完成。
    • 网络I/O:例如,进程等待从网络接口接收到数据,或者等待将数据发送到网络。在某些情况下,进程会在等待网络设备准备好进行数据传输时进入D状态。

Z状态

Z状态也叫做僵尸进程

  • 僵尸进程 是子进程已完成执行(死亡)但其父进程尚未读取子进程的退出状态。
  • ps 输出中,僵尸进程的状态标识为 Z
  • 僵尸进程不会占用系统资源(如 CPU、内存),但仍保留进程 ID(PID)。

Z状态的特点

内存占用
  • 僵尸进程不占用内存或 CPU,因为它已经结束运行。
  • 它只占用一个 进程 ID (PID) 和少量的系统资源来记录进程退出信息。
进程表中保留
  • Z 状态进程的条目会保留在进程表中,直到其父进程通过 wait() 系列系统调用清理它。
  • 如果父进程没有处理子进程的退出状态,僵尸进程将一直存在。

进程进入Z状态的常见场景

cpp 复制代码
 #include<iostream>
 #include<unistd.h>
 #include <sys/types.h>
 #include<string>
 using namespace std;
            
 int main()
 {                               
    pid_t id = fork();                                 
    while(true)                                        
     {             
         if(id == 0)                             
         {                           
             cout<<"我是子进程"<<getpid()<<endl;                   
            sleep(3);                   
         }              
         else
       {
            cout<<"我是父进程"<<getpid()<<endl;
            sleep(3);                                            
        }                                                 
    }                                 
     return 0;                                              
 }  

上面的进程执行后会创建两个进程,然后杀死子进程

cpp 复制代码
kill -9 <pid> // 杀死进程

执行ps指令, 进程进程查看

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process
  • 观察上面的进程状态变为了Z状态。

如何造成僵尸进程

  1. 子进程创建: 父进程通过调用 fork() 创建一个子进程。

  2. 子进程执行: 子进程完成自己的任务并退出,进入 EXIT_ZOMBIE 状态。

  3. 系统通知父进程: 子进程退出时,操作系统会向父进程发送 SIGCHLD 信号,告知其子进程已终止。

  4. 父进程未处理:

  • 如果父进程未对 SIGCHLD 信号做出响应,也未调用 wait()waitpid(),子进程的资源无法被完全释放。
  • 子进程的状态信息仍保留在进程表中,导致该进程成为僵尸进程。

僵尸进程的危害

  • 占用系统资源
  • 系统不稳定
  • 潜在的安全隐患
  • 干扰父进程逻辑

孤儿进程

有了僵尸进程,就存在孤儿进程

**孤儿进程(Orphan Process)**是指一个进程的父进程在其运行期间终止,但子进程仍在继续运行。孤儿进程由操作系统的 init 进程(PID 为 1)接管,init 会成为孤儿进程的新父进程,负责清理它在结束后的资源。

孤儿进程的特点

  1. 父进程已终止: 孤儿进程的直接父进程已经退出。
  2. init 进程接管: 系统会自动将孤儿进程的父进程指向 init,由其负责清理子进程的资源。
  3. 进程状态正常: 孤儿进程仍在运行,不会进入僵尸状态。

进程孤儿状态的场景

cpp 复制代码
 #include<iostream>
 #include<unistd.h>
 #include <sys/types.h>
 #include<string>
 using namespace std;
            
 int main()
 {                               
    pid_t id = fork();                                 
    while(true)                                        
     {             
         if(id == 0)                             
         {                           
             cout<<"我是子进程"<<getpid()<<endl;                   
            sleep(3);                   
         }              
         else
       {
            cout<<"我是父进程"<<getpid()<<endl;
            sleep(3);                                            
        }                                                 
    }                                 
     return 0;                                              
 }  

杀死父进程

bash 复制代码
kill -9 <pid> // 杀死进程

执行ps指令, 进程进程查看

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process
  • 查看到子进程PPID是1.

孤儿进程的产生原因

  1. 父进程崩溃或异常退出: 父进程因错误、信号或其他原因终止,导致其子进程成为孤儿进程。
  2. 父进程有意终止: 某些设计模式下,父进程会主动退出,将子进程交给 init 进程接管。例如:
    • 一些守护进程的实现会通过 fork() 两次来确保子进程独立运行。
      死父进程**
bash 复制代码
kill -9 <pid> // 杀死进程

执行ps指令, 进程进程查看

bash 复制代码
ps ajx | head -1 ; ps ajx | grep process

外链图片转存中...(img-t7Y5ceQx-1734353587109)

  • 查看到子进程PPID是1.

孤儿进程的产生原因

  1. 父进程崩溃或异常退出: 父进程因错误、信号或其他原因终止,导致其子进程成为孤儿进程。
  2. 父进程有意终止: 某些设计模式下,父进程会主动退出,将子进程交给 init 进程接管。例如:
    • 一些守护进程的实现会通过 fork() 两次来确保子进程独立运行。
相关推荐
程序员拂雨6 分钟前
MongoDB知识框架
数据库·mongodb
风行無痕40 分钟前
Ubuntu Linux系统配置账号无密码sudo
linux·服务器·ubuntu
消失在人海中1 小时前
oracle 会话管理
数据库·oracle
爆农1 小时前
centos搭建dokcer和vulhub
linux·运维·centos
SZ1701102312 小时前
中继器的作用
服务器·网络·智能路由器
chenxy022 小时前
如何快速分享服务器上的文件
运维·服务器
Wyc724092 小时前
JDBC:java与数据库连接,Maven,MyBatis
java·开发语言·数据库
重启就好2 小时前
【Ansible】模块详解
linux·服务器·ansible
烧瓶里的西瓜皮2 小时前
Go语言从零构建SQL数据库(9)-数据库优化器的双剑客
数据库·sql·golang
o0o_-_3 小时前
【瞎折腾/mi50 32G/ubuntu】mi50显卡ubuntu运行大模型开坑(三)安装风扇并且控制转速
linux·运维·ubuntu