浅析Linux下进程的调度策略与优先级

线程调度

在 Linux 中,线程是由进程来实现的,可以认为线程就是一个轻量级的进程,因此,线程调度是按照进程调度的方式来进行的。这样设计,线程调度流程可以直接复用进程调度流程,没必要再设计一个进程内的线程调度器了。

调度策略与调度优先级

在 Linux 中,进程调度器是基于进程的调度策略与调度优先级来决定调度哪个进程运行。

调度策略主要包括:

  • SCHED_OTHER
  • SCHED_IDLE
  • SCHED_BACH
  • SCHED_FIFO
  • SCHED_RR

调度优先级的范围是 0~99,数值越大,表示优先级越高。

其中,SCHED_OTHER、SCHED_IDLE、SCHED_BACH 为非实时调度策略,其调度优先级为 0。而 SCHED_FIFO、SCHED_RR 是实时调度策略,其调度优先级范围为 1~99。

实时调度策略的进程总是比非实时调度策略的进程优先级高。

在 Linux 内部实现中,调度器会为每个可能的调度优先级维护一个可运行的进程列表,以最高优先级列表头部的进程作为下一次调度的进程,所有的调度都是抢占式的,如果一个具有更高调度优先级的进程转换为可运行状态,那么当前运行的进程将被强制进入其等待的队列中。

常见的调度策略

SCHED_OTHER

该调度策略是默认的 Linux 分时调度策略,该调度策略为非实时的,其调度优先级总是为 0。

对于该调度策略类型的进程,调度器是基于动态优先级来调度的。动态优先级跟属性 nice 有关,nice 的值会随着进程的运行时间而动态改变,以确保所有具有 SCHED_OTHER 策略的进程公平地得到调度。

在 Linux 中,nice 的值范围为-20 ~ +19,默认值为 0。nice 值越大,则优先级越低,因此相对较低 nice 值的进程可以获得更多的处理器时间。

通过命令 ps -el 查看系统中的进程列表,其中 NI 列就是进程对应的 nice 值。

[root@vm_rp0_cpu1_docker ~]# ps -el
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0     1     0  0  80   0 - 13653 ep_pol ?        00:00:40 systemd
1 S     0     2     0  0  80   0 -     0 kthrea ?        00:00:00 kthreadd
1 S     0     3     2  0  80   0 -     0 smpboo ?        00:00:05 ksoftirqd/0
1 S     0     7     2  0 -40   - -     0 smpboo ?        00:00:00 migration/0
1 S     0     8     2  0  80   0 -     0 rcu_gp ?        00:00:00 rcu_bh
1 S     0     9     2  0  80   0 -     0 rcu_gp ?        00:00:21 rcu_sched
5 S     0    10     2  0  60 -20 -     0 rescue ?        00:00:00 lru-add-drain
5 S     0    11     2  0 -40   - -     0 smpboo ?        00:00:01 watchdog/0
5 S     0    13     2  0  80   0 -     0 devtmp ?        00:00:00 kdevtmpfs
5 S     0    14     2  0  60 -20 -     0 rescue ?        00:00:00 netns

使用 top 命令,看到的 NI 列也是进程的 nice 值。

[root@vm_rp0_cpu1_docker ~]# top
top - 14:23:38 up 8 days,  5:44,  5 users,  load average: 3.73, 2.67, 2.00
Tasks: 136 total,   3 running, 132 sleeping,   1 stopped,   0 zombie
%Cpu(s): 29.4 us, 29.4 sy,  0.0 ni, 41.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st, 38.0 id_exact,  1.2 hi_exact,  1.7 irq_exac

