【Linux】进程4——进程状态

1.进程状态

什么是状态?

每个人都有状态——颓废,阳光,积极向上。。。。

进程也有状态

在操作系统中,由于进程的数量是非常多的,而系统的资源又非常少,所以不可能每一个进程在每时每刻都会处于上处理机运行的状态,所以在系统中应该要为进程维护好相关的状态:运行态,终止态,阻塞态,挂起态,还有Linux中特有的其他进程状态:R(Running),S(Sleeping),D(disk sleeping),T(stopping),t(trace stopping)

 事实上进程的基本状态如下

1.1.运行态

我们先补充一点知识        

        一个进程只要把自己放到CPU上开始运行了,是不是要一直执行完毕,才把自己放下来是不是?

  • 不是,因为每个进程都有一个时间片的概念,每个进程一次性在CPU上最多执行时间片大小的时间
  • 即在某个时间段内,所有进程代码都会被执行
  • 大量的把进程从CPU上放下去,拿上来的动作——进程切换 

      说到进程的运行状态,很多同学可能第一时间想到的是此时进程应该在处理机上运行,这个时候的进程所处的状态就称为运行态

       其实并不是的,我们可以思考:在系统中一般只有一个处理机,而进程的数量又非常庞大,如果说,上处理机处理的运行的进程才称为运行态,那么进程在处理机上的运行切换又是非常快的,所以就会导致进程的状态不断发生改变,从而维护进程属性的PCB结构体中的进程相关状态信息的值会频繁发生变化,显然这是不合理的,也没有必要。

       因此,如果正确定义进程的运行态呢??

       进程在载入内存的时候,通常操作系统会为该程序创建一个PCB结构体来维护该进程中的相关信息,那么当这一切的准备工作全部准备就绪的时候,这个PCB结构体就会被放入CPU相应的运行队列中,那么此时我们就称该进程已经处于运行态

       总结来说,就是一个进程中的PCB结构体加入运行队列中随时准备上处理机运行的状态就称为运行态,此时进程在内存中。

如图是CPU中的运行队列的大致图,上述所有在运行队列中的进程都处于运行状态

1.2.就绪态

        当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。

这个状态的特点是

  • ① 一个进程已经具备运行条件,但没有分配CPU,暂时不能运行。
  • ②当调度给该进程CPU时,立即可以运行。

1.3.阻塞态

        这个可以类比进程的运行态,但是与进程的运行态不同的是,运行态等待的是CPU资源,而阻塞态是由于等待一些非CPU资源而无法运行的状态,同样也会维护相应的等待队列,这个等待队列因等待的资源不同而有所差异,比如常见等待的资源:磁盘,显卡,网卡,打印机,因此这些资源都会维护对应的等待队列,比如:磁盘等待队列,网卡等待队列,显卡等待队列等,此时进程在内存中。

这个状态的特点是:

  • ① 有时也叫作 “等待态、封锁态、睡眠态” 。
  • ② 当前进程因等待某事件的发生而暂时不能运行的状态。
  • ③ 即使这时CPU空闲,该进程也不能运行。

此过程会发生的事情:

  1.  当前进程的PCB会从runqueue中移除
  2. 当前进程的PCB会被加入到对应等待资源的等待队列中
  3. 对应进程的PCB中的进程的状态会被改变 

1.4.挂起态

        挂起态也属于一种等待状态,和阻塞态稍微有点类似,

        不过,挂起态的本质原因是操作系统中的内存资源不够,进程的数量太多,导致系统的压力非常大,因此此时操作系统为了释放压力,必须将一些暂时不需要运行的进程中的相关的代码和数据先置换到磁盘上,一般情况下,磁盘都会预先留出一部分空间:swap分区,此时进程不在内存中,而在磁盘上。

1.5.终止态

终止态是指一个进程在运行结束之后,不需要再上处理机运行的状态,对应的PCB加入到终止队列中,此时我们称这样的进程处于终止状态

        思考:为什么进程在结束运行不再需要上处理机运行时不直接被操作系统回收,而需要维护一个终止态呢?

        操作系统在回收相关进程的资源的时候不是一蹴而就的,而是需要花费一定的时间和开销,因此,在回收之前需要维护一个终止队列,对应PCB处于终止队列上的进程就是处于终止态此时进程在内存中。

2.Linux内核源代码里的运行状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。

