深入理解Linux系统-进程调度器

张彤 2023年08月03日 329次浏览

三. 进程调度器

Linux内核具有进程调度器的功能.

它使得多个进程能够同时运行(准确来说,是看上去像是同时运行)

一般而言,我们是无法察觉调度器的存在的,下面是调度器的描述.

  • 一个cpu同时只能运行一个进程
  • 在同时运行多个进程时,每个进程都获得适当的时长.轮流在CPU上执行处理.

调度器的运行方式.jpg

需要指出的是,在CPU主频发展受到限制后,出现了多核心来提升CPU,这导致每一个核心都会被识别为CPU.

系统识别出来的核心称为逻辑核心

在开启超线程技术hyper thread技术后,每一个线程都被识别为一个逻辑CPU.

3.1 实验程序设计

在同时运行一个或多个消耗CPU时间执行处理的进程时,采集以下统计信息.

  • 在某一个时间点运行在逻辑CPU上的进程是哪一个
  • 每个进程运行的进度

程序设计如下

  • 命令行参数,(n,total,resol)

    参数定义
    n同时运行的进程数量
    total程序运行总时长,单位毫秒
    resol采集信息的频率周期,单位毫秒/次
  • 令n个进程同时运行,然后在全部进程金额数后结束父进程的运行,各个进程按照以下要求运行

    • 在消耗total毫秒的cpu时间后结束运行

    • 每resol毫秒记录一次以下三个指标

      pid,进程编号

      从程序开始运行到记录的时间点为止经过的时间

      进程的进度

    • 结束运行后,把统计信息用制表符隔开并逐行输出

实验程序视图图.jpg

3.2 实验程序的实现

代码如下