KiB Mem :   991516 total,   434932 free,   408604 used,   147980 buff/cache
KiB Swap:   524284 total,   158632 free,   365652 used.   461432 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
22067 root      20   0    7312     28      0 R 25.0  0.0  70:38.00 stress
22068 root      20   0  621716 215216     28 R 25.0 21.7  70:34.70 stress
    1 root      20   0   54612   3452   2108 S  0.0  0.3   0:40.82 systemd
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.17 kthreadd
    3 root      20   0       0      0      0 S  0.0  0.0   0:05.92 ksoftirqd/0
    7 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0
    8 root      20   0       0      0      0 S  0.0  0.0   0:00.00 rcu_bh
    9 root      20   0       0      0      0 S  0.0  0.0   0:21.27 rcu_sched
   10 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 lru-add-drain
   11 root      rt   0       0      0      0 S  0.0  0.0   0:01.39 watchdog/0
   13 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kdevtmpfs
   14 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 netns
   15 root      20   0       0      0      0 S  0.0  0.0   0:00.14 khungtaskd
   16 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 writeback
   17 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kintegrityd
   18 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 bioset
   19 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kblockd

调整 nice 值,可以通过 shell 命令 nice,该命令可以按照指定的 nice 值运行 cmd,命令的帮助信息为:

[root@vm_rp0_cpu1_docker ~]# nice --help
Usage: nice [OPTION] [COMMAND [ARG]...]
Run COMMAND with an adjusted niceness, which affects process scheduling.
With no COMMAND, print the current niceness.  Niceness values range from
-20 (most favorable to the process) to 19 (least favorable to the process).

Mandatory arguments to long options are mandatory for short options too.
  -n, --adjustment=N   add integer N to the niceness (default 10)
      --help     display this help and exit
      --version  output version information and exit

NOTE: your shell may have its own version of nice, which usually supersedes
the version described here.  Please refer to your shell's documentation
for details about the options it supports.

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
For complete documentation, run: info coreutils 'nice invocation'

重新调整已运行进程的 nice 值,可通过 renice 命令实现,命令的帮助信息为:

[root@vm_rp0_cpu1_docker ~]# renice --help

Usage:
 renice [-n] <priority> [-p|--pid] <pid>...
 renice [-n] <priority>  -g|--pgrp <pgid>...
 renice [-n] <priority>  -u|--user <user>...

Options:
 -g, --pgrp <id>        interpret argument as process group ID
 -n, --priority <num>   specify the nice increment value
 -p, --pid <id>         interpret argument as process ID (default)
 -u, --user <name|id>   interpret argument as username or user ID
 -h, --help             display help text and exit
 -V, --version          display version information and exit

For more information see renice(1).

另外,可以执行 top 命令,输入 r,根据提示输入进程的 pid,再输入 nice 数值,也可以调整进程的 nice 值。

SCHED_FIFO

该调度策略为先入先出调度策略,简单概括,就是一旦进程占用了 CPU,则一直运行,直到有更高优先级的任务抢占,或者进程自己放弃占用 CPU。

SCHED_RR

该调度策略为时间片轮转调度策略,该调度策略是基于 SCHED_FIFO 策略的演进,其在每个进程上增加一个时间片限制,当时间片使用完成后,调度器将该进程置于队列的尾端,放在尾端保证了所有具有相同调度优先级的进程的调度公平。

使用 top 命令,如果 PR 列的值为 RT,则说明该进程采用的是实时调度策略,其调度策略为 SCHED_FIFO 或者 SCHED_RR,而对于非实时调度策略的进程,该列的值为 NI + 20

可以通过命令 ps -eo state,uid,pid,ppid,rtprio,time,comm 来查看进程对应的实时优先级,实时优先级位于 RTPRIO 列下,如果进程对应的列显示为 -,说明该进程不是实时进程。

[root@vm_rp0_cpu1_docker ~]# ps -eo state,uid,pid,ppid,rtprio,time,comm
S   UID   PID  PPID RTPRIO     TIME COMMAND
S     0     1     0      - 00:00:40 systemd
S     0     2     0      - 00:00:00 kthreadd
S     0     3     2      - 00:00:05 ksoftirqd/0
S     0     7     2     99 00:00:00 migration/0
S     0     8     2      - 00:00:00 rcu_bh
S     0     9     2      - 00:00:21 rcu_sched
S     0    10     2      - 00:00:00 lru-add-drain
S     0    11     2     99 00:00:01 watchdog/0
S     0    13     2      - 00:00:00 kdevtmpfs
S     0    14     2      - 00:00:00 netns
S     0    15     2      - 00:00:00 khungtaskd
S     0    16     2      - 00:00:00 writeback

