进程管理的关键: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() 两次来确保子进程独立运行。
相关推荐
工业3D_大熊34 分钟前
3D可视化引擎HOOPS Visualize与HOOPS Luminate Bridge的功能与应用
linux·前端·c++·windows·macos·3d·c#
工业3D_大熊37 分钟前
3D开发工具HOOPS对B-Rep的支持:提升3D建模与可视化的精度与效率
linux·c++·windows·macos·3d·c#·制造
Stark、42 分钟前
【Linux】自定义项目-进度条
linux·运维·服务器
fegxg1 小时前
ROS Action通信解读
linux·ubuntu·机器人
见欢.1 小时前
sqli-labs靶场第26-30关
数据库
ggdpzhk1 小时前
idea连不上mysql数据库
数据库·mysql·intellij-idea
清酒伴风(面试准备中......)1 小时前
面经自测——Redis分布式锁实现/Redis/用Redis实现消息队列的功能怎么做中的RedLock具体解决了什么问题
数据库·redis·分布式·面试·面经·实习·自测
极客代码2 小时前
深入C语言文件操作:从库函数到系统调用
linux·c语言·文件操作·系统调用·库函数
逻各斯2 小时前
在Ubuntu上增加swap交换空间
linux·运维·ubuntu
简单哟2 小时前
mybatisplus 分库查询
数据库·spring boot·mybatis