Linux进程(二)

文章目录


进程(二)

前文我们知道了,传统的进程状态为:运行态、阻塞态、挂起,这是教科书上对于操作系统的进程状态描述,但是我们知道,不同的操作系统对于其自身的状态定义和描述是不同的,我们接下来看看Linux中的进程状态是分为哪几种?以及对于优先级的了解。

Linux的进程状态

我们先通过一段Linux的源码来了解一下,Linux有几种进程状态

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 */
};

R (running)运行态

运行态R,在前文已经很详细描述了,其进程的运行的流程,这里简单描述

将准备好运行的进程(实际上PCB即可)放在runqueue队列中,然后通过调度器来选取要将哪一个进程放在cpu上执行,当是时间片结束之后,就将该进程从cpu上拿下来,然后放在runqueue队列的队尾,再次排队,这就是依据时间片的轮转执行进程。

PCB排队,其对应的代码和数据,只有轮到该进程时候,才会调用。

PCB存在,进程就存在,控制一个进程就是控制其PCB

在Linux中R状态又分为两种表示形式,R和R+

区别:

R: 表示的是该进程在后台运行,此时我们只能通过kill -9 PID命令,或者是kill -19 PID来使得该进程结束或停止

R+:表示的是该进程在前台运行,此时我们在进程结束之前无法输入其他指令

选定执行方式:

./proc 默认前台运行proc进程

./proc & 默认后台运行proc进程

注意:如果程序运行需要I/O时,一般显示S+,表示阻塞状态,虽然先在进程是一直在运行,但是由于CPU处理数据太快,外设输入/输出太慢,导致进程大部分时间都是在等待,所以一般为S+

S (sleeping)阻塞状态

前文也对于阻塞状态做了详细的解释,我们这边也简单描述一下

当进程需要一定的外设输入/输出的时,也就是需要等待资源,这个时候进程并没有准备好被调用,会去找对应的外设,排队,实际上也是PCB排队,当得到资源之后,就会被唤醒,调到runqueue中,由阻塞态转换为运行态。

类型:

S+:表示的是前台,进程处于阻塞状态

S: 表示的是后台,进程处于阻塞状态

注意:此时的阻塞状态,为睡眠,是可以被唤醒的(等资源到位之后就被唤醒,转换成运行态)

D (disk sleep)深度睡眠

深度睡眠,此时进程阻塞,其对应的代码和数据是可以有可能被挂起的,而且处于当前状态是无法被唤醒,直到状态结束

D状态产生的原因:

责任判定:

进程:已执行自己的全部任务,且自己只是听从了操作系统的话,结束进程。

操作系统:为了大局着想,此时内存空间资源不足,得释放一些空间,以供其他进程使用,此进程此时比较空闲,我觉得是可以释放的,所以将其杀死,释放该进程,这是执行自己管理软硬件的能力,合理分配资源的职责。

硬盘:我也想反馈信息给进程,但是此时进程已经被杀死,我不知道这个资源到底是作何使用,我也有很多进程是要进行读写的,也需要空间来存储数据,对于该进程存储的数据,我只能按照规定,删除该数据,这是正常操作。

进程、操作系统、硬盘,都认为自己没有责任,而且陈词有理有据。所以应该不是某一部分的责任,应该是设计的问题。

如何解决该设计的问题呢?

我们要实现的目的是,只需要在进程等待磁盘写入完毕的期间,该进程不被任何人给杀掉即可

于是,我们加入的D(disk sleep)状态,磁盘睡眠,也就是深度睡眠状态,规定,处于该状态的进程不允许任何人来打扰,就算是操作系统也不行,在该状态无法被kill

总结:D状态表示进程正在等待某些资源的释放,通常是由于IO操作(如磁盘读写)过程中发生阻塞引起的。

D状态属于阻塞态,为深度睡眠,不可以被唤醒

S和D的区别:

  1. S一定是在等待某一种资源,等待唤醒成为运行态
  2. D可能是在等待某一种资源,也可能是正在被其他资源控制

T(stopped)状态

T状态,就是停止该进程,使得进程暂停运行,我们对于该状态有两种方法能实现

前台:

  1. 对于前台运行的程序,我们可以使用ctrl+z来使得进程停止运行,从而转换成T状态
  2. 对于前台运行的程序,我们可以使用kill -19 PID来使得进程停止运行,从而转换成T状态,通过kill -18 PID 来恢复运行

后台:

  1. 对于后台运行的程序我们只能使用kill -19 和 kill -18的方法

注意:T和t都是表示进程停止运行,没有太大的差别,所以我们就不讲t状态了

X(dead)状态

X状态,表示的是该进程被杀死,这是一个瞬时状态,我们不好观察到

Z(zombie)状态

Z状态就是僵尸状态,在进程被杀死之前,会先进入Z状态,由父进程得到该进程保留的信息之后,才会交给X状态,从而结束进程

使用kill -9 PID 命令杀死进程,就是先Z然后再X

将进程已经死掉,但是此时没有父进程来进行获取该进程的返回信息,此时操作系统要一直保留该状态,称为Z状态

总结:

  • 进程退出的时候,如果父进程没有主动回收子进程信息,子进程会一直让自己处于僵尸状态,就需要对于该进程的数据进行维护,进程基本信息存放在PCB中,所以要一直维护PCB进程的相关资源和代码数据信息,尤其的==task_struct结构体不能被释放==
  • 僵尸进程使得内存一直被占用,无法释放(如果父进程一直不回收),那么会导致==内存泄漏==
  • 僵尸状态的原因是,进程退出之前是需要告诉父进程,你交给我的任务我完成了没有,但是父进程一直没有获取子进程的结束信息,所以子进程要一直处于Z状态

