郭卫东 发表于 2024-6-24 23:34:08

Linux:进程概念(三.详解进程:进程状态、优先级、进程切换与调度)

上次讲了进程这些内容:Linux:进程概念(二.检察进程、父进程与子进程、进程状态详解)


1.Linux中的进程状态

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): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
[*] S就寝状态(sleeping): 意味着进程在等待变乱完成(这里的就寝偶然候也叫做可制止就寝(interruptible sleep))
[*] D磁盘休眠状态(Disk sleep)偶然候也叫不可制止就寝状态(uninterruptible sleep),在这个状态的进程通常会等待IO的竣事。
[*] T制止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来制止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继承运行。
[*] X殒命状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
[*] Z僵尸状态(zombie):下面详讲它
1.1前台进程和后台进程

在Linux中,可以将进程分为前台进程和后台进程,它们的区别在于与终端的交互方式和实行状态。

[*] 前台进程:会有+

[*]前台进程是当前正在与用户交互的进程,它会占用终端的输入和输出。
[*]当用户在终端启动一个程序时,该程序通常成为前台进程,用户可以看到程序的输出,而且可以与程序进行交互。
[*]前台进程会阻塞终端,直到该进程实行完毕大概暂停。
[*]用户可以通过按下Ctrl + C来制止前台进程的实行。

[*] 后台进程:没有+

[*]后台进程是在后台实行的进程,不会占用终端的输入和输出。
[*]用户可以在下令行中在下令后面加上"&"符号,将进程放入后台实行。
[*]后台进程不会阻塞终端,用户可以继承输入其他下令。
[*]用户可以利用下令bg将一个前台进程转为后台进程,大概利用下令jobs检察当前所有的作业(包罗前台和后台)。
[*]利用kill 进程ID下令关闭对应的后台进程,好比kill 1234

运行状态

   运行状态(Running)是进程可以被调度实行的状态。当一个进程处于运行状态时,它的代码正在被 CPU 实行,即正在运行指令并处理各种任务。在 Linux 中,通常用 R 表示进程处于运行状态。
int main()
{
        while (1)
        {
                ;
        }
        return 0;
}
https://img-blog.csdnimg.cn/direct/f4d094babbb146dd88844be9748d38a0.png
就寝状态

   就是我们上次学习的阻塞状态,在Linux中,进程的就寝状态(Sleeping)是指进程因等待某些变乱而临时制止实行。这个状态偶然也被称为可制止就寝(Interruptible Sleep),因为进程在这种状态下可以被制止,例如通过吸收信号来唤醒。
就寝状态的进程通常在等待某些变乱的完成,例如:


[*]等待某个I/O操作的完成,好比从磁盘读取数据。
[*]等待某个信号的到达,例如等待用户输入或其他进程发送的信号。
[*]等待某个条件的满意,好比等待某个锁的释放或某个共享资源的可用性。
必要留意的是,就寝状态的进程是可以被制止的,也就是说,在等待变乱的过程中,假如吸收到一个信号,进程大概会被唤醒并处理该信号,之后大概会继承等待或实行其他操作。我们利用Ctrl+c可以制止进程,因此,这种状态也称为可制止就寝。
#include<stdio.h>
#include<unistd.h>
int main()
{
    int a=0;
    while(1)
    {
      printf("%d",a);
      sleep(2);
    }
    return 0;
}
https://img-blog.csdnimg.cn/direct/90c1082e83ce470a86e6b8775643a6d0.png
   在这个程序中,主循环是一个无穷循环 while(1),它不会自动放弃 CPU,因此进程会一直处于运行状态(R)。但是,在每次循环迭代中,程序会调用 printf 函数打印 a 的值,并利用 sleep(2) 函数让进程休眠 2 秒。
