发现工作负载使用的 Linux 内核子系统

作者:
维护者:

Shuah Khan <skhan@linuxfoundation.org>

要点

  • 了解构建和运行工作负载所需的系统资源非常重要。

  • Linux 跟踪和 strace 可用于发现工作负载正在使用的系统资源。 系统使用信息的完整性取决于工作负载覆盖范围的完整性。

  • 操作系统的性能和安全性可以通过以下工具进行分析:perfstress-ngpaxtest

  • 一旦我们发现并理解了工作负载的需求,我们就可以专注于它们,以避免回归,并用它来评估安全注意事项。

方法论

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 install build-essential flex bison yacc
sudo apt install libelf-dev systemtap-sdt-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 stressor 工作负载

stress-ng 用于对内核执行压力测试。 它允许您使用“stressor-s”来锻炼计算机的各种物理子系统以及操作系统内核的接口。 它们适用于 CPU、CPU 缓存、设备、I/O、中断、文件系统、内存、网络、操作系统、管道、调度程序和虚拟机。 请参阅 stress-ng 手册页,以查找所有可用 stressor-s 的说明。 netdev stressor 启动指定数量 (N) 的工作线程,这些工作线程在所有可用的网络设备上执行各种 netdevice 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 用于对内核执行压力测试。 它允许您使用 stressor-s 来锻炼计算机的各种物理子系统以及操作系统内核的接口。 它们适用于 CPU、CPU 缓存、设备、I/O、中断、文件系统、内存、网络、操作系统、管道、调度程序和虚拟机。

netdev stressor 启动 N 个工作线程,这些工作线程在所有可用的网络设备上执行各种 netdevice ioctl 命令。 执行以下 ioctl

  • SIOCGIFCONF, SIOCGIFINDEX, SIOCGIFNAME, SIOCGIFFLAGS

  • SIOCGIFADDR, SIOCGIFNETMASK, SIOCGIFMETRIC, SIOCGIFMTU

  • SIOCGIFHWADDR, SIOCGIFMAP, SIOCGIFTXQLEN

以下命令运行 stressor

stress-ng --netdev 1 -t 60 --metrics command.

我们可以使用 perf record 命令来记录与进程相关的事件和信息。 此命令将分析数据记录在同一目录中的 perf.data 文件中。

使用以下命令,您可以记录与 netdev stressor 相关的事件、查看生成的报告 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 子系统。

系统调用

# calls

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

信号

sys_rt_sigprocmask()

rt_sigaction

36

信号

sys_rt_sigaction()

rt_sigreturn

2

信号

sys_rt_sigreturn()

wait4

889

时间

sys_wait4()

clock_nanosleep

37

时间

sys_clock_nanosleep()

capget

4

能力

sys_capget()

跟踪 stress-ng netdev stressor 工作负载

运行以下命令来跟踪 stress-ng netdev stressor 工作负载

strace -c  stress-ng --netdev 1 -t 60 --metrics

工作负载发出的系统调用

下表显示了工作负载调用的系统调用、每个系统调用的调用次数以及相应的 Linux 子系统。

系统调用

# calls

Linux 子系统

系统调用 (API)

openat

74

文件系统

sys_openat()

close

75

文件系统

sys_close()

read

58

文件系统

sys_read()

fstat

20

文件系统

sys_fstat()

flock

10

文件系统

sys_flock()

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

信号

sys_rt_sigaction()

rt_sigprocmask

1

信号

sys_rt_sigprocmask()

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 子系统。

系统调用

# calls

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

信号

sys_rt_sigaction()

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 收集有关工作负载使用的资源的细粒度信息。

参考文献