那么就有人会讲,为什么./proc,结束后不会看到Z状态呢?

这是因为,被其父进程给接收信息,然后结束进程了,其父进程为bash,会主动在proc进程结束后回收信息,所以我们观察不到Z状态和X状态,然后proc进程就结束了

那么为什么bash不能处理proc的子进程呢?爷爷管孙子不行吗?

任何父进程只对于其子进程负责

僵尸进程

僵尸进程就是该程序运行之后,其子进程没有被父进程回收信息,使得子进程称为僵尸状态。

僵尸进程的代码为:

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

int main()
{

    printf("当前进程的PID为:%d , PPID为: %d\n",getpid(),getppid());
    pid_t id = fork();
    if(id==0)
    {
        //表示这是一个子进程
        int cnt=5;
        while(cnt--)
        {
            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;

}

僵尸进程的主要危害就是对于内存一直占用,且没有回收,导致内存泄漏。

因为需要维护该Z状态,就要维护其PCB(存储进程的基础信息),PCB不能被释放,PCB占据不少的空间。从而导致内存泄漏

孤儿进程

孤儿进程顾名思义就是父进程提前退出,而子进程还在运行,子进程为孤儿进程

父进程已经退出,被bash回收处理,子进程如果再想退出,怎么办?

父进程退出后,子进程会被1号进程(init/systemd)领养所以init/systemd来回收子进程

init和systemd是不同版本下1号进程的不同叫法

孤儿进程被操作系统所领养,1号进程init表示的是操作系统。

为什么不是bash领养呢?

因为任何进程之对于子进程进行回收,所以父进程由bash回收管理,所以孤儿进程不能被bash回收(父亲只管其自己儿子,不会管孙子),这是代码层面不允许,这个时候,操作系统根据其内核来回收

进程优先级

查看进程优先级的方法,ps -al 显示所有当前进程信息

优先级是指对于资源的访问,谁先访问,谁后访问,因为资源是优先的,进程是多个的,注定了,进程之间是竞争关系,所以操作系统必须保证大家良性竞争,确认优先级。

如果我们进程长时间得不到CPU资源,该进程的代码长时间无法得到推荐,就导致了该进程的饥饿问题。

所以优先级的存在,就是为了调度器能够更好的合理利用CPU资源,来公平调度不同的进程。

基本概念

cpu资源分配的先后顺序,就是指进程的优先级

优先级高的进程有优先执行的权利,配置进程优先级对于多任务环境的Linux很有用,可能改善系统性能

可以将进程运行到指定的CPU上,这样不重要的进程放在其他CPU上,可以大大改善系统性能

主要的信息

  1. UID:代表执行者的身份 (用户ID)
  2. PID:当前进程的进程编号
  3. PPID:当前进程的父进程的编号
  4. PRI:代表这个进程可被执行的优先级,其值越小越早被执行
  5. NI:代表当前进程的nice值

优先级PRI,越小优先级级别越高,NI即nice值,是用来修正优先级数值

PRI(new)=PRI(old)+nice

默认PRI(old)= 80

当nice数值变小,PRI会变小,优先级会变高,便会很快被执行,所以调整进程优先级,实际上就是调整nice数值。

nice的数值范围为-20到19,一共40个级别

注意:实际上进程什么时候被运行,都是根据调度器来控制的,调整优先级,到底什么时候调度不确定,所以只能说是可能会改善系统性能

更改优先级

有多种方式可以更改优先级,也就是说有多种方式能更改nice数值。

以下方式来更改进程优先级

  1. 对于进程使用nice 或者 renice指令进行更改nice数值,进而更改PRI数值。
  2. root用户下,在top(任务资源管理器)中可以按 " r " ,之后输入进程PID,然后输入nice数值

注意:

  1. root用户更改的nice范围为-20,19,非root用户的可更改的nice范围为0,19
  2. 非root用户只能将nice越调越高,root用户随意
  3. 非root用户,只能调自己进程的nice值,root用户随意

更改nice数值,如果输入大于或者小于[-20,19]这个范围,取极值

nice指令

用法:对于未执行的进程,在执行前输入nice -n + 指令(进程)

renice

语法:renice [number] PID

renice是对于已经运行的进程的nice数值进行更改,number表示输入新的nice数值

top更改nice

top可以显示当前的所有进程运行情况

只有root用户才有权利通过top来更改指定PID的nice数值

非root用户,top更改nice会有以下错误

优先级对于操作系统很重要,但是操作时候没有那么重要

操作系统是如何根据优先级,开展的调度呢?

基于Linux内核2.6的O(1)调度算法

主要是,我们通过优先级的加入对于runqueue的结构,再次了解调用进程的过程。

  1. 100,139 是基于时间片轮转,公平调度的进程,强调公平
  2. 0,99 是实时进程,强调及时响应,在当代操作系统中一般不会使用,但是某些需要实时进程的是需要的。
相关推荐
groundhappy1 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕2 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫3 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生3 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.115 小时前
linux基础IO
linux·运维·服务器
BelongPanda6 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯7 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_777998347 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器
yunwei377 小时前
eBPF 教程:检查 exec 后真正安装的可执行镜像
linux·开源
笑锝没心没肺8 小时前
yum update 报错:GPG key retrieval failed: [Errno 14] HTTPS Error 404 - Not Found
linux·运维·服务器