在 sleep(2) 调用期间,进程临时制止实行,等待指定的时间竣事后再继承实行。虽然进程在休眠期间处于不活动状态,但是它并没有自动释放 CPU。因此,进程会被标记为就寝状态(S),表示它正在等待特定变乱的完成,即等待 sleep 函数定时器计时竣事。
但是假如我们把sleep()去掉后,会发现还是S状态
   在如许的程序中,主循环是一个无穷循环 while(1),它不会自动放弃 CPU,因此进程会一直处于运行状态(R)。但是,由于 printf 函数涉及输出操作,这大概会导致进程在等待标准输出设备的 I/O 操作完成时陷入就寝状态(S)。
当程序运行时,printf 函数将数据输出到标准输出设备(通常是终端),而且在数据传输过程中,大概必要等待设备的响应。在这段等待期间,进程临时制止实行,处于就寝状态。因此,即使主循环一直在运行,但是由于进程在某些时刻必要等待设备响应,因此会被标记为就寝状态(S)。(CPU实行是很快的)
https://img-blog.csdnimg.cn/direct/d98b0677e78146b2b07b2b3f764c9d3d.png
磁盘休眠状态

也是阻塞状态。D磁盘休眠状态(Disk sleep)是Linux体系中的一种进程状态,偶然也称为不可制止就寝状态(uninterruptible sleep)。进程进入这种状态通常是因为正在等待某些IO操作的完成,好比磁盘读写操作,网络哀求等。在D状态下的进程是无法被制止大概唤醒的,直到IO操作完成为止。

[*]进程状态:

[*]当一个进程被阻塞在等待IO操作完成的环境下,它会被标记为D状态。这种状态下的进程无法响应信号,也无法被制止。

[*]缘故因由:

[*]进程进入D状态通常是因为正在等待硬件设备的响应。好比,一个进程正在等待硬盘读取数据,但是硬盘响应较慢,导致进程无法继承实行。

[*]办理方法:

[*]通常环境下,D状态的进程会在IO操作完成后自动恢复,进程会从D状态转为可运行状态。假如进程长时间处于D状态,大概必要检查硬件设备是否正常,大概实验重新启动体系。

   也可以理解磁盘休眠状态的进程是有免死金牌的,能防止CPU因为资源不敷而删除这个正在等待的进程
因此,即使体系资源告急或CPU负载高,磁盘休眠状态下的进程仍然会被体系保存,不会被强制删除。这种机制确保了IO操作的完整性和体系的稳定性。
制止状态

   在Linux体系中,当一个进程吸收到SIGSTOP信号时,它会被暂停(制止)实行,进入制止状态。在这种状态下,进程的实行被临时挂起,不会继承实行,也不会被调度到CPU上运行。
制止状态下的进程不会斲丧CPU资源,也不会响应任何信号,直到吸收到SIGCONT信号后才会继承实行。制止状态的进程可以通过ps下令大概类似的工具检察,通常会表现为T状态。
要将一个进程从制止状态恢复到运行状态,可以向该进程发送SIGCONT信号。如许进程就会从制止状态恢复到运行状态,继承实行
kill指令—向进程发送信号

在Linux体系中,kill指令用于向进程发送信号。通过kill指令,可以向指定的进程发送差别的信号,从而影响进程的行为。常用的kill指令格式如下:
kill <PID>
此中,<PID>是要发送信号的进程的进程ID(Process ID)。可以利用ps指令大概pgrep指令来查找进程的进程ID。


[*] -9:发送SIGKILL信号,强制制止进程。
[*] -15(或不加选项):发送SIGTERM信号,哀求进程正常制止。
[*] kill -l是用来列出体系支持的信号列表的下令
[*]-19 SIGSTOP(编号为19):发送SIGSTOP信号会使进程制止实行,进程将被挂起,直到吸收到SIGCONT信号继承实行。SIGSTOP信号不能被捕获、忽略或阻塞,是一种强制制止进程的信号。
[*]-18 SIGCONT(编号为18):发送SIGCONT信号会使之前被制止的进程继承实行。这个信号用于恢复被SIGSTOP大概类似信号暂停的进程的实行。
   https://img-blog.csdnimg.cn/direct/79ffb37e17514d70a0f339d6e9c9d3ae.png
https://img-blog.csdnimg.cn/direct/8b63bf25ccdd4ddc983d8a23861076b1.png
殒命状态

