关键词搜索

源码搜索 ×
×

Linux 操作系统原理 — 进程管理 — 进程调度

发布2023-03-26浏览862次

详情内容

目录

进程调度

进程调度,即 Linux Kernel Scheduler 如何将多个 User Process 调度给 CPU 执行,从而实现多任务执行环境的公平竞争以及合理分配 CPU 资源。

在古早的单核环境中,Linux Scheduler 的主要目的是通过 “时间片轮转算法” 和 “优先级调度算法“ 来实现调度。而在现代多核环境中,Linux Scheduler 则需要考虑更多的复杂因素,如:CPU 负载均衡、Cache 亲和性、多核互斥等。所以本文主要讨论的是多核环境中的进程调度。

为了应对不同应用场景中的进程调度需求,Linux Kernel 实现了多种 Scheduler 类型,常见的有:

  1. CFS(Completely Fair Scheduler,完全公平调度器)
  2. RT(Real-time Scheduler,实时调度器)
  3. DS(Deadline Scheduler,最后期限调度器)

在这里插入图片描述

这些 Scheduler 会被作用于每个 CPU Cores 的 “就绪队列“ 中,且具有各自不同的调度算法和优先级策略。

在这里插入图片描述

在操作系统层面用户可以操作的只有用户进程实体,所以我们能够看见并使用的大多数调度配置都是针对 User Process 而言。

如下图,Kernel 将进程分为 2 大类型,对应各自的优先级区域以及不同的调度算法。

  1. 实时进程:具有实时性要求,共有 0~99 这 100 个优先级。
  2. 普通进程:没有实时性要求,共有 100~139 这 40 个级别。

在这里插入图片描述

但实际上,实时进程的优先级是初设后不可更改的。也就是说,从系统管理员的角度(Shell)只能配置普通进程的优先级。

针对普通进程的优先级配置,Linux 引入了 Nice level 的设计,Nice 值的范围是 -20~19 刚好对应到普通进程的 40 个优先级。其中,普通用户可以配置的范围是 0~19,而 Root 管理员则可以配置 -20~19。

这里写图片描述

CFS 完全公平调度器

Linux CFS(Completely Fair Scheduler,完全公平调度器)是 Kernel 默认使用的进程调度器,常用于通用计算场景。

CFS 的 “完全公平“ 并不是简单粗暴的按照 CPU 时间片大小来进行调度,而是会根据进程的运行时间来计算出优先级,运行时间较短的进程会拥有更高的优先级,从而保证了每个进程都能够获得公平的 CPU 时间。

具体而言,CFS 是一种基于红黑树的调度算法,它的目标是让所有进程都可以获得相同的 CPU 时间片。实现原理如下:

  1. CFS 在每个 CPU 上都有一棵红黑树,每个节点对应一个普通进程的 PCB(task_struct)和一个 Key。这个 Key 是进程的一个 VRT(虚拟运行时间),反应了进程在 CPU 上的运行时间。运行时间越长,VRT 就越大,优先级就越小。

  2. 当一个新的普通进程被创建时,它会被加入到红黑树中,并且初始的 VRT 值为 0,表示拥有最高调度优先级。

  3. 当 CPU 空闲时,就查询红黑树,并将 VRT 最小的就绪进程调度执行,完毕后增加其 VRT,降低其下次调度的优先级。

可见,CFS 的优点让每个进程都获得了公平的 CPU 时间。然而,CFS 的缺点是由于红黑树的操作复杂度较高,对于高并发的场景可能会影响系统的性能。

在这里插入图片描述

SCHED_NORMAL(普通进程调度算法)

SCHED_NORMAL 是 CFS 的基本实现,采用了上文中提到的 “时间片轮转“ 和 “动态优先级“ 调度机制。

  • 动态优先级:普通进程具有一个 nice 值来表示其优先级,nice 值越小,进程优先级越高。
  • 时间片轮转:如果有多个普通进程的优先级相同,则采用轮流执行的方式。

SCHED_BATCH(批量调度算法)

SCHED_BATCH 是一种针对 CPU 密集型批处理作业的调度算法。它的主要目的是在系统空闲时间运行一些需要大量 CPU 时间的后台任务。

区别于 SCHED_NORMAL,它并不使用时间片轮转和动态优先级调度机制,而是采用了一种基于进程组的批量处理策略。该算法会将所有的后台任务进程加入到一个进程组中,该进程组会共享一个可调度时间片。

在 SCHED_BATCH 中,进程组会被赋予更高的优先级,以确保后台任务能够在系统空闲时间得到足够的 CPU 时间。

RTS 实时调度器

Linux RTS(Real-Time Scheduler,实时调度器)采用固定优先级调度算法,优先级高的进程会获得更多的 CPU 时间。RTS 是 RT-Kernel 的默认调度算法,常用于对实时性要求高的计算场景。

在这里插入图片描述

