1
问题:内核对进程调度时发生了什么?

进程调度的本质

  • 任务 / 进程切换

    • 即:上下文切换,内核对处理器上的进程进行切换
    • “上下文” 指 : 寄存器上的值
    • “上下文” 指 :

      • 将寄存器的值保存到内存中(进程被剥夺处理器,停止执行)
      • 将另一组寄存器的值从内存中加载到寄存器(调度下一个进程执行)

    image.png

进程调度的本质

image.png

当时间片耗完,不管进程正在执行什么代码,都一定会发生上下文切换!

  • 上下文切换必然导致进程状态的切换
  • 上下文切换由终端触发(时钟中断,IO 中断, 等)
有趣的问题
    上下文切换时,突然收到一个中断会发生什么

答:
    对于简单设计,在上下文切换时会关闭中断

image.png

详解 Linux 进程状态 (ps au)

image.png

可执行状态 <==> 就绪态
Z, 当前进程已经执行结束,但是资源还未被回收(暂时存在的状态)

image.png

main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>

static void *thread_entry(void *arg)
{
    while (1);

    return arg;
}

static void make_thread(void)
{
    pthread_t tid = {0};

    pthread_create(&tid, NULL, thread_entry, NULL);
}

int main(void)
{
    printf("pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    printf("hello word\n");

    while (1) sleep(1);

    return 0;
}
输出:
wu_tiansong@ubuntu-server:~/project/my_linux$ ./a.out 
pid = 914014, ppid = 829629, pgid = 914014
hello word
启用新的控制台
# CPU 状态 S+,CPU 利用率 0

wu_tian+  914014  0.0  0.0   2644   624 pts/20   S+   20:17   0:00 ./a.out
mian.c
int main(void)
{
    printf("pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    printf("hello word\n");

    while (1);  // 删除 sleep

    return 0;
}
启用新的控制台
# CPU 状态 R+,CPU 利用率 109

wu_tian+ 1038500  109  0.0   2644   620 pts/20   R+   20:23   0:03 ./a.out
main.c
int main(void)
{
    printf("pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    printf("hello word\n");

    make_thread();  // 启用多线程

    while (1);

    return 0;
}
启用新的控制台
# CPU 状态 Rl+,CPU 利用率 180

wu_tian+ 1044852  180  0.0  10840   560 pts/20   Rl+  20:27   0:12 ./a.out
对于 CPU 利用率超过 100% 的说明: 有多个 CPU 核心被占用 (CPU 调度的基本单位是线程)
wu_tiansong@ubuntu-server:~/project/my_linux$ lscpu
Architecture:                       x86_64
CPU op-mode(s):                     32-bit, 64-bit
Byte Order:                         Little Endian
Address sizes:                      46 bits physical, 48 bits virtual
CPU(s):                             12                   # ======>>>>> CPU 总计 12 核心
...
指定进程在特定核心运行
# 控制台运行
wu_tiansong@ubuntu-server:~/project/my_linux$ taskset -c 0 ./a.out 
pid = 1054371, ppid = 829629, pgid = 1054371
hello word

# 启用新的控制台, CPU 利用率在 100% 内
wu_tian+ 1054371 98.8  0.0  10840   620 pts/20   Rl+  20:32   0:04 ./a.out
总结
线程是 CPU 调度的基本单位,同时 linux 会将线程调度到不同的 CPU 上

每个核心一时刻只能执行一个线程,对于当前处理器,最多可以并行执行 12 个线程

细说空闲状态

  • 处理器上电后,开始一直不停的向下执行指令
  • 当线程中没有进程时,会执行一个“不执行任何操作”的空闲进程
  • 空闲进程的职责:执行特殊指令使处理器进入休眠状态(低功耗状态)
  • 空闲状态是一种暂态,但凡出现就绪进程,空闲状态立即结束

Linux 性能工具介绍

  • ps, 查看进程运行时的数据 (pa au)
  • top, Linux 整体性能检测工具(类似任务管理器)
  • sar, Linux 活动情况报告(系统性能分析工具)

系统的平均负载

  • 即:Linux 系统平均负载值 (Linux System Load Averages)
  • 该值表示的时一段时间内任务对系统资源需求的平均值 (1、5和15分钟)

    • 如果平均值接近 0 , 意味着系统处于空闲状态
    • 如果平均值大于 1, 意味着系统繁忙,任务需要等待,无法及时执行 (对于仅有1个处理器时为1,4个处理器时为4)

      • 如果 1min 平均值高于 5min 或 15min 平均值,则负载正在增加
      • 如果 1min 平均值低于 5min 或 15min 平均值,则负载正在减少

详解 sar -q

  • runq-sz : 执行队列的长度 (正在被执行的线程个数)
  • plist-sz : 运行中的任务(进程 & 线程)总数
  • ldavg-1 : 最近 1 分钟系统平均负载
  • ldavg-5 : 最近 5 分钟系统平均负载
  • ldavg-15 : 最近 15 分钟系统平均负载
如果系统平均负载值大于处理器的数量,那么系统可能遇到了性能问题 (即,1个处理器时超过1,4个处理器时超过4)

系统调度实验观察

  • 通过 Linux 性能工具观察进程调度

    • 单处理器运行过程
    • 多处理器运行过程
int main(void)
{
    printf("pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    printf("hello word\n");

    while (1) sleep(1);

    return 0;
}
输出:
tiansong@tiansong-VMware-Virtual-Platform:~/桌面$ taskset -c 0,1 ./a.out &
[1] 3642
tiansong@tiansong-VMware-Virtual-Platform:~/桌面$ pid = 3642, ppid = 3272, pgid = 3642
hello word
taskset -c 2,3 ./a.out &
[2] 3706
pid = 3706, ppid = 3272, pgid = 3706
hello word

###############################################################################

# CPU 占用率 200 + 200, 处于繁忙状态
tiansong    3642  200  0.0  10876  1408 pts/1    Rl   21:19   0:51 ./a.out
tiansong    3706  200  0.0  10876  1408 pts/1    Rl   21:19   0:14 ./a.out

################################################################################

load average: 3.96, 2.40, 1.09. 其中1分钟平均负载值小于 12, 说明还有 CPU 资源可用

# top -d 1  (1s 刷新一次)
top - 21:23:20 up 7 min,  3 users,  load average: 3.96, 2.40, 1.09
任务: 402 total,   3 running, 399 sleeping,   0 stopped,   0 zombie
%Cpu0  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu3  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu4  :  4.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu5  :  4.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu6  :  58.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu7  :  0.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu8  :  0.0 us,  1.0 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu9  :  2.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu10 :  2.0 us,  1.0 sy,  0.0 ni, 97.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
%Cpu11 :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
MiB Mem :   7716.2 total,   5661.7 free,   1273.8 used,   1059.5 buff/cache     
MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.   6442.4 avail Mem 

 进程号 USER      PR  NI    VIRT    RES    SHR    %CPU  %MEM     TIME+ COMMAND                                                           
   3642 tiansong  20   0   10876   1408   1408 R 199.0   0.0   8:09.79 a.out                                                             
   3706 tiansong  20   0   10876   1408   1408 R 199.0   0.0   7:32.90 a.out                                                             
   2972 tiansong  20   0 1318012  89760  45312 S   2.0   1.1   0:08.92 node                                                              
   3795 tiansong  20   0   14832   6016   3712 R   2.0   0.1   0:01.46 top                                                               
   1585 tiansong  20   0 4268160 267528 121952 S   1.0   3.4   0:08.09 gnome-shell                                                       
   3125 tiansong  20   0   15684   7332   4992 S   1.0   0.1   0:00.86 sshd                                                              
      1 root      20   0   23196  13668   9316 S   0.0   0.2   0:03.65 systemd                                                           
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.02 kthreadd                                                          
      3 root      20   0       0      0      0 S   0.0   0.0   0:00.00 pool_workqueue_release                                            
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-rcu_g                                                   
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-rcu_p                                                   
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-slub_                                                   
      7 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-netns                                                   
      8 root      20   0       0      0      0 I   0.0   0.0   0:00.03 kworker/0:0-cgroup_destroy                                        
      9 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/0:0H-events_highpri                                       
     11 root      20   0       0      0      0 I   0.0   0.0   0:00.06 kworker/u256:0-ext4-rsv-conversion                                
     12 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-mm_pe                                                   
     13 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_kthread
sq 的使用
tiansong@tiansong-VMware-Virtual-Platform:~/桌面$ taskset -c 0,1 ./a.out &
[1] 6082
pid = 6082, ppid = 5980, pgid = 6082
hello word
tiansong@tiansong-VMware-Virtual-Platform:~/桌面$ sar -q 1   # 指定 1 S 中打印一次,同时观察数据可得,系统负载值在增加
Linux 6.8.0-39-generic (tiansong-VMware-Virtual-Platform)       2024年09月24日  _x86_64_        (12 CPU)

21时35分36秒   runq-sz  plist-sz   ldavg-1   ldavg-5  ldavg-15   blocked
21时35分37秒         3       811      0.41      2.43      2.52         0
21时35分38秒         2       812      0.62      2.44      2.52         0
21时35分39秒         3       809      0.62      2.44      2.52         0
21时35分40秒         2       809      0.62      2.44      2.52         0
21时35分41秒         2       811      0.62      2.44      2.52         0
21时35分42秒         2       808      0.62      2.44      2.52         0
21时35分43秒         2       808      0.73      2.43      2.52         0
21时35分44秒         2       808      0.73      2.43      2.52         0
21时35分45秒         2       808      0.73      2.43      2.52         0
21时35分46秒         3       811      0.73      2.43      2.52         0
21时35分47秒         2       808      0.73      2.43      2.52         0
21时35分48秒         3       808      0.83      2.42      2.52         0
tiansong@tiansong-VMware-Virtual-Platform:~/桌面$ sar -P 0,1,2,3 1  # 观察四个处理器的负载情况,并且 1 秒钟输出一次
Linux 6.8.0-39-generic (tiansong-VMware-Virtual-Platform)       2024年09月24日  _x86_64_        (12 CPU)

21时38分55秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21时38分56秒       0    100.00      0.00      0.00      0.00      0.00      0.00
21时38分56秒       1    100.00      0.00      0.00      0.00      0.00      0.00
21时38分56秒       2      0.00      0.00      0.00      0.00      0.00    100.00
21时38分56秒       3      1.00      0.00      0.00      0.00      0.00     99.00

21时38分56秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21时38分57秒       0    100.00      0.00      0.00      0.00      0.00      0.00
21时38分57秒       1    100.00      0.00      0.00      0.00      0.00      0.00
21时38分57秒       2      0.00      0.00      1.98      0.00      0.00     98.02
21时38分57秒       3      0.00      0.00      0.00      0.00      0.00    100.00

系统调度核心性能指标

  • 吞吐量:单位时间内的工作总量 (越大越好)

    • 处理器资源消耗越多(空闲状态占比越低),吞吐量越大
    • 对于进程调度而言,吞吐量指单位时间处理(处理完)的进程数量
  • 延迟:从开始处理任务到结束处理任务所耗费的时间(越短越好)

    • 对于进程而言,延迟即是生命周期,指进程从运行到结束所经历的事故华北
    • 注意:运行 和 执行 不同,运行时间可能很长,但执行时间可能很短

吞吐量计算一

image.png

吞吐量 = 1 进程 / 100ms = 1 进程 / 0.1s = 10 进程 / 秒
延迟 = 100ms

吞吐量计算二

image.png

吞吐量 = 2 进程 / 120ms = 1 进程 / 0.06s = 16.7 进程 / 秒
延迟 = 120ms

吞吐量计算三

image.png

吞吐量 = 3 进程 / (40 + 60*2 + 20) = 1进程 / 0.06s = 16.7 进程 / 秒
延迟 = 180ms (图中没有花完整)

示例

  • 假设:每个进程固定执行 60ms
  • 则: 进程运行结束时

image.png

结论
  • 处理器的能力由硬件决定,吞吐量存在一个上限
  • 当吞吐量未达上限,进程的延迟取决于进程自身
  • 当吞吐量达到上线,随着进程数量增加,总延迟增加,但平均延迟不变(即吞吐量不变,上图当有多个线程时的平均延迟均为60ms)

思考

问:如何提高系统吞吐量?
答:提高处理器能力 ; 提高处理器数量

多核吞吐量计算

image.png

吞吐量 = 4 进程 / 120ms = 2 进程 / 0.06s = 33.3 进程 / 秒
延迟 = 120ms

对于多处理器计算机来说,只有多个进程并行执行才能提高吞吐量;
并且吞吐量也存在一个上限值,当进程数量多于处理器数量时,吞吐量不再提高。

现实中的系统

  • 理想状态:进程正在执行,并且没有就绪状态的进程
  • 空闲状态:处理器占用率低,吞吐量低
  • 繁忙状态:

    • 多个进程同时运行,但存在多个就绪状态进程
    • 此时,吞吐量很高(可能达到峰值),但总体延迟会边长

课后思考

如何验证处理器,进程数量,吞吐量之间的关系?

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