对应的就是我们之前讲解的制止状态
   在Linux体系中,"殒命状态(dead)"通常指的是进程已经制止(terminated)而且退出,但其进程形貌符(process descriptor)还未被释放。这种状态通常在进程制止后,其父进程还未对其进行处理或采取资源时出现。
当一个进程制止后,其进程形貌符会保存一段时间,直到父进程调用wait()或waitpid()等体系调用来采取子进程的资源。在这段时间内,进程的状态会被标记为"殒命状态(dead)"。一旦父进程采取了子进程的资源,进程形貌符就会被释放,进程完全被打扫。
因为是一个瞬间的动作,我们很难看到该状态
2.僵尸进程

2.1僵尸状态

   在Linux体系中,当一个进程制止后,其进程形貌符==(PCB并不会立刻被释放)。此时,该进程会变成一个僵尸进程(Zombie Process)。僵尸进程是指一个进程已经制止==,但其父进程还未对其进行处理或采取资源。
在Linux体系中,当父进程读取了子进程的退出状态后,子进程的状态会从僵尸状态(Zombie)变为制止状态(Terminated),通常用X表示。这意味着父进程已经处理了子进程的退出状态信息,而且子进程的资源已经被释放,不再占用体系资源。因此,及时处理子进程的退出状态好坏常重要的,可以避免僵尸进程的积累,进步体系的稳定性和性能。
而bash会自动读取子进程的退出状态
2.2僵尸进程

   