RTS 的主要目的是保证实时任务的响应性和可预测性。固定优先级调度算法,总是可以让高优先级任务先运行,同时还实现了基于抢占的调度策略,以保证实时任务能够在预定的时间内运行完成。实现原理如下:

  1. RTS 优先级数值范围从 1(最高)~99(最低),其中 0 保留给 Kernel 使用。

  2. RTS 还实现了基于抢占的调度策略。当一个高优先级的任务到来时,它可以抢占当前正在运行的任务,并且直到运行完毕。

  3. RTS 使用了多队列的方法来管理实时进程。RTS 在每个 CPU 上维护 2 级就绪队列,一个是实时队列,一个是普通队列。并采用了不同的调度算法和优先级策略来进行调度。例如:实时进程采用 SCHED_FIFO 调度算法,普通进程采用 SCHED_RR。

  4. 调度器每次选择下一个要运行的进程时,会先从实时队列中选择进程,如果实时队列为空,则从普通队列中选择进程。这样可以保证实时进程的优先级高于普通进程,同时也避免了实时进程长时间等待的情况。

RTS 的优点是能够保证实时任务的响应性和可预测性,但缺点是对于普通任务来说可能会出现长时间等待的情况。

在这里插入图片描述

SCHED_FIFO(先到先服务调度算法)

SCHED_FIFO 调度算法会按照进程的提交顺序来分配 CPU 时间,当一个进程获得 CPU 时间后,它会一直运行直到完成或者被更高优先级的进程抢占。因此,该算法可能导致低优先级进程的饥饿情况,因为高优先级进程可能会一直占用 CPU 时间。

SCHED_RR(时间片轮转调度算法)

与 SCHED_FIFO 类似,SCHED_RR 调度算法也会按照进程的提交顺序来分配 CPU 时间。不同之处在于,每个进程都被赋予一个固定的时间片,当时间片用完后,该进程就会被放回就绪队列的尾部,等待下一次调度。该算法可以避免低优先级进程饥饿的问题,因为每个进程都能够获得一定数量的 CPU 时间,而且高优先级进程也不能一直占用 CPU 时间。

DS 最后期限调度器

Linux DS(Deadline Scheduling,最后期限调度器)是一种基于最后期限(Deadline)的调度器。实现原理如下:

  1. DS 与 CFS 类似的采用了红黑树,但主要区别在于 DS 的树节点 Key 是 Deadline 值,而不是 VRT。

  2. DS 为每个进程赋予一个 Deadline,DS 会按照进程的最后期限的顺序,安排进程的执行顺序。进程的最后期限越近,其优先级就越高。

  3. 当 CPU 空闲时,就查询红黑树,并将 Deadline 离与当前时间最近的就绪进程调度执行。

在这里插入图片描述

SCHED_DEADLINE(最后期限调度算法)

SCHED_DEADLINE 调度算法是 DS 调度器的默认调度算法,主要用于实时任务的调度。

进程调度策略的配置

ps 指令

我们在配置一个进程的调度策略之前,常常需要使用 ps 指令查看进程的状态信息。

查看进程资源使用信息

$ ps aux

USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.1  0.0  78088  9188 ?        Ss   04:26   0:03 /sbin/init maybe-ubiquity
...
stack      2152  0.1  0.8 304004 138160 ?       S    04:27   0:04 nova-apiuWSGI worker 1
stack      2153  0.1  0.8 304004 138212 ?       S    04:27   0:04 nova-apiuWSGI worker 2
...

    这里写图片描述

    查看指定进程的 CPU 资源详细使用信息

    $ pidstat -p 12285
    
    02:53:02 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
    02:53:02 PM     0     12285    0.00    0.00    0.00    0.00     5  python
    
    • 1
    • 2
    • 3
    • 4
    • PID:进程 ID。
    • %usr:进程在用户态运行所占 CPU 的时间比率。
    • %system:进程在内核态运行所占 CPU 的时间比率。
    • %CPU:进程运行所占 CPU 的时间比率。
    • CPU:进程在哪个核上运行。
    • Command:创建进程对应的命令。

    查看进程优先级信息

    $ ps -le
    
    F S   UID    PID   PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
    4 S     0      1      0  0  80   0 - 19522 ep_pol ?        00:00:03 systemd
    1 S     0      2      0  0  80   0 -     0 kthrea ?        00:00:00 kthreadd
    1 I     0      4      2  0  60 -20 -     0 worker ?        00:00:00 kworker/0:0H
    1 I     0      6      2  0  60 -20 -     0 rescue ?        00:00:00 mm_percpu_wq
    ...
    
      • UID:进程执行者 ID。
      • PID:进程 ID。
      • PPID:父进程 ID。
      • PRI:进程优先级,值越小优先级越高。
      • NI:进程的 nice 值。

      查看系统中所有的实时进程

      $ ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,policy,stat,wchan:14,comm |awk '$4 !~ /-/{print $0}'
      
        PID   TID CLS RTPRIO  NI PRI PSR %CPU POL STAT WCHAN          COMMAND
          7     7 FF      99   - 139   0  0.0 FF  S    smpboot_thread migration/0
         10    10 FF      99   - 139   0  0.0 FF  S    smpboot_thread watchdog/0
         11    11 FF      99   - 139   1  0.0 FF  S    smpboot_thread watchdog/1
         12    12 FF      99   - 139   1  0.0 FF  S    smpboot_thread migration/1
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      查看 nice 不为 0 的普通进程

      $ ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,policy,stat,wchan:14,comm|awk '$4 ~ /-/ &&$5 !~/0/ {print $0}'
      
         63    63 TS       -   5  14   2  0.0 TS  SN   ksm_scan_threa ksmd
         64    64 TS       -  19   0   2  0.0 TS  SN   khugepaged     khugepaged
      12995 12995 TS       -  -4  23   1  0.0 TS  S<sl ep_poll        auditd
      
      • 1
      • 2
      • 3
      • 4
      • 5

      查看进程运行状态及其内核函数名称

      $ ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,policy,stat,wchan:34,nwchan,pcpu,comm
      
        PID   TID CLS RTPRIO  NI PRI PSR %CPU POL STAT WCHAN                               WCHAN %CPU COMMAND
          1     1 TS       -   0  19   4  0.0 TS  Ssl  ep_poll                            ffffff  0.0 systemd
          2     2 TS       -   0  19   0  0.0 TS  S    kthreadd                            b1066  0.0 kthreadd
          3     3 TS       -   0  19   0  0.0 TS  S    smpboot_thread_fn                   b905d  0.0 ksoftirqd/0
      ...
         44    44 TS       -   0  19   7  0.0 TS  R    -                                       -  0.0 kworker/7:0
      
        • wchan:显示进程处于休眠状态的内核函数名称,如果进程正在运行则为 -,如果进程具有多线程且 ps 指令未显示,则为 *
        • nwchan:显示进程处于休眠状态的内核函数地址,正在运行的任务将在此列中显示短划线 -

        nice 指令

        nice 指令用于修改普通进程的 nice 值。

        设定即将启动的普通进程的 nice 值

        nice -n -5 service httpd start
        
        • 1

        修改已经存在的普通进程的 nice 值

        $ ps -le | grep nova-compute
        4 S  1000  9301     1  2  80   0 - 530107 ep_pol ?       00:02:50 nova-compute
        
        $ renice -10 9301
        9301 (process ID) old priority 0, new priority -10
        
        $ ps -le | grep nova-compute
        4 S  1000  9301     1  2  70 -10 - 530107 ep_pol ?       00:02:54 nova-compute
        

          chrt 指令

          chrt 指令可用于修改进程的调度算法和优先级。

          $ chrt --help
          Show or change the real-time scheduling attributes of a process.
          
          Set policy:
           chrt [options] <priority> <command> [<arg>...]
           chrt [options] --pid <priority> <pid>
          
          Get policy:
           chrt [options] -p <pid>
          
          Policy options:
           -b, --batch          set policy to SCHED_BATCH
           -d, --deadline       set policy to SCHED_DEADLINE
           -f, --fifo           set policy to SCHED_FIFO
           -i, --idle           set policy to SCHED_IDLE
           -o, --other          set policy to SCHED_OTHER
           -r, --rr             set policy to SCHED_RR (default)
          
            9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          修改进程的调度算法

          $ chrt -r 10 bash
          
          $ chrt -p $$
          pid 13360's current scheduling policy: SCHED_RR
          pid 13360's current scheduling priority: 10
          
          $ ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,policy,stat,wchan:14,comm |awk '$4 !~ /-/{print $0}'
          
            PID   TID CLS RTPRIO  NI PRI PSR %CPU POL STAT WCHAN          COMMAND
          13360 13360 RR      10   -  50   7  0.0 RR  S    do_wait        bash
          
            9
          • 10

          修改实时进程的优先级

          $ ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,policy,stat,wchan:14,comm |awk '$4 !~ /-/{print $0}'
          
            PID   TID CLS RTPRIO  NI PRI PSR %CPU POL STAT WCHAN          COMMAND
             27    27 FF      99   - 139   4  0.0 FF  S    smpboot_thread migration/4
          
          $ chrt -p 31
          pid 31's current scheduling policy: SCHED_FIFO
          pid 31's current scheduling priority: 99
          
          $ chrt -f -p 50 31
          
          $ chrt -p 31
          pid 31's current scheduling policy: SCHED_FIFO
          pid 31's current scheduling priority: 50
          
            9
          • 10
          • 11
          • 12
          • 13
          • 14

          相关技术文章

          点击QQ咨询
          开通会员
          返回顶部
          ×
          微信扫码支付
          微信扫码支付
          确定支付下载
          请使用微信描二维码支付
          ×

          提示信息

          ×

          选择支付方式

          • 微信支付
          • 支付宝付款
          确定支付下载