发现工作负载使用的 Linux 内核子系统¶
- 作者:
Shuah Khan <skhan@linuxfoundation.org>
Shefali Sharma <sshefali021@gmail.com>
- 维护者:
Shuah Khan <skhan@linuxfoundation.org>
要点¶
方法论¶
strace 是一种诊断、教学和调试工具,可用于发现工作负载正在使用的系统资源。一旦我们发现并了解了工作负载的需求,我们就可以专注于它们以避免回归,并使用它来评估安全考虑因素。我们使用 strace 工具来跟踪工作负载。
这种使用 strace 进行跟踪的方法告诉我们工作负载调用的系统调用,并且不包括它可以调用的所有系统调用。此外,这种跟踪方法只告诉我们这些系统调用中被调用的代码路径。例如,如果工作负载打开文件并从中成功读取,则跟踪的是成功路径。该系统调用中的任何错误路径都不会被跟踪。如果有一个工作负载提供了对工作负载的全面覆盖,那么此处概述的方法将跟踪并找到所有可能的代码路径。系统使用信息的完整性取决于工作负载覆盖范围的完整性。
目标是在运行默认内核的系统上跟踪工作负载,而无需自定义内核安装。
我们如何收集细粒度的系统信息?¶
strace 工具可用于跟踪进程发出的系统调用及其接收的信号。系统调用是应用程序和操作系统内核之间的基本接口。它们使程序能够请求内核的服务。例如,Linux 中的 open() 系统调用用于提供对文件系统中文件的访问。strace 使我们能够跟踪应用程序发出的所有系统调用。它列出进程发出的所有系统调用及其产生的输出。
您可以生成分析数据,将 strace 和 perf record 工具结合起来,以记录与进程相关的事件和信息。这提供了对进程的深入了解。“perf annotate”工具生成程序每个指令的统计信息。本文档详细介绍了如何收集关于工作负载系统资源使用的细粒度信息。
我们使用 strace 来跟踪 perf、stress-ng、paxtest 工作负载,以说明我们发现工作负载使用资源的方法。此过程可用于跟踪其他工作负载。
让系统为跟踪做好准备¶
在开始之前,我们将向您展示如何让您的系统做好准备。我们假设您在物理系统或虚拟机上运行 Linux 发行版。大多数发行版都将包含 strace 命令。让我们安装通常不包含在构建 Linux 内核中的其他工具。请注意,以下操作适用于基于 Debian 的发行版。您可能必须在其他 Linux 发行版上找到等效的软件包。
安装构建 Linux 内核和内核存储库中的工具的工具。scripts/ver_linux 是检查您的系统是否已安装必要工具的好方法
sudo apt-get build-essentials flex bison yacc
sudo apt install libelf-dev systemtap-sdt-dev libaudit-dev libslang2-dev libperl-dev libdw-dev
cscope 是浏览内核源代码的好工具。让我们现在安装它
sudo apt-get install cscope
安装 stress-ng 和 paxtest
apt-get install stress-ng
apt-get install paxtest
工作负载概述¶
如前所述,我们使用 strace 来跟踪 perf bench、stress-ng 和 paxtest 工作负载,以展示如何分析工作负载并识别这些工作负载使用的 Linux 子系统。让我们从这三个工作负载的概述开始,以更好地了解它们的作用以及如何使用它们。
perf bench (all) 工作负载¶
perf bench 命令包含多个多线程微内核基准测试,用于在 Linux 内核和系统调用中执行不同的子系统。这使我们能够轻松衡量更改的影响,这有助于缓解性能回归。它还充当公共基准测试框架,使开发人员能够轻松创建测试用例、透明地集成以及使用性能丰富的工具子系统。
Stress-ng netdev 压力器工作负载¶
stress-ng 用于对内核执行压力测试。它允许您使用“压力器”来测试计算机的各种物理子系统以及 OS 内核的接口。它们可用于 CPU、CPU 缓存、设备、I/O、中断、文件系统、内存、网络、操作系统、管道、调度器和虚拟机。请参阅 stress-ng 手册页以查找所有可用压力器的描述。netdev 压力器启动指定数量 (N) 的工作程序,这些工作程序在所有可用的网络设备上执行各种网络设备 ioctl 命令。
paxtest kiddie 工作负载¶
paxtest 是一个测试内核中缓冲区溢出的程序。它测试内核对内存使用的强制执行。通常,在某些内存段中的执行会使缓冲区溢出成为可能。它运行一组尝试破坏内存使用的程序。它用作 PaX 的回归测试套件,但可能有助于测试内核的其他内存保护补丁。我们使用了 paxtest kiddie 模式,该模式查找简单的漏洞。
什么是 strace 以及我们如何使用它?¶
如前所述,strace 是一种有用的诊断、教学和调试工具,可用于发现工作负载正在使用的系统资源。它可用于
查看进程如何与内核交互。
查看进程为何失败或挂起。
用于逆向工程进程。
查找程序依赖的文件。
用于分析应用程序的性能。
用于排除与操作系统相关的各种问题。
此外,strace 可以生成关于每个系统调用的时间、调用和错误的运行时统计信息,并在程序退出时报告摘要,从而抑制常规输出。这试图显示独立于挂钟时间的系统时间(在内核中运行所花费的 CPU 时间)。我们计划使用这些功能来获取有关工作负载系统使用情况的信息。
strace 命令支持基本、详细和统计模式。当在详细模式下运行时,strace 命令会提供有关进程调用的系统调用的更详细信息。
运行 strace -c 会生成一份报告,其中包含每个系统调用所花费的时间百分比、总时间(秒)、每次调用的微秒数、总调用次数、因错误而失败的每个系统调用的计数以及进行的系统调用类型。
用法:strace <要跟踪的命令>
详细模式用法:strace -v <命令>
收集统计信息:strace -c <命令>
我们使用“-c”选项来收集我们为本次分析选择的三个工作负载使用的细粒度运行时统计信息。
perf
stress-ng
paxtest
什么是 cscope?我们如何使用它?¶
现在让我们看看 cscope,这是一个用于浏览 C、C++ 或 Java 代码库的命令行工具。我们可以使用它来查找对符号的所有引用、全局定义、函数调用的函数、调用函数的函数、文本字符串、正则表达式模式、包含文件的文件。
我们可以使用 cscope 来查找哪个系统调用属于哪个子系统。这样,我们可以找到进程执行时使用的内核子系统。
让我们检出最新的 Linux 存储库并构建 cscope 数据库
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux
cd linux
cscope -R -p10 # builds cscope.out database before starting browse session
cscope -d -p10 # starts browse session on cscope.out database
注意:运行“cscope -R -p10”来构建数据库,运行“cscope -d -p10”来进入浏览会话。cscope 默认使用 cscope.out 数据库。要退出此模式,请按 ctrl+d。-p 选项用于指定要显示的文件路径组件的数量。-p10 是浏览内核源代码的最佳选择。
什么是 perf?我们如何使用它?¶
Perf 是一个基于 Linux 2.6+ 系统的分析工具,它抽象了 Linux 中性能测量中 CPU 硬件的差异,并提供了一个简单的命令行界面。Perf 基于内核导出的 perf_events 接口。它对于分析系统和查找应用程序中的性能瓶颈非常有用。
如果您尚未检出 Linux 主线存储库,您可以这样做,然后构建内核和 perf 工具
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux
cd linux
make -j3 all
cd tools/perf
make
注意:perf 命令可以在不构建存储库中的内核的情况下构建,并且可以在较旧的内核上运行。但是,匹配内核和 perf 版本可以提供更准确的子系统使用信息。
我们使用了“perf stat”和“perf bench”选项。有关 perf 工具的详细信息,请运行“perf -h”。
perf stat¶
perf stat 命令会生成各种硬件和软件事件的报告。它借助现代 CPU 中用于记录这些活动计数的硬件计数器寄存器来实现这一点。“perf stat cal”显示 cal 命令的统计信息。
perf bench¶
perf bench 命令包含多个多线程微内核基准测试,用于执行 Linux 内核和系统调用中的不同子系统。这使我们可以轻松衡量更改的影响,从而有助于缓解性能下降。它还充当一个通用的基准测试框架,使开发人员可以轻松创建测试用例、透明地集成并使用性能丰富的工具。
“perf bench all”命令运行以下基准测试
sched/messaging
sched/pipe
syscall/basic
mem/memcpy
mem/memset
什么是 stress-ng?我们如何使用它?¶
如前所述,stress-ng 用于对内核执行压力测试。它允许您使用压力源来练习计算机的各种物理子系统以及操作系统内核的接口。它们可用于 CPU、CPU 缓存、设备、I/O、中断、文件系统、内存、网络、操作系统、管道、调度程序和虚拟机。
netdev 压力源启动 N 个工作线程,这些工作线程在所有可用的网络设备上执行各种网络设备 ioctl 命令。执行以下 ioctl 操作
SIOCGIFCONF、SIOCGIFINDEX、SIOCGIFNAME、SIOCGIFFLAGS
SIOCGIFADDR、SIOCGIFNETMASK、SIOCGIFMETRIC、SIOCGIFMTU
SIOCGIFHWADDR、SIOCGIFMAP、SIOCGIFTXQLEN
以下命令运行压力源
stress-ng --netdev 1 -t 60 --metrics command.
我们可以使用 perf record 命令来记录与进程关联的事件和信息。此命令将分析数据记录在同一目录的 perf.data 文件中。
使用以下命令,您可以记录与 netdev 压力源关联的事件,查看生成的报告 perf.data,并添加注释以查看程序每条指令的统计信息
perf record stress-ng --netdev 1 -t 60 --metrics command.
perf report
perf annotate
什么是 paxtest?我们如何使用它?¶
paxtest 是一个测试内核中缓冲区溢出的程序。它测试内核对内存使用情况的强制执行。通常,在某些内存段中的执行会使缓冲区溢出成为可能。它运行一组试图破坏内存使用情况的程序。它用作 PaX 的回归测试套件,并且将有助于测试内核的其他内存保护补丁。
paxtest 提供 kiddie 和 blackhat 模式。paxtest kiddie 模式以正常模式运行,而 blackhat 模式则试图绕过内核的保护来测试漏洞。我们在此处重点介绍 kiddie 模式,并将“paxtest kiddie”运行与“perf record”相结合,以收集 paxtest kiddie 运行的 CPU 堆栈跟踪,以查看性能配置文件中哪个函数正在调用其他函数。然后可以使用“dwarf”(DWARF 的调用帧信息)模式来展开堆栈。
以下命令可用于以调用图格式查看生成的报告
perf record --call-graph dwarf paxtest kiddie
perf report --stdio
跟踪工作负载¶
现在我们了解了工作负载,让我们开始跟踪它们。
跟踪 perf bench all 工作负载¶
运行以下命令以跟踪 perf bench all 工作负载
strace -c perf bench all
工作负载发出的系统调用
下表显示了工作负载调用的系统调用、每个系统调用的调用次数以及相应的 Linux 子系统。
系统调用 |
# 调用 |
Linux 子系统 |
系统调用 (API) |
---|---|---|---|
getppid |
10000001 |
进程管理 |
sys_getpid() |
clone |
1077 |
进程管理 |
sys_clone() |
prctl |
23 |
进程管理 |
sys_prctl() |
prlimit64 |
7 |
进程管理 |
sys_prlimit64() |
getpid |
10 |
进程管理 |
sys_getpid() |
uname |
3 |
进程管理 |
sys_uname() |
sysinfo |
1 |
进程管理 |
sys_sysinfo() |
getuid |
1 |
进程管理 |
sys_getuid() |
getgid |
1 |
进程管理 |
sys_getgid() |
geteuid |
1 |
进程管理 |
sys_geteuid() |
getegid |
1 |
进程管理 |
sys_getegid |
close |
49951 |
文件系统 |
sys_close() |
pipe |
604 |
文件系统 |
sys_pipe() |
openat |
48560 |
文件系统 |
sys_opennat() |
fstat |
8338 |
文件系统 |
sys_fstat() |
stat |
1573 |
文件系统 |
sys_stat() |
pread64 |
9646 |
文件系统 |
sys_pread64() |
getdents64 |
1873 |
文件系统 |
sys_getdents64() |
access |
3 |
文件系统 |
sys_access() |
lstat |
1880 |
文件系统 |
sys_lstat() |
lseek |
6 |
文件系统 |
sys_lseek() |
ioctl |
3 |
文件系统 |
sys_ioctl() |
dup2 |
1 |
文件系统 |
sys_dup2() |
execve |
2 |
文件系统 |
sys_execve() |
fcntl |
8779 |
文件系统 |
sys_fcntl() |
statfs |
1 |
文件系统 |
sys_statfs() |
epoll_create |
2 |
文件系统 |
sys_epoll_create() |
epoll_ctl |
64 |
文件系统 |
sys_epoll_ctl() |
newfstatat |
8318 |
文件系统 |
sys_newfstatat() |
eventfd2 |
192 |
文件系统 |
sys_eventfd2() |
mmap |
243 |
内存管理 |
sys_mmap() |
mprotect |
32 |
内存管理 |
sys_mprotect() |
brk |
21 |
内存管理 |
sys_brk() |
munmap |
128 |
内存管理 |
sys_munmap() |
set_mempolicy |
156 |
内存管理 |
sys_set_mempolicy() |
set_tid_address |
1 |
进程管理 |
sys_set_tid_address() |
set_robust_list |
1 |
Futex |
sys_set_robust_list() |
futex |
341 |
Futex |
sys_futex() |
sched_getaffinity |
79 |
调度器 |
sys_sched_getaffinity() |
sched_setaffinity |
223 |
调度器 |
sys_sched_setaffinity() |
socketpair |
202 |
网络 |
sys_socketpair() |
rt_sigprocmask |
21 |
信号 |
|
rt_sigaction |
36 |
信号 |
|
rt_sigreturn |
2 |
信号 |
sys_rt_sigreturn() |
wait4 |
889 |
时间 |
sys_wait4() |
clock_nanosleep |
37 |
时间 |
sys_clock_nanosleep() |
capget |
4 |
能力 |
sys_capget() |
跟踪 stress-ng netdev 压力源工作负载¶
运行以下命令以跟踪 stress-ng netdev 压力源工作负载
strace -c stress-ng --netdev 1 -t 60 --metrics
工作负载发出的系统调用
下表显示了工作负载调用的系统调用、每个系统调用的调用次数以及相应的 Linux 子系统。
系统调用 |
# 调用 |
Linux 子系统 |
系统调用 (API) |
---|---|---|---|
openat |
74 |
文件系统 |
sys_openat() |
close |
75 |
文件系统 |
sys_close() |
read |
58 |
文件系统 |
sys_read() |
fstat |
20 |
文件系统 |
sys_fstat() |
flock |
10 |
文件系统 |
|
write |
7 |
文件系统 |
sys_write() |
getdents64 |
8 |
文件系统 |
sys_getdents64() |
pread64 |
8 |
文件系统 |
sys_pread64() |
lseek |
1 |
文件系统 |
sys_lseek() |
access |
2 |
文件系统 |
sys_access() |
getcwd |
1 |
文件系统 |
sys_getcwd() |
execve |
1 |
文件系统 |
sys_execve() |
mmap |
61 |
内存管理 |
sys_mmap() |
munmap |
3 |
内存管理 |
sys_munmap() |
mprotect |
20 |
内存管理 |
sys_mprotect() |
mlock |
2 |
内存管理 |
sys_mlock() |
brk |
3 |
内存管理 |
sys_brk() |
rt_sigaction |
21 |
信号 |
|
rt_sigprocmask |
1 |
信号 |
|
sigaltstack |
1 |
信号 |
sys_sigaltstack() |
rt_sigreturn |
1 |
信号 |
sys_rt_sigreturn() |
getpid |
8 |
进程管理 |
sys_getpid() |
prlimit64 |
5 |
进程管理 |
sys_prlimit64() |
arch_prctl |
2 |
进程管理 |
sys_arch_prctl() |
sysinfo |
2 |
进程管理 |
sys_sysinfo() |
getuid |
2 |
进程管理 |
sys_getuid() |
uname |
1 |
进程管理 |
sys_uname() |
setpgid |
1 |
进程管理 |
sys_setpgid() |
getrusage |
1 |
进程管理 |
sys_getrusage() |
geteuid |
1 |
进程管理 |
sys_geteuid() |
getppid |
1 |
进程管理 |
sys_getppid() |
sendto |
3 |
网络 |
sys_sendto() |
connect |
1 |
网络 |
sys_connect() |
socket |
1 |
网络 |
sys_socket() |
clone |
1 |
进程管理 |
sys_clone() |
set_tid_address |
1 |
进程管理 |
sys_set_tid_address() |
wait4 |
2 |
时间 |
sys_wait4() |
alarm |
1 |
时间 |
sys_alarm() |
set_robust_list |
1 |
Futex |
sys_set_robust_list() |
跟踪 paxtest kiddie 工作负载¶
运行以下命令以跟踪 paxtest kiddie 工作负载
strace -c paxtest kiddie
工作负载发出的系统调用
下表显示了工作负载调用的系统调用、每个系统调用的调用次数以及相应的 Linux 子系统。
系统调用 |
# 调用 |
Linux 子系统 |
系统调用 (API) |
---|---|---|---|
read |
3 |
文件系统 |
sys_read() |
write |
11 |
文件系统 |
sys_write() |
close |
41 |
文件系统 |
sys_close() |
stat |
24 |
文件系统 |
sys_stat() |
fstat |
2 |
文件系统 |
sys_fstat() |
pread64 |
6 |
文件系统 |
sys_pread64() |
access |
1 |
文件系统 |
sys_access() |
pipe |
1 |
文件系统 |
sys_pipe() |
dup2 |
24 |
文件系统 |
sys_dup2() |
execve |
1 |
文件系统 |
sys_execve() |
fcntl |
26 |
文件系统 |
sys_fcntl() |
openat |
14 |
文件系统 |
sys_openat() |
rt_sigaction |
7 |
信号 |
|
rt_sigreturn |
38 |
信号 |
sys_rt_sigreturn() |
clone |
38 |
进程管理 |
sys_clone() |
wait4 |
44 |
时间 |
sys_wait4() |
mmap |
7 |
内存管理 |
sys_mmap() |
mprotect |
3 |
内存管理 |
sys_mprotect() |
munmap |
1 |
内存管理 |
sys_munmap() |
brk |
3 |
内存管理 |
sys_brk() |
getpid |
1 |
进程管理 |
sys_getpid() |
getuid |
1 |
进程管理 |
sys_getuid() |
getgid |
1 |
进程管理 |
sys_getgid() |
geteuid |
2 |
进程管理 |
sys_geteuid() |
getegid |
1 |
进程管理 |
sys_getegid() |
getppid |
1 |
进程管理 |
sys_getppid() |
arch_prctl |
2 |
进程管理 |
sys_arch_prctl() |
结论¶
本文档旨在用作指南,介绍如何使用 strace 收集有关工作负载所用资源的细粒度信息。