[*] 僵死状态(Zombies)是一个比力特别的状态。当进程退出而且父进程(利用wait()体系调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
[*] 僵死进程会以制止状态保持在进程表中,而且会一直在等待父进程读取退出状态代码。
[*] 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
2.3僵尸进程危害


[*]进程的退出状态对于父进程来说很重要,因为它告诉父进程子进程实行任务的效果。假如父进程不读取子进程的退出状态,子进程就会一直处于僵尸(Zombie)状态。
[*]退出状态是必要被维护的数据,保存在进程控制块(task_struct或PCB)中。因此,即使进程处于僵尸状态,其退出状态信息也必要被维护(PCB不会被释放,进程控制块(task_struct或PCB)中保存了进程的重要信息)。
[*]假如一个父进程创建了很多子进程但不采取它们,会导致内存资源的浪费。因为每个进程都有自己的进程控制块,不采取子进程会导致内存走漏。
3.孤儿进程

   孤儿进程是指父进程先于子进程竣事而竣事,导致子进程成为孤儿进程。在Linux体系中,孤儿进程会被init进程(进程ID为1的进程)接管。当父进程先于子进程竣事时,子进程的父进程ID会被修改为1,即init进程的进程ID,如许子进程就成为了孤儿进程。

[*]孤儿进程的父进程ID会被修改为1,即init进程的进程ID。
[*]孤儿进程会被init进程接管,init进程会负责采取孤儿进程的资源。
[*]孤儿进程的父进程竣事后,其父进程ID会被修改为1,但其依然可以正常运行,直到自己竣事或被init进程接管。
[*]这种领养机制保证了即使父进程制止,子进程仍然能够正常运行并被体系管理
孤儿进程的产生通常发生在父进程没有等待子进程竣事就提前竣事的环境下。为了避免产生孤儿进程,父进程在创建子进程后应该等待子进程竣事,并及时处理子进程的制止状态。如允许以确保子进程在父进程竣事时能够正常退出,而不会成为孤儿进程。
4.进程的优先级

概念

   

[*] cpu资源分配的先后顺序,就是指进程的优先权(priority)。
[*] 优先权高的进程有优先实行权利。配置进程优先权对多任务环境的linux很有效,可以改善体系性能。
[*] 还可以把进程运行到指定的CPU上,如许一来,把不重要的进程安排到某个CPU,可以大大改善体系团体性能。
[*] 体系通常会根据进程的实际需求和体系负载来动态调整优先级。较高优先级的进程将更频繁地获得CPU时间片,从而更快地实行,而较低优先级的进程大概会等待更长的时间才华获得CPU实行时间。

[*]Linux中优先级默认是80
[*]Linux优先级是可以被修改的,Linux的优先级的范围 ,
[*]数字越小,优先级越高
task_struct
{
        //...
        int PRI;
        int nice;
        //...
};
检察进程优先级

ps -al
   

[*] -a 选项:表现所有进程,包罗其他用户的进程。默认环境下,ps 下令只表现当前用户的进程,利用 -a 选项可以表现所有效户的进程。
[*] -l 选项:以长格式表现进程信息。长格式包罗更多的字段,如进程状态、进程 ID、父进程 ID、优先级、CPU 利用环境、内存利用环境等。
https://img-blog.csdnimg.cn/direct/2a8dfd6ff3d94671a5c4e997a4648f0d.png
   

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

   

[*] PRI也还是比力好理解的,即进程的优先级,大概通俗点说就是程序被CPU实行的先后顺序,此值越小进程的优先级别越高
[*] 那NI就是我们所要说的nice值了,其表示进程可被实行的优先级的修正数值

[*]nice 值简直是影响进程优先级的修正因子,通过调整 nice 值,可以间接地影响进程的优先级,从而影响其在CPU上的实行顺序。

[*] PRI值越小越快被实行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
[*] 如许,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被实行
[*] 所以,调整进程优先级,在Linux下,就是调整进程nice值
[*] nice其取值范围是-20至19,一共40个级别
为什么要有-20到19的这个限制?
   
[*]假如没有这个限制,那么就有人会把自己利用的优先级调的特别特别高,把别人的进程调低,如许显然不好
[*]进程饥饿题目是指在多任务体系中,优先级较高的进程持续占用CPU资源,导致优先级较低的进程无法及时获得CPU时间片,从而长时间处于等待状态,无法实行。这种环境下,低优先级的进程大概会长时间等待CPU资源,无法完成其任务,造成资源浪费和体系性能下降
更改nice来间接改变优先级

   top:进入top后按“r”–>输入进程PID–>输入nice值
利用 top 下令可以实时监视体系的运行环境,包罗进程的资源占用环境、进程列表等。按下 r 键后,可以对指定进程的优先级进行调整。
在按下 r 键后,按照提示输入要调整优先级的进程的PID,然后输入要为其设置的新的 nice 值。输入完毕后,top 将会实验修改指定进程的优先级,根据新的 nice 值重新盘算其优先级。如允许以实现对指定进程实行优先级的调整。
5.进程其他重要概念


[*] 竞争性:指体系中进程的数量多于可用的CPU资源。由于资源有限,进程之间会竞争CPU、内存、IO等资源。为了高效地完成任务,进程之间必要根据优先级进行竞争,以便合理地分配资源并进步体系性能。
[*] 独立性:多进程运行时,每个进程都有自己的内存空间和实行环境,相互之间相互独立,互不干扰。这意味着一个进程的错误或异常不会直接影响其他进程的正常运行,进步了体系的稳定性和可靠性。
[*] 并行:多个进程在多个CPU上同时实行(我们一样平常遇不到),每个CPU负责处理一个或多个进程。如允许以加快任务的实行速度,进步体系的吞吐量和性能。
[*] 并发:多个进程在单个CPU上交替实行,通过进程切换的方式,让多个进程在一段时间内都得以推进。虽然在恣意时刻只有一个进程在CPU上实行,但由于进程切换的快速性,给人的感觉就像是多个进程在同时实行一样。并发可以进步体系的响应速度和资源利用率。
   当代操作体系采取时间片轮转的方式来调度进程实行,而不是等待一个进程的代码完全实行完毕后再切换到下一个进程。这种方式能够实现多任务的并发实行,进步体系的响应速度和资源利用率。

[*]时间片(Time Slice): 时间片是操作体系分配给每个进程的实行时间段。一旦一个进程的时间片用完,操作体系就会将CPU的控制权交给另一个就绪状态的进程,从而实现进程的切换和并发实行。
[*]轮转调度(Round-Robin Scheduling): 时间片轮转调度算法是一种简单而高效的调度算法,它会将就绪队列中的进程按照先来先服务的原则列队,并为每个进程分配一个固定长度的时间片。当一个进程的时间片用完后,操作体系会将其移到队列的末尾,然后继承实行下一个就绪进程。
6.进程的切换与调度

进程的切换与调度是操作体系中非常重要的部分,它涉及到怎样有效地利用CPU资源,保证体系的响应速度和吞吐量。
进程切换

https://img-blog.csdnimg.cn/direct/399b56ecfa5f40a3a3940d059b904b99.png
进程切换指的是从一个正在实行的进程切换到另一个进程的过程。当操作体系决定将CPU的控制权从当进步程转移到另一个进程时,就必要进行进程切换。进程切换包罗以下几个关键步骤:

[*] 上下文保存: 当操作体系决定要切换到另一个进程时,首先必要保存当进步程的上下文信息,包罗程序计数器、寄存器内容、栈指针等。这些信息存储在进程的控制块(PCB)中。
[*] 选择新进程: 在确定要切换到哪个新进程之前,操作体系会根据调度算法从就绪队列中选择一个合适的进程。这个选择大概基于进程的优先级、先到先服务(FIFO)、轮转法等。
[*] 加载新进程的上下文: 一旦确定了新进程,操作体系就会从其对应的PCB中恢复该进程的上下文信息。这包罗将新进程的程序计数器值加载到CPU中,以便实行新进程的代码。
进程调度

https://img-blog.csdnimg.cn/direct/0ffa4f24fb8542bfb34987218e58f4d3.png
进程调度是操作体系根据一定的调度计谋从就绪队列中选择下一个要实行的进程的过程。调度计谋的选择会影响体系的性能、响应速度和资源利用率。

[*] 进程队列数组 queue:这个数组用于存储差别优先级的进程队列。每个队列按照先进先出(FIFO)规则进行列队调度。数组的下标表示进程的优先级,因此可以直接根据优先级来访问对应的进程队列,进步了访问效率。
[*] 进程队列状态位图 bitmap:为了快速判定哪些队列好坏空的,利用了一个位图来表示每个队列的状态。每个比特位对应一个队列,假如该队列非空,则对应的比特位为1;否则为0。如许,查找非空队列的操作变得高效,时间复杂度为常数级别。
[*] active指针和expired指针:这两个指针用于指示当前生动队列和过期队列。随着调度的进行,它们的内容可以互换,从而实现生动队列和过期队列的动态切换。
[*] 生动队列和过期队列:生动队列中包罗当前生动的进程,而过期队列包罗一段时间内未被调度的进程。Linux 内核根据必要从生动队列和过期队列中选择进程进行调度,以平衡优先级和资源利用效率。
   活动队列


[*] 时间片还没有竣事的所有进程都按照优先级放在该队列
[*] nr_active: 统共有多少个运行状态的进程
[*] queue: 一个元素就是一个进程队列,雷同优先级的进程按照FIFO规则进行列队调度,所以,数组下标就是优先级!
[*] bitmap:一共140个优先级,一共140个进程队列,为了进步查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,如许,便可以大大进步查找效率
[*] 从该结构中,选择一个最合适的进程,过程是怎么样呢?

[*]从0下表开始遍历queue
[*]找到第一个非空队列,该队列必定为优先级最高的队列
[*]拿到选中队列的第一个进程,开始运行,调度完成

    过期队列


[*]过期队列和活动队列结构千篇一律
[*]过期队列上放置的进程,都是时间片耗尽的进程、
[*]当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新盘算

[*]O(1) 调度算法:Linux 内核的调度器通常采取 O(1) 调度算法(利用了位图(bitmap)来实现),该算法在常数时间内选择下一个要实行的进程,而不受进程数量的影响。这确保了调度器的高效性,使得体系在任何负载环境下都能快速响应。
好啦,进程干系的知识,也大概梳理完毕了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Linux:进程概念(三.详解进程:进程状态、优先级、进程切换与调度)