Linux进程理解【进程状态】

简介: Liunx进程状态详细讲解,包括运行、睡眠、暂停、死亡等状态讲解,以及僵尸进程、孤儿进程的介绍,干货满满!

Linux进程理解【进程状态】

进程运行时,进程会被CPU调度,但系统中存在多个进程,进程运行的先后顺序等等该怎么保证呢?OS将进程分成了运行、睡眠、休眠、暂停、死亡等几种状态来对进程进行管理,下面我将带大家学习进程各种状态的应用场景及作用

1. 阻塞和挂起

==阻塞==

  • 阻塞就是进程因为等待某种条件就绪,而导致的一种不推进的状态
  • 简单来说就是,进程卡住了,原因是在等待某种资源
  • 从数据结构角度来理解,阻塞就是不被调度,这一定是因为进程task_struct结构体需要在某种被OS管理的资源下排队

举个例子,我们在使用到scanf函数时,当我们运行程序,光标会一直到控制台闪烁,实际上此时这个进程就是阻塞状态,它是在等待键盘的输入,也就是在等待键盘输入这一资源

为什么要阻塞呢?

  • 进程要通过等待的方式,等具体的资源被别人用完之后,再被自己使用,阻塞就是进程等待某种资源就绪的过程

==挂起==

  • 挂起状态就是当CPU资源不足时,OS将数据和代码(进程)交换到磁盘中挂起,此时内存中只有PCB

举个例子,我们在下载软件时,下载这个进程就会被加载到内存中,此时如果突然断网了,就会阻塞(缺少网卡资源),由于这里进程暂时不会被运行,资源放在内存中就会浪费空间,OS就会将代码和数据(进程)暂时存放到磁盘中,等到有网了再从磁盘中拿取,这个过程就是挂起状态,可以说挂起是一种特殊的阻塞状态

2. 进程状态

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): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)

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

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

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

2.1 运行状态 R

按照以往的观点,程序一旦运行起来了,那么该程序就是处于运行状态,真的是这样吗?下面我们来看一个例子:

Makefile文件

myproc:mytest.c
    gcc -o myproc mytest.c

.PHONY:monitor
monitor:
    ps ajx | head -1 && ps ajx | grep myproc | grep -v grep 

.PHONY:clean
clean:
    rm -f myproc

这里可以直接将监视指令写入Makefile文件中,使用起来更加方便

下面我们运行监视起来看看现象

通过监视可以观察到此时的进程状态为S+,也就是睡眠状态,+ 号表示当前进程在前台运行

这是为什么呢,难道程序没有运行吗?

  • printf库函数是向外设打印信息,当CPU执行printf库函数时,就需要访问外设,但是这里如果时频繁打印的时候,CPU的运行速度是非常快的,外设和CPU的速度相差太大,导致外设就不一定是就绪的,那么这里就会产生阻塞,这个进程就会被放在对应的外设的结构体队列中排队 。

  • 其实程序运行了,但是我们很难捕捉到,大多数时间,进程都在外设等待队列中排队,导致效果上是休眠状态

这里如果我们去掉printf这一条需要访问外设的语句,进程就不会发生排队阻塞了,此时就可以观察到是运行状态

注意:R状态表示进程在运行,但不一定是在CPU上运行,进程在运行队列中排队也算运行状态

2.2 睡眠状态 S

睡眠状态S的本质就是进程阻塞,表示进程等待某种资源,在上面的测试中已经体现了

进程处于睡眠志状态我们可以通过ctrl + c来终止进程

当进程是在后台运行时,状态表示没有 +号,此时ctrl + c是无法终止进程的,需要使用kill -9 PID来终止进程

2.3 休眠状态 D

休眠状态D是一种特殊的睡眠状态,又被称为不可中断睡眠状态,休眠状态下的进程是无法终止的,只能等到拿到所需资源,阻塞结束后,进程才会停止休眠状态