chrt 命令

chrt 命令可以用来很简单地更改进程的调度策略与调度优先级。在 Linux 下查看 chrt 命令的帮助信息:

[root@vm_rp0_cpu1_docker ~]# 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)

Scheduling options:
 -R, --reset-on-fork       set SCHED_RESET_ON_FORK for FIFO or RR
 -T, --sched-runtime <ns>  runtime parameter for DEADLINE
 -P, --sched-period <ns>   period parameter for DEADLINE
 -D, --sched-deadline <ns> deadline parameter for DEADLINE

Other options:
 -a, --all-tasks      operate on all the tasks (threads) for a given pid
 -m, --max            show min and max valid priorities
 -p, --pid            operate on existing given pid
 -v, --verbose        display status information

 -h, --help     display this help and exit
 -V, --version  output version information and exit

For more details see chrt(1).

比如,获取某个进程的调度策略,使用如下命令:

[root@vm_rp0_cpu1_docker ~]# chrt -p 27916
pid 27916's current scheduling policy: SCHED_FIFO
pid 27916's current scheduling priority: 80

在比如,设置某个进程的调度策略为 SCHED_FIFO,调度优先级为 70,使用如下命令:

[root@vm_rp0_cpu1_docker ~]# chrt -p -f 70 27917
[root@vm_rp0_cpu1_docker ~]#

测试代码

#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <assert.h>
 
static int get_thread_policy(pthread_attr_t *attr)
{
    int policy;
    int rs = pthread_attr_getschedpolicy(attr,&policy);
    assert(rs==0);
 
    switch(policy)
    {   
        case SCHED_FIFO:
            printf("policy=SCHED_FIFO\n");
            break;
 
        case SCHED_RR:
            printf("policy=SCHED_RR\n");
            break;
 
        case SCHED_OTHER:
            printf("policy=SCHED_OTHER\n");
            break;
 
        default:
            printf("policy=UNKNOWN\n");
            break;
    }   
    return policy;
}
 
static void show_thread_priority(pthread_attr_t *attr,int policy)
{
    int priority = sched_get_priority_max(policy);
    assert(priority != -1);
    printf("max_priority=%d\n",priority);
 
    priority= sched_get_priority_min(policy);
    assert(priority != -1);
    printf("min_priority=%d\n",priority);
}
 
static int get_thread_priority(pthread_attr_t *attr)
{
    struct sched_param param;
    int rs = pthread_attr_getschedparam(attr,¶m);
    assert(rs == 0);
 
    printf("priority=%d\n",param.__sched_priority);
    return param.__sched_priority;
}
 
static void set_thread_policy(pthread_attr_t *attr,int policy)
{
    int rs = pthread_attr_setschedpolicy(attr,policy);
    assert(rs==0);
}
 
int main(void)
{
    pthread_attr_t attr;
    int rs;
 
    rs = pthread_attr_init(&attr);
    assert(rs==0);
 
    int policy = get_thread_policy(&attr);
 
    printf("Show current configuration of priority\n");
    get_thread_policy(&attr);
    show_thread_priority(&attr,policy);
 
    printf("show SCHED_FIFO of priority\n");
    show_thread_priority(&attr,SCHED_FIFO);
 
    printf("show SCHED_RR of priority\n");
    show_thread_priority(&attr,SCHED_RR);
 
    printf("show priority of current thread\n");
    get_thread_priority(&attr);
 
    printf("Set thread policy\n");
 
    printf("set SCHED_FIFO policy\n");
    set_thread_policy(&attr,SCHED_FIFO);
    get_thread_policy(&attr);
    get_thread_priority(&attr);
 
    printf("set SCHED_RR policy\n");
    set_thread_policy(&attr,SCHED_RR);
    get_thread_policy(&attr);
 
    printf("Restore current policy\n");
    set_thread_policy(&attr,policy);
    get_thread_priority(&attr);
 
    rs = pthread_attr_destroy(&attr);
    assert(rs==0);
 
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容