[Linux]进程状态

[Linux]进程状态

文章目录

进程状态的概念

了解进程状态前,首先要知道一个正在运行的进程不是无时无刻都在CPU上进行运算的,而是在操作系统的管理下,和其他正在运行的进程轮流循环使用CPU,而操作系统管理进程是否需要放到CPU上进行计算,所依据的就是进程状态。

阻塞状态

阻塞状态是进程因为等待某种资源就绪,而导致的一种不推进的状态。

阻塞状态从主观上给人的感觉就是进程"卡"住了,比如进程在下载某一个软件过程中,网络断了,进程下载软件的过程就会"卡"住,进程需要等待网络这种资源就绪,才能继续推进。因此进程处于阻塞状态时,一定是在等待某种资源。进程进入阻塞状态是想要通过等待的方式,等具体的资源被别人使用完后,给自身使用。

由于操作系统要管理各种各样的硬件,因此操作系统需要用某种结构的描述硬件,然后为了方便管理要将这些结构组织起来,描述这些硬件时,会有一个描述信息就是等待队列,这个队列会记录正在等待当前硬件资源的进程的PCB,操作系统就是通过这样的大致原理实现的让进程进入阻塞状态:

挂起状态

挂起状态是进程等待某种资源就绪时,操作系统系统将进程在内存中的代码和数据释放的状态。

挂起状态下进程的PCB还在硬件的等待队列上,操作系统认为进程等待的资源需要很长时间才会准备就绪时,就会释放进程在内存中的代码和数据,让多出来的内存空间用于做其他的事情,提高内存的利用率,当进程等待的资源准备就绪后,操作系统就会把进程的代码和数据再加载到内存中,将进程启动起来。

Linux下的进程状态

Linux系统下,在kernel源代码里定义了如下进程状态:

c 复制代码
/*
* 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 */
};

说明: Linux下的进程会在task_struct设置一个变量来记录进程状态。

R状态

R状态就是运行状态,说明进程的task_struct在操作系统中的运行状态等待队列之中,在操作系统轮循执行处于运行状态的进程的代码的过程中,被执行。

注意: 运行状态的进程不代表一直都在CPU上执行,而是和其他处于运行状态的进程一样,轮循的被执行。

为了验证R状态,编写如下代码:

c 复制代码
#include <stdio.h>

int main()
{
  while(1)
  {}
  return 0;
}

在Linux系统下运行程序并查看进程状态:

由于这段代码的执行不需要任何资源,不会进入阻塞状态,因此会一直保持运行状态。

S状态

S状态是可中断休眠状态,可中断休眠状态是Linux系统中阻塞状态的一种。

为了验证S状态,编写如下代码:

c 复制代码
#include <stdio.h>

int main()
{
  while(1)
  {
    printf("hello world\n");
  }
  return 0;
}

在Linux系统下运行程序并查看进程状态:

在这段代码中虽然程序是一个死循环操作,但是由于循环结构的代码运行很快,而将数据打印到屏幕上很慢,因此造成了程序运行时,大部分的时候都是在等待打印屏幕的资源,因此查询状态时大部分时间查询到的都是S状态。另外可中断休眠状态下的进程可以选择输入ctrl+z终止进程。

D状态

D状态是不可中断休眠状态,不可中断休眠状态是Linux系统中阻塞状态的一种。

D状态是在进程将数据从内存传输到磁盘时,磁盘的压力过大,导致传输数据的速度很慢,进程必须等待数据传输完成处于休眠状态,为了避免操作系统将这个处于休眠状态的进程杀死,因此将进程设置成不可中断休眠状态。D状态不是一种常见的状态,如果进程处于D状态一般说明计算机磁盘压力过大。

T状态

T状态是暂停状态,暂停状态下进程不再继续运行。

为了验证T状态,编写如下代码:

c 复制代码
#include <stdio.h>
#include <unistd.h>

int main()
{
  int i = 1;
  while(1)
  {
    printf("hello world %d\n", i);
    i++;
    sleep(1); //Linux系统提供的休眠函数,头文件是unistd.h
  }
  return 0;
}

在Linux系统下运行程序使用kill -19 进程id暂停进程并查看进程状态:

