感谢前辈,光荣属于前辈。
1进程概念
进程定义
进程是并发环境下,一个具有独立功能的程序在某个数据集上的一次执行活动,它是操作系统进行资源分配和保护的基本单位,也是执行的单位。
PCB
进程控制块(Process Control Block,PCB)是为了描述和控制进程的运行而定义的一种数表结构,它是进程存在的唯一标志,也是进程实体的一部分。操作系统对进程的管理和控制主要以PCB为依据。PCB中包括了操作系统所需要的进程运行的所有信息。
进程的上下文
用户级上下文: 正文、数据、用户堆栈以及共享存储区;寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。
2线程概念
线程上下文
多线程环境下的进程定义,很显然,进程是系统进行资源分配和保护的基本单位。进程包括容纳进程映像的一个虚拟地址空间,以及对CPU、I/O资源、文件以及其他资源的有保护有控制的访问。
可见,一个进程可以划分为两个部分:一部分是资源部分,一部分是线程部分。
TCB
线程由线程控制块(Thread Control Block,TCB)、用户堆栈、系统堆栈以及一组处理器状态寄存器和一个私用内存存储区组成。
3上下文
cpu上下文
CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU 上下文。
进程上下文切换
每当内核压入一个新的系统上下文层时,它就要保存一个进程的上下文。特别是当系统收到一个中断,或一个进程执行系统调用,或当内核做上下文切换时,就要对进程的上下文进行保存。上下文切换情况:
了解这几个场景是非常有必要的,因为一旦出现上下文切换的性能问题,它们就是幕后凶手。
线程上下文切换
虽然同为上下文切换,但同进程内的线程切换,要比多进程间的切换消耗更少的资源,而这,也正是多线程代替多进程的一个优势。
系统调用
系统调用 (system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态运行。
典型实现(Linux)
Linux 在x86上的系统调用通过 int 80h 实现,用系统调用号来区分入口函数。操作系统实现系统调用的基本过程是:
-
应用程序调用库函数(API);
-
API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;
-
内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
-
系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数;
-
中断处理函数返回到 API 中;
-
API 将 EAX 返回给应用程序。
CPU 寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。而系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。
总结:一次系统调用的过程,其实是发生了两次 CPU 上下文切换。但是主要是CPU寄存器,不会涉及到虚拟内存等资源。
中断上下文切换
无论是硬件中断(如来自时钟和外设)、可编程中断(programmed interrupt)(执行引起「软件中断」(software interrupt)的指令),还是例外中断(如页面错),都由系统负责处理。
当发生一个中断时,如果CPU正在比该中断级低的处理机运行级上运行,它就在解码下条指令之前,接收该中断,并提高处理机执行级。这样,在它处理当前的中断时,就不会响应该级别或更低级别的中断,从而维护了内核数据结构的完整性。内核处理中断的操作顺序如下。
4分析linux系统的cpu上下文切换
工具
vmstat
vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。
$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 27458868 145996 4781912 0 0 0 1 0 0 0 0 99 0 0
0 0 0 27459388 145996 4781928 0 0 0 3 8937 15791 1 1 99 0 0
0 0 0 27457272 145996 4781948 0 0 0 10 9022 15774 1 1 99 0 0
你可以通过man手册解读每列的含义。现在我们重点强调:
vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,你就可以查看每个进程上下文切换的情况了。
# 每隔5秒输出1组数据
$ pidstat -wu -t 5
Linux 4.4.0-142-generic (i-0nxoa13q) 07/22/2021 _x86_64_ (16 CPU)
03:54:53 PM UID TGID TID %usr %system %guest %CPU CPU Command
03:54:58 PM 0 3 - 0.00 0.20 0.00 0.20 0 ksoftirqd/0
03:54:58 PM 0 - 3 0.00 0.20 0.00 0.20 0 |__ksoftirqd/0
03:54:58 PM 0 7 - 0.00 0.20 0.00 0.20 12 rcu_sched
03:54:58 PM 0 6849 - 0.80 0.00 0.00 0.80 10 dockerd
...
03:54:53 PM UID TGID TID cswch/s nvcswch/s Command
03:54:58 PM 0 3 - 64.21 0.00 ksoftirqd/0
03:54:58 PM 0 - 3 64.21 0.00 |__ksoftirqd/0
03:54:58 PM 0 7 - 98.01 0.00 rcu_sched
03:54:58 PM 0 - 7 98.01 0.00 |__rcu_sched
...
这个结果中有两列内容是我们的重点关注对象。一个是 cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数,另一个则是 nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。这两个概念你一定要牢牢记住,因为它们意味着不同的性能问题:
proc 文件系统
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。深入了解可以去看这篇文章:https:// tldp.org/LDP/Linux-File system-Hierarchy/html/proc.html 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。
sysbench压力测试
sysbench是一个开源的、模块化的、跨平台的多线程性能测试工具,可以用来进行CPU、内存、磁盘I/O、线程、数据库的性能测试。Sysbench的测试主要包括以下几个方面:
下面的案例基于 Ubuntu 18.04,当然,其他的 Linux 系统同样适用。我使用的案例环境如下所示:
# 以10个线程运行5分钟的基准测试,模拟多线程切换的问题
sysbench --threads=10 --max-time=300 threads run
top命令:
top - 11:10:05 up 10 days, 35 min, 3 users, load average: 2.72, 1.27, 0.64
Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie
%Cpu0 : 22.3 us, 69.4 sy, 0.0 ni, 8.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 23.2 us, 66.3 sy, 0.0 ni, 10.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 22.3 us, 67.1 sy, 0.0 ni, 10.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 22.8 us, 68.8 sy, 0.0 ni, 8.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8008272 total, 2646040 free, 3650548 used, 1711684 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 4048240 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18770 root 20 0 94368 3672 2696 S 378.4 0.0 2:43.48 sysbench
看到内核cpu使用率很高。可疑进程为sysbench。此时可以使用pidstat命令查看:
pidstat -wu -t 5 -p 18770
Linux 3.10.0-1160.15.2.el7.x86_64 (pulsar-1-348d-0002-f741-0002) 2021年07月23日 _x86_64_ (4 CPU)
11时11分26秒 UID TGID TID %usr %system %guest %CPU CPU Command
11时11分31秒 0 18770 - 98.60 100.00 0.00 100.00 3 sysbench
11时11分31秒 0 - 18770 0.00 0.00 0.00 0.00 3 |__sysbench
11时11分31秒 0 - 18771 9.00 28.60 0.00 37.60 2 |__sysbench
11时11分31秒 0 - 18772 9.60 27.40 0.00 37.00 3 |__sysbench
11时11分31秒 0 - 18773 9.80 29.00 0.00 38.80 3 |__sysbench
11时11分31秒 0 - 18774 9.60 27.80 0.00 37.40 2 |__sysbench
11时11分31秒 0 - 18775 9.80 29.00 0.00 38.80 1 |__sysbench
11时11分31秒 0 - 18776 10.40 27.80 0.00 38.20 2 |__sysbench
11时11分31秒 0 - 18777 11.20 27.00 0.00 38.20 1 |__sysbench
11时11分31秒 0 - 18778 9.80 27.40 0.00 37.20 2 |__sysbench
11时11分31秒 0 - 18779 9.80 28.20 0.00 38.00 2 |__sysbench
11时11分31秒 0 - 18780 9.80 28.40 0.00 38.20 0 |__sysbench
11时11分26秒 UID TGID TID cswch/s nvcswch/s Command
11时11分31秒 0 18770 - 0.00 0.00 sysbench
11时11分31秒 0 - 18770 0.00 0.00 |__sysbench
11时11分31秒 0 - 18771 47647.40 132455.60 |__sysbench
11时11分31秒 0 - 18772 47523.60 148473.80 |__sysbench
11时11分31秒 0 - 18773 44959.80 162137.20 |__sysbench
11时11分31秒 0 - 18774 46765.00 138126.60 |__sysbench
11时11分31秒 0 - 18775 47758.40 157361.60 |__sysbench
11时11分31秒 0 - 18776 47324.00 142616.40 |__sysbench
11时11分31秒 0 - 18777 48346.40 145743.60 |__sysbench
11时11分31秒 0 - 18778 49163.60 140320.40 |__sysbench
11时11分31秒 0 - 18779 47157.00 144586.60 |__sysbench
11时11分31秒 0 - 18780 46882.20 144493.00 |__sysbench
非自愿上下文切换为15万左右,说明系统存在强制调度,都在争抢cpu时间,说明 CPU 为系统瓶颈。
上下文切换多少次才算正常?
这个数值其实取决于系统本身的 CPU 性能。如果系统的上下文切换次数比较稳定,且内核cpu使用率很低,都应该算是正常的。当上下文切换在一万左右波动,且内核cpu使用率偏高,就很可能出现了性能问题。
总结
上下文切换发生在操作系统内核中。当看到内核cpu使用率过大,考虑在发生上下文切换。
自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题;
非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 为系统瓶颈;
中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。
本文使用 文章同步助手 同步