(base) [root@ecs0003 linux_pro]# vim sched.c
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#define NLOOP_FOR_ESTIMATION 1000000000UL
#define NSECS_PER_MSEC 1000000UL
#define NSECS_PER_SEC 1000000000UL
static inline long diff_nsec(struct timespec before, struct
        timespec after)
{
    return ((after.tv_sec * NSECS_PER_SEC + after.tv_nsec)- (before.tv_sec * NSECS_PER_SEC + before.tv_nsec));
}
static unsigned long loops_per_msec()
{
    struct timespec before, after;
    clock_gettime(CLOCK_MONOTONIC, &before);
    unsigned long i;
    for (i = 0; i < NLOOP_FOR_ESTIMATION; i++);
    clock_gettime(CLOCK_MONOTONIC, &after);
    int ret;
    return NLOOP_FOR_ESTIMATION * NSECS_PER_MSEC / diff_nsec
            (before, after);
}
static inline void load(unsigned long nloop)
{
    unsigned long i;
    for (i = 0; i < nloop; i++)
        ;
}
static void child_fn(int id, struct timespec *buf, int
nrecord, unsigned long nloop_per_resol,
                     struct timespec start)
{
    int i;
    for (i = 0; i < nrecord; i++){
        struct timespec ts;
        load(nloop_per_resol);
        clock_gettime(CLOCK_MONOTONIC, &ts);
        buf[i] = ts;
    }
    for (i = 0; i < nrecord; i++){
        printf("%d\t%ld\t%d\n", id, diff_nsec(start, buf[i]) /
                                    NSECS_PER_MSEC, (i+1) * 100 / nrecord);
    }
    exit(EXIT_SUCCESS);
}
static void parent_fn(int nproc)
{
    int i;
    for (i = 0; i < nproc; i++)
        wait(NULL);
}
static pid_t *pids;
int main(int argc, char *argv[])
{
    int ret = EXIT_FAILURE;
    if (argc < 4){
        fprintf(stderr, "usage: %s <nproc> <total[ms]> <resolution[ms]>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    int nproc = atoi(argv[1]);
    int total = atoi(argv[2]);
    int resol = atoi(argv[3]);
    if (nproc < 1){
        fprintf(stderr, "<nproc>(%d) should be >= 1\n", nproc);
        exit(EXIT_FAILURE);
    }
    if (total < 1){
        fprintf(stderr, "<total>(%d) should be >= 1\n", total);
        exit(EXIT_FAILURE);
    }
    if (resol < 1){
        fprintf(stderr, "<resol>(%d) should be >= 1\n", resol);
        exit(EXIT_FAILURE);
    }
    if (total % resol){
        fprintf(stderr, "<total>(%d) should be multiple of <resolution>(%d)\n", total, resol);
        exit(EXIT_FAILURE);
    }
    int nrecord = total / resol;
    struct timespec *logbuf = malloc(nrecord * sizeof(struct timespec));
    if (!logbuf)
        err(EXIT_FAILURE, "malloc(logbuf) failed");
    puts("estimating workload which takes just one milisecond");
    unsigned long nloop_per_resol = loops_per_msec() * resol;
    puts("end estimation");
    fflush(stdout);
    pids = malloc(nproc * sizeof(pid_t));
    if (pids == NULL){
        warn("malloc(pids) failed");
        goto free_logbuf;
    }
    struct timespec start;
    clock_gettime(CLOCK_MONOTONIC, &start);
    int i, ncreated;
    for (i = 0, ncreated = 0; i < nproc; i++, ncreated++){
        pids[i] = fork();
        if (pids[i] < 0){
            goto wait_children;
        } else if (pids[i] == 0){
// 子进程
            child_fn(i, logbuf, nrecord, nloop_per_resol, start);
/* 不应该运行到这里*/
        }
    }
    ret = EXIT_SUCCESS;
// 父进程
    wait_children:
    if (ret == EXIT_FAILURE)
        for (i = 0; i < ncreated; i++)
            if (kill(pids[i], SIGINT) < 0) warn("kill(%d) failed", pids[i]);
    for (i = 0; i < ncreated; i++)
        if (wait(NULL) < 0) warn("wait() failed.");
    free_pids:
    free(pids);
    free_logbuf:
    free(logbuf);
    exit(ret);
}

编译

(base) [root@ecs0003 linux_pro]# cc -o sched sched.c

3.3 实验

结合上面的程序,进行如下3个实验

  • A 运行1个进程时的情况
  • B 运行2个进程时的情况
  • C 运行4个进程时的情况

利用负载均衡(后述)功能,进程可以根据系统的负载转移逻辑CPU运行.

本实验为了准确,只能运行在指定的单个逻辑CPU上.可以通过taskset命令实现.-c参数指定逻辑CPU

(base) [root@ecs0003 linux_pro]# taskset -c 0 ./sched <n> <total> <resol>

上述命令的意思是,指定0号逻辑CPU运行 sched程序

可以使用使用重定向将结果打印到文件中进行详细分析,绘制图表

taskset -c 0 ./sched 1 100 1 > 1core_sched.log
  1. 实验A

    (base) [root@ecs0003 linux_pro]# taskset -c 0 ./sched 1 100 1
    estimating workload which takes just one milisecond
    end estimation
    0	1	1
    0	2	2
    0	4	3
    0	5	4
    0	6	5
    0	7	6
    0	9	7
    0	10	8
    0	12	9
    0	13	10
    0	14	11
    0	16	12
    0	17	13
    .............
    0	115	88
    0	116	89
    0	117	90
    0	119	91
    0	120	92
    0	121	93
    0	123	94
    0	124	95
    0	125	96
    0	127	97
    0	128	98
    0	129	99
    0	131	100
    
    
  2. 实验B

    (base) [root@ecs0003 linux_pro]# taskset -c 0 ./sched 2 100 1
    estimating workload which takes just one milisecond
    end estimation
    0	2	1
    0	4	2
    0	5	3
    0	7	4
    0	8	5
    0	10	6
    0	12	7
    0	26	8
    0	27	9
    0	29	10
    0	30	11
    0	31	12
    0	32	13
    0	33	14
    0	35	15
    ..........................................................................
    0	234	89
    0	236	90
    0	237	91
    0	238	92
    0	240	93
    0	241	94
    0	243	95
    0	244	96
    0	246	97
    0	260	98
    0	261	99
    0	263	100
    .............................................................................
    1	14	1
    1	15	2
    1	17	3
    1	18	4
    1	20	5
    1	21	6
    1	23	7
    1	24	8
    1	38	9
    1	40	10
    1	41	11
    1	42	12
    ...........................................................................
    1	225	83
    1	226	84
    1	228	85
    1	229	86
    1	230	87
    1	232	88
    1	246	89
    1	247	90
    1	249	91
    1	250	92
    1	252	93
    1	253	94
    1	254	95
    1	256	96
    1	257	97
    1	259	98
    1	264	99
    1	266	100
    
    • 2个进程(进程0与进程1) 在轮流使用cpu.换句话说,不是同时使用该逻辑CPU
    • 2个进程获得的时间片几乎相同
  3. 实验C

    (base) [root@ecs0003 linux_pro]# taskset -c 0 ./sched 4 100 1
    estimating workload which takes just one milisecond
    end estimation
    1	12	1
    1	13	2
    1	15	3
    1	16	4
    1	18	5
    ...............................
    1	521	97
    1	522	98
    1	523	99
    1	525	100
    2	27	1
    2	28	2
    2	30	3
    2	31	4
    2	33	5
    2	34	6
    2	35	7
    ..................................
    2	539	97
    2	541	98
    2	542	99
    2	543	100
    0	1	1
    0	3	2
    0	4	3
    0	5	4
    0	7	5
    0	8	6
    0	9	7
    0	11	8
    .....................
    0	480	96
    0	545	97
    0	547	98
    0	548	99
    0	549	100
    3	38	1
    3	39	2
    3	41	3
    3	42	4
    3	44	5
    3	45	6
    3	46	7
    ..............................
    3	501	92
    3	503	93
    3	528	94
    3	529	95
    3	530	96
    3	532	97
    3	533	98
    3	534	99
    3	551	100
    
    
    • 单位时间进度约为进程数量为1时的1/4.全部进程运行结束所消耗的时间约为进程数量为1时的4倍

3.4 思考

通过上面三个实验,我们得出以下结论:

  • 不管同时运行多少进程,在任意时间点上,只能有一个进程运行在逻辑CPU上.
  • 在逻辑CPU上运行多个进程时,它们将按轮询调度的方式循环运行,即所有进程按照顺序逐个运行,一轮过后,重新从第一个进程开始理轮流运行
  • 每个进程被分配到的时间片的长度大体相等.
  • 全部进程运行结束消耗的时间,随着进程数量的增加而等比增加.

3.5 上下文切换

上下文切换是指切换正在逻辑CPU上运行的进程.

上下文切换.jpg

  • 当一个时间段被消耗完毕后,不管进程正在执行什么代码,都一定会发生上下文切换.

程序中的函数执行时机.jpg

3.6 进程的状态

系统中有多少进程在运行,可以通过命令ps -ax来查看.某个时间点上,系统大部分进程处于睡眠状态

进程的一部分状态如下

状态名含义
运行态在逻辑CPU上运行
就绪态进程具备运行条件,准备分配CPU时间
睡眠态进程不准备运行,除非发生某事件.在此期间不消耗CPU时间
僵死状态进程结束,等待父进程将其回收(Linux系统进程编号为1,所有的进程的根就是它)

PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process:

       D    uninterruptible sleep (usually IO)
          不间断睡眠,通常是IO等待
       R    running or runnable (on run queue)
          正在运行(或者在运行的队列上)
       S    interruptible sleep (waiting for an event to complete)
            可中断的睡眠(等待事件完成,就像上图中的进程0,在进程1运行的时候,就进入了这种睡眠状态)
       T    stopped by job control signal
            被作业控制信号终止.
       t    stopped by debugger during the tracing
            在跟踪期间被调试器停止
       W    paging (not valid since the 2.6.xx kernel)
            分页(内核版本2.6以后失效)
       X    dead (should never be seen)
            死掉(永远不会被看到的)
       Z    defunct ("zombie") process, terminated but not reaped by its parent
            失效的僵尸进程,已经终止,但是其父进程并未回收该进程.

For BSD formats and when the stat keyword is used, additional characters may be displayed:

对于BSD格式以及使用stat关键字时,可能会显示其他字符:

​ 一般使用top,ps -ax命令,状态码的第二个字符

       <    high-priority (not nice to other users)
            高优先级(对其他用户不友好)
       N    low-priority (nice to other users)
            低优先级(对其他用户友好)
       L    has pages locked into memory (for real-time and custom IO)
            将页面锁定入内存(对于实时和客户端IO)
       s    is a session leader
            一个会话的leader
       l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
          是一个多线程进程(使用CLONE_THREAD,类NPTL父线程等系统包装函数)
       +    is in the foreground process group
            前台进程组中的进程

3.7 状态转换

进程各种状态之间的关联和转换

进程的各种状态.jpg

进程的状态以及逻辑CPU上执行的处理.多个进程在某个逻辑CPU核心上

进程的状态以及逻辑CPU上执行的处理.jpg

如果只有一个进程运行在某个逻辑CPU核心上

单个进程运行在逻辑CPU上的状态.jpg

3.8 空闲状态

在上图中,p0进程有一段事件没有在逻辑CPU0上运行.

在这一段时间内,逻辑CPU0会运行一个被称为空闲进程的不执行任何处理的特殊进程.

空闲进程最简单的实现方式就是创建一个新进程.或者在唤醒处于睡眠态的进程之前执行无意义的循环(有点像用户态的自旋锁)

然而,这样是非常费电的,所以通常不会这样处理,而是使用特殊CPU指令,让其进入休眠,直到出现就绪状态的进程.

使用命令sar -P -ALL 1可以每秒观察一次cpu空闲时间占比

[root@sx-v-scjx-dwzyywzt-sjk-pgk-0001 ~]# sar -P ALL 1
Linux 3.10.0-862.el7.x86_64 (sx-v-scjx-dwzyywzt-sjk-pgk-0001.novalocal) 	07/24/2023 	_x86_64_	(32 CPU)

09:29:29 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
09:29:30 AM     all      9.01      0.00      2.07      3.85      0.00     85.07
09:29:30 AM       0      1.00      0.00      0.00      5.00      0.00     94.00
09:29:30 AM       1      1.00      0.00      0.00     24.00      0.00     75.00
09:29:30 AM       2     85.86      0.00      0.00      0.00      0.00     14.14
09:29:30 AM       3     47.52      0.00      0.00      0.00      0.00     52.48
09:29:30 AM       4      0.00      0.00      0.00      0.00      0.00    100.00
09:29:30 AM       5      0.00      0.00      0.00      0.00      0.00    100.00
09:29:30 AM       6     18.00      0.00     11.00      0.00      0.00     71.00
09:29:30 AM       7      0.00      0.00      0.00      0.00      0.00    100.00
09:29:30 AM       8      0.00      0.00      0.00      0.00      0.00    100.00
09:29:30 AM       9      0.00      0.00      0.00      0.00      0.00    100.00
..............................................................................................................
Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all     10.63      0.00      3.48      2.72      0.00     83.17
Average:          0     10.62      0.00      6.25     14.37      0.00     68.75
Average:          1      8.46      0.00      3.76     14.11      0.00     73.67
Average:          2     46.23      0.00      5.35     12.89      0.00     35.53
Average:          3     30.09      0.00      7.21      0.00      0.00     62.70
Average:          4     12.19      0.00      2.50      0.31      0.00     85.00
Average:          5      6.90      0.00      4.08      0.00      0.00     89.03
Average:          6     45.91      0.00     28.93      0.00      0.00     25.16
Average:          7      0.00      0.00      0.00      0.00      0.00    100.00
Average:          8      0.00      0.00      0.00      0.00      0.00    100.00
Average:          9      0.00      0.00      0.00      0.00      0.00    100.00
................................................................................................................
Average:         26      8.72      0.00      0.31      1.56      0.00     89.41
Average:         27      9.35      0.00      0.31      3.74      0.00     86.60
Average:         28      1.89      0.00     10.06     29.56      0.00     58.49
Average:         29      8.12      0.00      6.25      0.00      0.00     85.62
Average:         30     17.45      0.00      9.66      0.00      0.00     72.90
Average:         31     66.25      0.00     12.19      0.94      0.00     20.62

下面,我们编写一个有性能侵入性的python程序

while True:
    pass

运行这个程序后再观察cpu的%ideal

(base) [root@ecs0003 linux_pro]# taskset -c 0 python loop.py &
[1] 160209
(base) [root@ecs0003 linux_pro]# sar -P ALL 1
Linux 3.10.0-862.el7.x86_64 (ecs0003.novalocal) 	07/24/2023 	_x86_64_	(16 CPU)

09:55:05 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
09:55:06 AM     all      9.71      0.00      1.69      0.19      0.00     88.41
09:55:06 AM       0    100.00      0.00      0.00      0.00      0.00      0.00
09:55:06 AM       1      9.00      0.00      5.00      0.00      0.00     86.00
09:55:06 AM       2      0.00      0.00      0.00      0.00      0.00    100.00
09:55:06 AM       3      0.99      0.00      1.98      0.99      0.00     96.04
09:55:06 AM       4      5.00      0.00      4.00      0.00      0.00     91.00
09:55:06 AM       5      5.00      0.00      3.00      0.00      0.00     92.00
09:55:06 AM       6      0.99      0.00      0.99      0.99      0.00     97.03
.............................................................................................
Average:        CPU     %user     %nice   %system   %iowait    %steal     %idle
Average:        all      9.14      0.00      1.00      0.51      0.00     89.35
Average:          0    100.00      0.00      0.00      0.00      0.00      0.00
Average:          1      3.07      0.00      1.71      0.00      0.00     95.22
Average:          2      0.00      0.00      0.00      0.00      0.00    100.00
Average:          3      0.34      0.00      1.71      0.34      0.00     97.61
Average:          4      5.80      0.00      4.10      1.37      0.00     88.74
Average:          5      6.48      0.00      3.07      1.02      0.00     89.42
...................................................
Average:         11      0.68      0.00      0.68      0.00      0.00     98.63
Average:         12      0.00      0.00      0.34      0.00      0.00     99.66
Average:         13      0.34      0.00      0.34      0.00      0.00     99.32
Average:         14      3.07      0.00      2.05      0.68      0.00     94.20
Average:         15     25.43      0.00      1.03      4.81      0.00     68.73
(base) [root@ecs0003 linux_pro]# kill 160209

可以看到0号cpu已经被占满了,%ideal时间为0了.

3.9 各种各样的状态转换

结合3.8的论证,逻辑CPU的实际状态可能更复杂一些

当某个逻辑CPU上只存在一个进程运行时

进程的状态以及逻辑CPU上执行的处理只有进程0时的情况.jpg

我们可以看到,在内核态的时间段内都完成了作业,进程切换为用户态进行输入,文件读取等操作的时候,进程在CPU上呈现就绪态.

当存在多个进程的时候,情况如图

进程的状态以及逻辑CPU上执行的处理多进程时的情况.jpg

看起来多进程是比较复杂的,但是核心就两条

  1. 逻辑CPU上同一时间只能运行一个进程
  2. 睡眠态的进程不会占用CPU时间.

3.10 吞吐量与延迟

CPU主频,吞吐量是CPU性能的两个重要衡量指标

  • 吞吐量:单位时间内的总工作量.
  • 延时:各种处理从开始到完成所耗费的时间.
吞吐量=处理完成的进程数量/耗费的时间
延迟=结束处理的时间-开始处理的时间
  • cpu的计算资源消耗的越多,空闲时间就越少.

  • 在耗尽逻辑CPU的计算力后,所有逻辑CPU都不处于空闲状态后,不管增加多少进程,吞吐量都不会发生变化

  • 随着进程数量的增加,延迟会越来越长

  • 每个进程平均延迟是相等的.

为了避免多进程排队执行出现的不公平,才有了cpu时间分片的方法.

3.11 存在多个逻辑CPU时的调度

存在多个逻辑CPU的时候,如何进行调度呢?

调度器会运行一个被称为均衡负载全局调度的功能.

简单说,负载均衡将进程分配给多个逻辑CPU.

下图演示逻辑CPU0,CPU1为空闲状态后,按顺序执行进程0-3的情况

在2个逻辑CPU上创建并运行进程0进程3的情形.jpg

很显然,增加CPU的逻辑核心,有助于提高吞吐量.

3.12 运行时间和执行时间

通过time命令运行进程,就可以得到进程从开始到结束所经过的时间elaps

  • 运行时间:

    进程从开始运行到运行结束为止所经过的时间.类似于利用秒表从开始运行的时间点开始计时,一直测量到运行结束.

  • 执行时间:

    进程实际占用逻辑CPU的时长

两者关系如下图

运行时间和执行时间.jpg

我们使用[3.2 实验程序的实现](### 3.2 实验程序的实现)的程序sched,结合time命令来观察运行时间和执行时间.

  1. 逻辑CPU数量=1.进程数量=1

    (base) [root@ecs0003 linux_pro]# time taskset -c 0 ./sched 1 10000 10000
    estimating workload which takes just one milisecond
    end estimation
    0	13054	100
    
    real	0m13.172s
    user	0m13.142s
    sys	0m0.001s
    
    
    • real的值为运行时间
    • user+sys的时间是用户态+内核态的执行时间.

    此例中用户独占CPU资源,因此在CPU0上的执行时间几乎等于运行时间.两者差出来的0.029,是sched.cloops_per_msec()这个预处理消耗了一部分事件.

    不入内核态,是因为很少发生中断事件,一直在用户态循环而已.所以sys的值几乎为0.

运行时间与执行时间的差.jpg

  1. 逻辑CPU数量=1,进程数量=2

    (base) [root@ecs0003 linux_pro]# time taskset -c 0 ./sched 2 10000 10000
    estimating workload which takes just one milisecond
    end estimation
    1	26975	100
    0	26977	100
    
    real	0m27.077s
    user	0m27.068s
    sys	0m0.003s
    
    

    可以看到,这次的运行时间和执行时间也几乎相等.

    去除预处理的时间后,运行时间和执行时间几乎是上一次的两倍

    如下图

运行时间与执行时间的差.jpg 运行时间与执行时间的时间差(逻辑CPU数量=1,进程数量=2)

  1. 逻辑CPU数量=2,进程数量=1

    (base) [root@ecs0003 linux_pro]# time taskset -c 0,1 ./sched 1 10000 10000
    estimating workload which takes just one milisecond
    end estimation
    0	13168	100
    
    real	0m13.269s
    user	0m13.262s
    sys	0m0.000s
    
    

    这与单个逻辑CPU时的数据几乎一模一样.计算存在两个逻辑cpu,其中一个也会闲着.果然是1核干活其他看.

运行时间与执行时间的差.jpg

  1. 逻辑CPU数量=2,进程数量=4

    (base) [root@ecs0003 linux_pro]# time taskset -c 0,1 ./sched 4 10000 10000
    estimating workload which takes just one milisecond
    end estimation
    1	26318	100
    0	26325	100
    3	26417	100
    2	26419	100
    
    real	0m26.519s
    user	0m52.917s
    sys	0m0.004s
    
    

    虽然运行时间与逻辑CPU=1,进程=2相似,但是执行时间几乎是它的两倍,这是因为两个进程可以同时在两核心上同一时间运行.

运行时间与执行时间的时间差逻辑CPU数量=2,进程数量=4.jpg

  • 如果没有调度器的相关知识,我们很难理解为什么执行时间比运行时间长.但是通过上图我们就看出多核心对于吞吐量的意义,对于提升性能的意义了.

3.13 睡眠时的进程

进程一开始就进入睡眠会发生什么呢?

(base) [root@ecs0003 linux_pro]# time sleep 10

real	0m10.006s
user	0m0.000s
sys	0m0.002s

执行时间几乎为0,运行时间是10秒.

这意味这基本没有使用逻辑CPU.

3.14 现实中的进程

在现实的服务器中,进程会发生包括睡眠态在内的各种复杂的状态转换.

ps -eo命令的etime字段和time字段分别表示进程从开始到执行命令为止的运行时间执行时间

(base) [root@ecs0003 linux_pro]# ps -eo pid,comm,time,etime
   PID COMMAND             TIME     ELAPSED
     1 systemd         04:33:28 441-23:50:58
     2 kthreadd        00:01:39 441-23:50:58
     3 ksoftirqd/0     00:01:39 441-23:50:58
     5 kworker/0:0H    00:00:00 441-23:50:58
     7 migration/0     00:01:08 441-23:50:58
     8 rcu_bh          00:00:00 441-23:50:58
     9 rcu_sched       07:51:29 441-23:50:58
...........................................................................
172699 postmaster      00:07:06 15-00:25:08
178898 postmaster      00:00:00  7-01:40:49
180107 postmaster      00:00:06  1-00:05:19
180113 postmaster      00:00:03  1-00:05:19
180180 postmaster      00:00:00  1-00:05:05
182919 kworker/12:1    00:00:00    10:56:49
192684 kworker/7:1     00:00:00    10:26:49
195369 dio/dm-0        00:00:00 380-17:40:56
195831 kworker/9:0     00:00:21  2-00:06:03
200870 kworker/13:0    00:00:18  8-00:32:03
235215 kworker/10:2    00:00:00    09:26:46
254034 postmaster      00:03:42  4-22:08:51
254474 kworker/0:0     00:00:00    08:26:49

3.15 变更优先级

前面我们知道的,进程是通过轮询这样的算法进行平均分配逻辑CPU计算资源.

为特定进程指定优先级是可以的,nice()命令可以达到此目的.

  • nice 可以通过-19到20指定进程的运行优先级,默认为0.其中,-19的优先级最高,20的优先级最低.

  • 优先级高的进程可以比优先级低的进程获得更多的CPU时间.

  • 只有root权限的用户才能进行提高优先级的操作,其他用户只能进行进程优先级的降低.

  • 优先级除了在程序中调用函数来设定,比如c语言中的nice(5).还可以通过执行nice命令通过-n选项直接设定.

    nice -n 5 echo hello
    
  • sar命令中的%nice字段的值,表示从默认值0更改为其他优先级后,进程所占用的时间比例.

下面我们降低loop.py的运行优先级,观察sar的输出

(base) [root@ecs0003 linux_pro]# nice -n 5 python ./loop.py &
[1] 166863
(base) [root@ecs0003 linux_pro]# sar -P ALL 1 1
Linux 3.10.0-862.el7.x86_64 (ecs0003.novalocal) 	07/25/2023 	_x86_64_	(16 CPU)

11:23:10 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
11:23:11 AM     all      6.32      6.26      0.50      2.19      0.00     84.72
11:23:11 AM       0      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM       1     39.80      0.00      3.06     17.35      0.00     39.80
11:23:11 AM       2      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM       3      0.00    100.00      0.00      0.00      0.00      0.00
11:23:11 AM       4     11.88      0.00      1.98      5.94      0.00     80.20
11:23:11 AM       5      0.00      0.00      1.00      0.00      0.00     99.00
11:23:11 AM       6      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM       7      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM       8     29.00      0.00      1.00      8.00      0.00     62.00
11:23:11 AM       9      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      10      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      11      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      12      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      13      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      14      0.00      0.00      0.00      0.00      0.00    100.00
11:23:11 AM      15     19.80      0.00      0.99      4.95      0.00     74.26
........................................................................................
Average:         13      0.00      0.00      0.00      0.00      0.00    100.00
Average:         14      0.00      0.00      0.00      0.00      0.00    100.00
Average:         15     19.80      0.00      0.99      4.95      0.00     74.26
(base) [root@ecs0003 linux_pro]# kill 166863
[1]+  Terminated              nice -n 5 python ./loop.py

在不使用nice命令后,%user字段由之前的100%变化为非100%,而%nice则出现了100%