休眠状态比较少见,一般会出现于IO阻塞,这个状态的进程通常会等待IO的结束。系统不能出现D状态,出现D状态说明磁盘被占用空间很大,此时这个进程不能删除,一直等到数据被CPU处理完后这个进程才可中断,此时就表示系统离宕机不远了

2.4 暂停状态 T

可以通过指令使进程处于暂停状态T

  • kill -19 PID暂停指定进程
  • kill -18 PID恢复进程暂停

使用kill -19 PID暂停进程

使用kill -18 PID恢复进程暂停

可以看到此时恢复暂停后,进程状态为S,没有+表示进入了后台睡眠状态,此时使用ctrl+c是无法终止进程的,只能使用kill -9 PID来终止

gdb 中调试代码时,打断点本质上就是 使 进程 在指定行暂停运行,此时 进程 处于 t状态,也就是追踪暂停状态

2.5 死亡状态 X

当进程被终止后,就是处于死亡状态X,无法在任列表中看到这个状态,死亡状态只是一个返回状态

2.6 僵尸状态 Z

僵尸状态Z引入

  • 在多进程执行任务时,如果一个进程退出了,马上就成为了死亡X状态,bash作为父进程,是没有机会拿到退出结果的
  • 为了解决这一问题,Linux下当子进程终止后,不会立即退出,而是会维持一个僵尸Z状态,方便父进程来读取子进程的退出结果

bash有僵尸进程的回收机制,直接在bash环境下终止子进程无法观察到僵尸状态,这里我们可以使用上文中讲到的fork()函数来创建父子进程关系,观察僵尸状态

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>

int main()
{
   
   
  pid_t ret = fork(); //获取返回值
  assert(ret != -1);  //创建失败的情况

  if(ret == 0) //子进程
  {
   
   
    while(1)
    {
   
   

      printf("我是子进程, 我的PID是: %d,PPID是: %d\n", getpid(), getppid());
      sleep(1);
    }
  }
  else if(ret > 0) //父进程
  {
   
   
    while(1)
    {
   
   
      printf("我是父进程, 我的PID是: %d,PPID是: %d\n", getpid(), getppid());
      sleep(1);
    }                                                      
  }
  else
  {
   
   
    printf("进程创建失败!");
  }

  return 0;
}

运行监视起来观察现象

这时我们终止子进程,由于子进程没有被回收,此时就是处于僵尸状态

僵尸进程的危害:

  • 僵尸状态一直不退出,PCB需要一直维护,浪费内存资源
  • 僵尸进程如果不被回收,还会导致内存泄漏和标识符占用问题

至于如何避免僵尸进程造成的问题,我会在后续文章详细讲解

3. 孤儿进程

上面了解到了父子进程运行时,当子进程终止后,并不会直接结束,而是处于僵尸状态,方便父进程来读取子进程的结果。

那么如果反过来,父进程先结束,子进程会处于什么状态呢?

下面我们再来启动上面的程序实践一下

然后终止父进程

这里我们观察到,终止父进程后子进程的父进程变成了1号进程

此时的子进程就是孤儿进程

  • 当父进程退出后,子进程会被OS自动领养
  • 子进程的父进程就变成了1号进程,即OS,这个被领养的子进程就是孤儿进程

为什么子进程会被OS领养呢?

  • 如果不被领养,子进程后续再退出,就会变成一个无人回收的僵尸进程,会造成内存泄漏的问题

Linux进程理解—进程状态,到这里就介绍结束了,本篇文章对你由帮助的话,期待大佬们的三连,你们的支持是我最大的动力!

文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
14天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
44 4
linux进程管理万字详解!!!
|
5天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
41 8
|
13天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
46 4
|
14天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
16天前
|
消息中间件 存储 Linux
|
22天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
24 1
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
22 1
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
30 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
26 0
Linux c/c++进程间通信(1)
|
1月前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
20 0

热门文章

最新文章