进程状态和进程优先级


一、进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态,一个进程可以有几个状态。下面的状态在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)

定义:并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

我们用以下代码来测试一个无限循环向显示器输出的程序:

#include<iostream>
#include<unistd.h>
using namespace std;

int main()
{
    while(1)
    {
        cout<<"running!"<<endl;
        
    }
    return 0;
}

这里我们看见R的后面有一个+号,这个+号代表它是一个前台进程。

在这里插入图片描述

2.睡眠状态S(Sleeping)

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

我们用以下这段代码来测试:

#include<iostream>
#include<unistd.h>
using namespace std;

int main()
{
    int a=0;
    while(1)
    {
        a++;
        cout<<"running!"<<endl;
    }
    return 0;
}

检测程序状态如下:

在这里插入图片描述

运行并查看进程,当前进程状态处于 S,叫做睡眠状态,睡眠状态也是阻塞状态的一种。因为 cout 要访问显示器,显示器是外设的一种,外设有个特点就是相较于 CPU 比较慢,慢就要等待显示器就绪,等待就要花较长的时间。我们这个程序可能只有万分之一的时间在运行,其它时间都在休眠,站在用户的角度它是 R,但是对于操作系统来说它不一定是 R,它有可能在队列中等待调度。

​ 所以并不是每次测试的时候打印状态都能打出 S,只不过这个概率很高,因为这个状态是瞬间的,有可能那个瞬间是在执行代码而不是在等 IO就绪 ,所以有时候测试得到的状态可能是 R 。

3.磁盘休眠状态 D(Disk sleep)

定义:也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

上面的 S 状态也叫 浅度睡眠,进程可以被杀掉,这里的 D 状态叫做深度睡眠,表示该进程不会被杀掉,即便是操作系统也不行,只有该进程自动醒来才可以恢复或者给机器断电。

假设场景:进程A需要向磁盘写入10万条用户数据,这些数据对用户很重要。进程A 访问磁盘,等待磁盘写入数据,进程A 等待磁盘返回一个结果,数据是否写入成功,此时进程A 处于休眠状态S;此时突然内存空间不足了,挂起也无法解决内存空间不足的问题,操作系统会自主杀掉一些进程(特别是内存资源不用的,比如进程A),操作系统就把进程A 给杀掉了,造成了磁盘写入数据失败,磁盘给进程A 返回结果,发现进程A 没有应答,磁盘只能把这些数据丢弃,然后磁盘继续为其他进程提供服务。结果,这重要的 10万条数据皆丢失了。

​ 为了防止这种情况的发生,Linux 给进程设置了深度睡眠 (D) 状态,处于深度睡眠状态的进程既不能被用户杀掉,也不能被操作系统杀掉,只能通过断电,或者等待进程自己醒来

深度睡眠状态一般很难见到,一般在企业中做高并发或高IO的时候会遇到。

4.停止状态 T(stopped)

定义:可以通过发送 SIGSTOP (signal stop)信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT (signal continue)信号让进程继续运行。

这里提到了信号,那么我们来介绍两个相关的信号(18号信号是继续,19号信号是暂停):

在这里插入图片描述

我们发送19号信号,发现进程状态变为了T,可以看出 T 状态也是阻塞状态的一种,但是有没有挂起完全不知道,这是由操作系统自己决定的!

在这里插入图片描述

再发送18号信号,发现状态变为了S,而非S+,所以它变成了后台进程:

前台进程:可以被 [Ctrl]+c 杀掉的进程,命令行在这个终端可以起作用,S+ 的 + 号就是前台进程的意思
后台进程:无法被 [Ctrl]+c 杀掉的进程,命令行在这个终端不起作用,这个时候没有 + 号

在这里插入图片描述

5.追踪停止状态 t(tracing stop)

定义:追踪暂停状态t 是也是一种暂停状态,不过它是一种特殊暂停状态,表示该进程正在被追踪。比如在使用 gdb 调试打断点的时候,程序暂停下来的状态就是 t状态,这里就不细谈了!

6.死亡状态 X(dead)

定义:这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也一瞬间就不存在了,所以你几乎不会在任务列表当中看到死亡状态。

7.僵尸状态 Z(zombie)

我们创建一个进程的目的是为了让其帮我们完成某种任务,而既然是完成任务,进程在结束前就应该返回任务执行的结果,供父进程或者操作系统读取;所以进程退出的时候,不能立即释放该进程的资源,该进程要保存一段时间,让父进程或操作系统读取该进程的执行结果(保存一段时间是对于CPU 而言)

定义:僵尸状态 Z 就是进程退出时,该进程的资源不能立即被释放,该进程要保留一段时间,等待父进程或操作系统读取该进程结果的过程就叫僵尸状态。

  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

用以下代码来测试僵尸进程:

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
using namespace std;

int main()
{
    pid_t pid = fork();
    if(pid==0)
    {

        cout<<"child process->"<<getpid()<<" ppid->"<<getppid()<<endl;
        sleep(2);
        exit(-1);

    }
    else
    {
        cout<<"father process->"<<getpid()<<" ppid->"<<getppid()<<endl;
        sleep(20);
    }

    return 0;
}

状态变化如下:

在这里插入图片描述

僵尸进程的危害:

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

8.孤儿进程

定义: 父进程先退出,子进程就称之为“孤儿进程”,孤儿进程会被1号 init进程(有些版本高的叫做systemd)领养,由 init进程回收,1号进程就是操作系统。

我们用以下代码来测试孤儿进程:

//孤儿进程
int main()
{
    pid_t pid = fork();
    if(pid==0)
    {
        while(1)
        {
            cout<<"child process->"<<getpid()<<" ppid->"<<getppid()<<endl;
            sleep(1);
        }
    }
    else
    {
        cout<<"father process exit!"<<endl;
        exit(-1);
    }
    return 0;
}

我们可以看到,子进程被1号进程领养,且进程状态由前台转为了后台

在这里插入图片描述

二、进程优先级

对于优先级的理解:

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能

在这里插入图片描述

其中这几个参数的含义如下:

UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值

如何判断一个进程的优先级呢?

  1. PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
  2. 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行

所以,调整进程优先级,在Linux下,就是调整进程nice值nice其取值范围是-20至19,一共40个级别。

我们可以用top 命令来修改优先级:

  • top
  • 进入top后按“r”–>输入进程PID–>输入nice值
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值