输入kill -18 进程id可以重启进程:

补充: Linux下进程状态后面的+的含义是这是一个前台进程:

进程状态中没有+的是后台进程:

在Linux下前台进程运行时,bash是失效的,而在后台进程运行时bash是可用的,另外后台进程可以使用kill -9 进程id来关闭。

t状态

t状态是追踪式暂停状态,是T状态的一种特例。

为了验证t状态,可以借用gdb工具,使用gdb开启调试一个程序:

Z状态

Z状态被称作僵尸状态,僵尸状态下进程已经完全停止了,但是保留了进程的task_struct和部分数据。进程进入僵尸状态是为了了解进程的运行结果是否出现问题,因为保留的数据中包含退出码等信息,而退出码是用于判断进程运行结果是否出现问题的数据。进程终止后都会进入僵尸状态等待父进程的回收处理。

为了验证T状态,编写如下代码:

c 复制代码
#include <stdio.h>
#include <unistd.h>

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

在Linux系统下运行程序使用kill -9 进程id杀死子进程并查看进程状态:

僵尸进程的危害: 由于僵尸状态下进程的task_struct和数据得到保留,因此会占用内存空间,如果创建多个子进程不回收就会造成大量的空间被占用,造成严重的内存泄露问题。

X状态

X状态被称作死亡状态,死亡状态下进程完成停止了,并且操作系统会很快将进程的task_struct和代码和数据回收释放。

孤儿进程

孤儿进程是在进程运行时,父进程停止,父进程转为操作系统(1号进程),被操作系统管理的进程。

为了验证孤儿进程,编写如下代码:

c 复制代码
#include <stdio.h>
#include <unistd.h>

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //子进程
    while(1)
    {
      printf("我是子进程,我的pid:%d, 我的ppid:%d\n", getpid(), getppid());
      sleep(1);
    }
  }
  else if (id > 0)
  {
    //父进程
    int cnt = 5;
    while(1)
    {
      printf("我是父进程,我的pid:%d, 我的ppid:%d\n", getpid(), getppid());
      sleep(1);
      if (--cnt == 0) break;
    }
  }
  return 0;
}

为了方便编译,编写如下makefile文件:

makefile 复制代码
myproc:myproc.c
		gcc -o $@ $^
.PHONY:clean
clean:
		rm -f myproc

准备好代码和makefile文件后编译得到程序,然后运行程序,并使用while :; do ps axj | head -1 && ps axj | grep myproc | grep -v grep; sleep 1; done指令来每隔一秒查询一次进程状态:

进程myproc中的父进程的父进程是bash,它停止后进入僵尸状态后,bash作为其父进程会进行回收操作,因此无法看到其僵尸状态,进程myproc中的子进程仍在进行,但是原有父进程停止了,需要有新的父进程来回收它,否则会造成其停止后,僵尸状态无法回收的情况。

变成孤儿进程后,就变成了后台进程,可以选择使用killall 进程名指令杀死同一进程名的所有进程。

相关推荐
Kusunoki_D2 分钟前
速查 Linux 常用指令 II
linux·运维·服务器
xmweisi0222 分钟前
Ansible内置模块之 group
linux·运维·ansible·rhce·rhca·红帽认证
小猪写代码29 分钟前
Ubuntu 系统默认已安装 python,此处只需添加一个超链接即可
linux·python·ubuntu
孤寂大仙v1 小时前
【Linux笔记】——Linux线程理解与分页存储的奥秘
linux·运维·笔记
有谁看见我的剑了?2 小时前
ubuntu 22.04 wifi网卡配置地址上网
linux·运维·ubuntu
iangyu2 小时前
【windows server脚本每天从网络盘复制到本地】
开发语言·windows·php
码农新猿类2 小时前
Ubuntu摄像头打开失败
linux·运维·ubuntu
jstart千语2 小时前
【消息队列】RabbitMQ基本认识
java·服务器·分布式·rabbitmq
PWRJOY2 小时前
Ubuntu磁盘空间分析:du命令及常用组合
linux·运维·ubuntu
ASDyushui3 小时前
Shell 编程之正则表达式与文本处理器
linux·正则表达式