一个进程可以有几个状态(在 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. R  ——运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列 里。
  2. S  ——睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
  3. D  ——磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
  4. T  ——停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
  5. X  ——死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

2.1. R(running):运行态

进程中的进程如果一直被运行,就会处于运行状态

我们来看看

我们编译运行,换另外一个账号来

我们发现不是r状态???

我们修改一下代码

我们编译运行

我们再用别的账号看看

都是R状态了

我们看到的现象是程序不断地输出,但是为啥是处于S状态呢??
        原因很简单,主要是因为我们在程序的源代码中调用printf函数,这个函数是向终端输出内容的,是一种向慢设备输出的一个过程,因此,因为我们知道处理机的速度是非常快的,慢设备的速度是非常慢的,所以,这个过程中大部分时间都是处于等待的过程,因此此时进程所属的状态是S状态

 S就对应我们的阻塞状态了

2.2. S(sleeping):阻塞状态——浅度睡眠

这种状态是一种阻塞状态,表示的是等待外设的就绪,是一种浅度睡眠状态,可以随时被中断

当然我们也可以采用kill -9 进程的pid 命令来杀掉该进程

2.3. D(disk sleeping):阻塞状态——深度睡眠

属于一种深度睡眠的状态,这个状态一旦发生,就不容易被中断,一般情况下只能通过关机或者断电才能够中断,其发生本质原因是等待磁盘资源的分配,磁盘的资源压力过大

系统里一旦出现一个D状态,就说明这个系统块崩溃了

2. 4. Z和X状态:死亡与僵尸

  • X状态:表示进程已经死亡,处于终止态
  • Z状态:表示进程处于僵尸状态,已经不用上处理机运行的状态

        我们需要知道,在Linux中,一个进程退出时不会马上进入X态,而是会进入Z状态,那么这是为什么呢?
        一个进程被创建就是为了完成某项任务,因此,当进程结束的时候,需要将进程的结束信息(是否完成任务等)告诉给父进程或者操作系统,也就是说:操作系统在一个进程退出后,这个进程不会马上处于X态,而是会为其维护一个称为僵尸态的状态,维护的目的是:就是维护进程的退出信息,以便将这个信息告知给操作系统或者父进程

如何模拟僵尸进程?
一般情况下,我们都会创建进程,这个进程会有相应的父进程,当子进程退出而父进程没有退出的时候,此时子进程所处的状态就是僵尸状态(Z状态)

我们来模拟一个30s的僵尸状态

#include <stdio.h>
#include <stdlib.h>
int main()
{
	pid_t id = fork();
	if (id < 0) {
		perror("fork");
		return 1;
	}
	else if (id > 0) { //parent
		printf("parent[%d] is sleeping...\n", getpid());
		sleep(30);
	}
	else {
		printf("child[%d] is begin Z...\n", getpid());
		sleep(5);
		exit(EXIT_SUCCESS);
	}
	return 0;	
}

此时可以看出,该进程处于僵尸状态

2.4.1.僵尸进程的危害

  1. 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎 么样了。可父进程如果一直不读取,那子进程就一直处于Z状态
  2. 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话 说,Z状态一直不退出,PCB一直都要维护
  3. 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构 对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空 间!
  4. 内存泄漏?是的!
  5. 如何避免?后面讲

因此,当出现僵尸进程的时候,我们必须合理地解决好僵尸进程的问题

2.5.孤儿进程

当一个进程的父进程提前退出的时候,子进程还在运行,此时子进程会被父进程的父进程领养,即会被bash(1号进程)领养

我们来模拟一下

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	pid_t id = fork();
	if (id < 0) {
		perror("fork");
		return 1;
	}
	else if (id == 0) {//child
		printf("我是子进程,我的pid是:%d,我的ppid是:%d\n", getpid(), getppid());
		sleep(10);
	}
	else {//parent
		printf("我是父进程,我的pid是:%d,我的ppid是:%d\n", getpid(), getppid());
		sleep(3);
		exit(0);
	}
	return 0;
			

}

 此时我们会发现,这个进程的状态相比于上述的进程来说是没有+的,这种进程状态没有+,是不能被Ctrl+C杀掉的,此时必须使用kill -9 进程的pid命令才能够杀掉进程

查看1号进程:使用top命令

从上图中我们可以看出,1号进程本质就是root,名为:systemd

2.5. T(Stopping):暂停状态

当进程被kill -19 pid 暂停的时候,此时进程会处于暂停状态

 想要将进程进行恢复的话可以采用kill -18 pid进行恢复

2.6. t(tracing stopping):追踪暂停状态

一般是在调试的过程中遇到断点的状态

相关推荐

  1. Linux进程状态

    2024-06-09 10:48:04       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 10:48:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 10:48:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 10:48:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 10:48:04       18 阅读

热门阅读

  1. SpringBoot集成ClickHouse,含集成kerberos认证

    2024-06-09 10:48:04       8 阅读
  2. Angular知识概览

    2024-06-09 10:48:04       9 阅读
  3. Mac电脑arm64芯片Cocoapods 的 ffi 兼容问题

    2024-06-09 10:48:04       6 阅读
  4. 0105__学习一个 Linux 命令:objcopy 命令

    2024-06-09 10:48:04       10 阅读
  5. 参观营业额变化增长(sql练习)

    2024-06-09 10:48:04       7 阅读
  6. g++ 预处理 编译 汇编 链接 命令

    2024-06-09 10:48:04       8 阅读
  7. Npm发布自己的插件包

    2024-06-09 10:48:04       7 阅读
  8. Linux基本指令查询硬件信息001

    2024-06-09 10:48:04       9 阅读
  9. 360数字安全:2024年3月勒索软件流行态势分析报告

    2024-06-09 10:48:04       8 阅读
  10. Redis中的发布/订阅模式:构建灵活的消息系统

    2024-06-09 10:48:04       8 阅读