【Linux】:进程状态

1.操作系统学科中的进程状态

进程状态_百度百科 (baidu.com)

运行

时间片 - 维基百科,自由的百科全书 (wikipedia.org)

阻塞

一般被阻塞的进程是指等待某个事件的进程,例如该进程正在等待计算机释放释放或等待I/O操作的完成。

当我们执行一个含有scanf的可执行C程序时,会发现终端会停顿一下,等待我们用键盘输入字符,这个可执行程序等待键盘输入的状态就叫做进程的阻塞状态。

操作系统内存在管理各种硬件相关的数据结构对象,数据结构对象中包含等待队列,等待队列执行的一系列进程都处在阻塞状态。

挂起

进程的挂机状态很少见。当阻塞进程过多,造成内存不足时,操作系统会先将阻塞进程的代码和资源存储在磁盘,只保留PCB对象在队列中排队,这个过程叫做"内存换出",同理,当从磁盘中取回进程的代码和数据,这叫做"内存的换入"。当进程只有PCB对象在排队时,此时进程的状态被称为"挂起"。

2.1.2 操作系统之进程的状态(运行、就绪、阻塞、创建、终止)及转换(就绪->运行、运行->就绪、运行->阻塞、阻塞->就绪)_计算机的就绪状态-CSDN博客

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:运行状态

R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

我们编写程序来看一下, 我们去掉循环中的打印。

S:等待状态(阻塞状态)

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

我们编写一个需要键盘输入的C程序。

大多数情况下,bash进程都在阻塞状态,等待用户输入指令。

S是浅度睡眠,可以被唤醒(杀死)。比如当程序执行到scanf函数时,可以直接Ctrl+C,中断进程。

D:阻塞状态(深度睡眠)

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

为什么进程要有Disk sleep状态呢?

因为当进程向磁盘写入数据时,需要写入一些非常重要的信息,比如涉及100亿元的1个G银行转账信息,需要等待磁盘写入完成并返回写入情况(磁盘是否写入成功等情况),如果在等待期间,操作系统因内存不足需要杀掉一些进程释放空间,一不小心将这条正在等待磁盘写入返回的进程杀掉,后果不堪设想。为了防止中断控制写入磁盘数据的进程,Linux操作系统会把正在写入的进程设置为D状态,处在D状态的进程,不会响应任何请求,包括操作系统要强制杀死该进程都不允许。

总结:处在D状态的进程不响应任何请求。

T:停止状态

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

S状态和T状态有什么区别?

T状态可能也跟S状态一样,在等待某个事件的响应,也可能单纯地被另一个进程控制为暂停。

比如用gdb调试一个程序,打断点然后运行到断点处,该进程就处于T状态。(我们暂时认为T和t是一个状态)

X:死亡状态

X死亡状态(dead):这个状态只是一个返回状态,我们不会在任务列表里看到这个状态。表示进程已经终止。

Z(zombie):僵尸进程

  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
cpp 复制代码
#include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 int main()
  5 {
  6     pid_t id = fork();
  7     if(id == 0)
  8     {
  9         //child
 10         int cnt = 5;
 11         while(cnt)
 12         {
 13             printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid    (),getppid(),cnt);
 14             --cnt;
 15             sleep(1);
 16         }
 17         exit(0);
 18     }
 19     else
 20     {
 21         //father
 22         while(1)
 23         {
 24             printf("I am father,pid:%d,ppid:%d,\n",getpid(),ge    tppid());                                                     
 25             sleep(1);
 26         }
 27     }
 28     return 0;
 29 }

我们编写上面的.c文件,执行,

fork创建的子进程在打印五次"I am child"后,会进入僵尸状态,等待父进程读取。

僵尸进程的危害

  • 任何一个进程都要返回自身的执行状态,当该进程任务完成后,在进程销毁前要将完成信息返回给父进程,在此前见,该进程进入僵尸状态,直到交接完成信息给父进程才可被销毁。
  • 维护退出状态本身就是维护数据,也就是维护进程的基本信息,所以task_struct(PCB)会继续占用内存,换句话说,父进程不回收子进程,子进程的PCB实例化对象一直都要占用内存。
  • 一个父进程创建了很多子进程,就是不回收,就会造成内存泄漏。因为数据结构对象会一直占用内存。

孤儿进程

操作系统领域中,孤儿进程 (Orphan Process)指的是在其父进程执行完成或被终止后仍继续运行的一类进程孤儿进程 - 维基百科,自由的百科全书 (wikipedia.org)

我们编写一个父进程比子进程先销毁的程序。

cpp 复制代码
1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 int main()
  5 {
  6     pid_t id = fork();
  7     if(id == 0)
  8     {
  9         //child
 10         int cnt = 500;
 11         while(cnt)
 12         {
 13             printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppi    d(),cnt);
 14             --cnt;
 15             sleep(1);
 16         }
 17         exit(0);
 18     }
 19     else
 20     {
 21         //father
 22         int cnt = 5;
 23         while(cnt--)                                                   
 24         {
 25             printf("I am father,pid:%d,ppid:%d,\n",getpid(),getppid());
 26             sleep(1);
 27         }
 28     }
 29     return 0;
 30 }

生成对应的可执行程序并执行,如下,

我们发现当8682进程的原父进程结束后,父进程变为"1"。我们输入top指令回车,可以看到1号进程。1号进程其实就是系统进程,

所以当父进程比其子进程先被回收时,子进程一般会被系统的1号进程"领养"(不同的操作系统回收的进程可能不同),1号进程就变成了子进程的父进程。

注意:Ctrl + C不能终断孤儿进程,要用kill + 9 + 孤儿进程标识符。

相关推荐
dsywws2 分钟前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
yeyuningzi10 分钟前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
上辈子杀猪这辈子学IT29 分钟前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian
minihuabei34 分钟前
linux centos 安装redis
linux·redis·centos
EasyCVR1 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
lldhsds2 小时前
书生大模型实战营第四期-入门岛-1. Linux前置基础
linux
wowocpp2 小时前
ubuntu 22.04 硬件配置 查看 显卡
linux·运维·ubuntu
山河君2 小时前
ubuntu使用DeepSpeech进行语音识别(包含交叉编译)
linux·ubuntu·语音识别
鹏大师运维2 小时前
【功能介绍】信创终端系统上各WPS版本的授权差异
linux·wps·授权·麒麟·国产操作系统·1024程序员节·统信uos
筱源源2 小时前
Elasticsearch-linux环境部署
linux·elasticsearch