L B T

记 录 过 去 的 经 验

BPF(Berkeley Packet Filter) 是由 Berkeley 学院于 1992 年开发的一款,主要用于提高抓包工具的性能。并于 2014 年被重写进 Linux 内核中,可以用于网络、性能观测、安全等方面。 [1]

BPF 是一种灵活且高效的技术实现,主要由 指令集(Instruction Set)存储对象(Storage Objects maps)帮助函数(Helper Functions) 构成。因为其 虚拟指令集规范(Virtual Instruction Set Specification) ,可以认为 BPF 是一个虚拟机,BPF 运行于 内核模式(Kernel Mode) 。BPF 基于事件(Events)运行: Socket EventsTracepointsUSDT Probeskprobesuprobesperf_events.

bpftrace

bpftrace 是一个基于 BPF 的追踪工具(Trace Tools),提供了高级别的编程能力,同时包含了命令行和脚本使用方式

bpftrace 命令行

bpftrace 命令详细帮助文档请查看 man bpftrace,下表列出常用选项和参数

选项 说明 示例
-l [SEARCH] 列出匹配的事件(Event/Probe),没有 SEARCH 表达式则列出所有的 Event。SEARCH 支持通配符

筛选指定的事件

# bpftrace -l "tracepoint:*exec*"
tracepoint:libata:ata_exec_command
tracepoint:sched:sched_kthread_work_execute_end
tracepoint:sched:sched_kthread_work_execute_start
tracepoint:sched:sched_process_exec
tracepoint:syscalls:sys_enter_execve
tracepoint:syscalls:sys_enter_execveat
tracepoint:syscalls:sys_enter_kexec_file_load
tracepoint:syscalls:sys_enter_kexec_load
tracepoint:syscalls:sys_exit_execve
tracepoint:syscalls:sys_exit_execveat
tracepoint:syscalls:sys_exit_kexec_file_load
tracepoint:syscalls:sys_exit_kexec_load
tracepoint:workqueue:workqueue_execute_end
tracepoint:workqueue:workqueue_execute_start
tracepoint:writeback:writeback_exec

跟踪新启动的进程

执行以下命令,可以追踪系统中新启动的进程及其参数

# bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'
Attaching 1 probe...
/home/oneuser/cwrsync_6.2.4_x64_free/data/ops/rsync2Server.sh
/usr/bin/rsync --progress -a -c -u --timeout=300 -z --password-file /home/oneuser/cwrsync_6.2.4_x64_free/rsync.client.pswd --exclude .idea --exclude rsync2Server.sh /home/oneuser/cwrsync_6.2.4_x64_free/data/ops/ rsync@66.6.9.25::backup
/home/oneuser/cwrsync_6.2.4_x64_free/data/ops/rsync2Server.sh
/usr/bin/rsync --progress -a -c -u --timeout=300 -z --password-file /home/oneuser/cwrsync_6.2.4_x64_free/rsync.client.pswd --exclude .idea --exclude rsync2Server.sh /home/oneuser/cwrsync_6.2.4_x64_free/data/ops/ rsync@66.6.9.25::backup
runc --root /var/run/docker/runtime-runc/moby --log /run/containerd/io.containerd.runtime.v2.task/moby/53daccf2e93fcf83831ce6773a495361afe9bb9694881269d1902f399f5c621c/log.json --log-format json --systemd-cgroup exec --process /tmp/runc-process3178774999 --detach --pid-file /run/containerd/io.containerd.runtime.v2.task/moby/53daccf2e93fcf83831ce6773a495361afe9bb9694881269d1902f399f5c621c/c73d8d4d46ac3995bdc1d7648a3562ddb4715a549a509f6125a7cfc4e88abe50.pid 53daccf2e93fcf83831ce6773a495361afe9bb9694881269d1902f399f5c621c
runc init
/watchtower --health-check
/home/oneuser/cwrsync_6.2.4_x64_free/data/ops/rsync2Server.sh
/usr/bin/rsync --progress -a -c -u --timeout=300 -z --password-file /home/oneuser/cwrsync_6.2.4_x64_free/rsync.client.pswd --exclude .idea --exclude rsync2Server.sh /home/oneuser/cwrsync_6.2.4_x64_free/data/ops/ rsync@66.6.9.25::backup
/usr/lib/x86_64-linux-gnu/dcv/dcvpamhelper --stdout -s dcv
/usr/libexec/sssd/krb5_child --debug-microseconds=0 --debug-timestamps=1 --debug-fd=28 --debug-level=0x0070 --chain-id=7187 --sss-creds-password --canonicalize --realm=OPSDEV666.COM --fast-ccache-gid=0 --fast-ccache-uid=0
/home/oneuser/cwrsync_6.2.4_x64_free/data/ops/rsync2Server.sh
/usr/bin/rsync --progress -a -c -u --timeout=300 -z --password-file /home/oneuser/cwrsync_6.2.4_x64_free/rsync.client.pswd --exclude .idea --exclude rsync2Server.sh /home/oneuser/cwrsync_6.2.4_x64_free/data/ops/ rsync@66.6.9.25::backup

参考链接

Systems Performance: Enterprise and the Cloud v2

脚注

perf 是 Linux 中标准的 Profiler,常用于 CPU Profiling、CPU flame Graphs、Syscall Tracing 等。

perf 命令行操作

perf 常用子命令,命令帮助及选项参数帮助文档可查看 man perfman perf-recordman perf-report

子命令 说明 示例
record 进行 Profile 操作并将结果写入 perf.data
report 读取 perf.data (created by perf record)并展示 Profile 内容
script 读取 perf.data (created by perf record)并展示堆栈内容
stat 显示 PMC(Performance Monitor Counter) 统计数据
list 列出系统支持的事件(Event)列表

perf record 命令常用选项

选项 说明 示例
-F, --freq= 指定 Profile 的频率,使用 max 以当前系统允许的最大频率(对应内核参数 kernel.perf_event_max_sample_rate)进行 Profile
通常建议 99 Hz
-a, --all-cpus 采集所有 CPU 的全局数据,默认操作
-g Kernel Space 和 User Space Stack Traces
-p, --pid= 对指定 PIDs (comma separated list) 进行采样
-e, --event= 针对 Event 进行采样追踪 perf record -F 99 -a -e sched:sched_process_exec

perf report 命令常用选项

选项 说明 示例
-n, --show-nr-samples 显示每个 Symbol 采样的数量
-s, --sort= 按照指定的(CSV)字段排序

perf 使用示例

记录使用 exec 启动的进程

通过监控系统调用事件,可以采样/追踪系统上符合条件的行为,比如以下命令追踪使用 exec 系统调用启动的进程

# perf record -F 99 -a -e sched:sched_process_exec

# perf report
Overhead Command Shared Object Symbol
40.48% runc [kernel.kallsyms] [k] exec_binprm
34.86% exe [kernel.kallsyms] [k] exec_binprm
24.57% rsync [kernel.kallsyms] [k] exec_binprm
0.06% rsync2Server.sh [kernel.kallsyms] [k] exec_binprm
0.03% watchtower [kernel.kallsyms] [k] exec_binprm

添加 -g 选项追踪详细的堆栈信息

# perf record -F 99 -a -e sched:sched_process_exec -g

# perf report
Children Self Command Shared Object Symbol
+ 59.92% 59.92% rsync [kernel.kallsyms] [k] exec_binprm
+ 59.92% 0.00% rsync ld-linux-x86-64.so.2 [.] _start
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] do_syscall_64
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] x64_sys_call
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] __x64_sys_execve
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] do_execveat_common.isra.0
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] bprm_execve
+ 59.92% 0.00% rsync [kernel.kallsyms] [k] bprm_execve.part.0
+ 40.02% 40.02% rsync2Server.sh [kernel.kallsyms] [k] exec_binprm
+ 40.02% 0.00% rsync2Server.sh ld-linux-x86-64.so.2 [.] _start
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] do_syscall_64
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] x64_sys_call
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] __x64_sys_execve
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] do_execveat_common.isra.0
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] bprm_execve
+ 40.02% 0.00% rsync2Server.sh [kernel.kallsyms] [k] bprm_execve.part.0
0.07% 0.07% sleep [kernel.kallsyms] [k] exec_binprm
0.07% 0.00% sleep ld-linux-x86-64.so.2 [.] _start
0.07% 0.00% sleep [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe
0.07% 0.00% sleep [kernel.kallsyms] [k] do_syscall_64
0.07% 0.00% sleep [kernel.kallsyms] [k] x64_sys_call
0.07% 0.00% sleep [kernel.kallsyms] [k] __x64_sys_execve
0.07% 0.00% sleep [kernel.kallsyms] [k] do_execveat_common.isra.0
0.07% 0.00% sleep [kernel.kallsyms] [k] bprm_execve
0.07% 0.00% sleep [kernel.kallsyms] [k] bprm_execve.part.0

perf 读取 PMC 统计数据

使用以下命令可以读取系统中的 PMC 统计数据,其中包含了 CPU 使用时钟、CPU Context Switch、CPU Migrations、Page-Faults 等统计信息

# perf stat -a -- sleep 10

Performance counter stats for 'system wide':

40,019.66 msec cpu-clock # 4.000 CPUs utilized
51,242 context-switches # 1.280 K/sec
4,912 cpu-migrations # 122.740 /sec
7,944 page-faults # 198.502 /sec
<not supported> cycles
<not supported> instructions
<not supported> branches
<not supported> branch-misses

10.004545496 seconds time elapsed

<not supported> 可能包括以下原因:

  • 缺乏硬件支持
    • 某些处理器可能不支持所需的硬件性能计数器(如 cyclesinstructions
    • 这可能出现在虚拟机环境中,尤其是未启用硬件性能监控功能的虚拟机。
  • 权限不足
    • 性能计数器访问可能需要更高权限,通常是 root 权限
  • 内核配置问题
    • perf 依赖于 Linux 内核的性能监控功能。如果内核未启用某些性能计数器,可能会导致 <not supported>
  • 禁用了特定的事件源
    • 某些环境中,特定性能监控功能可能被内核禁用(例如为了安全性)。

参考链接|Bibliography

Systems Performance: Enterprise and the Cloud v2

脚注

Grafana 是一款用 GO 语言开发的开源数据可视化工具,可以做数据监控和数据统计,带有告警功能。

基础概念

组织(Organization) 与用户(User)

Organization 相当于一个 Namespace,一个 Organization 完全独立于另一个 Organization,包括 datasourcedashboard 等,创建一个 Organization 就相当于打开了一个全新的视图,所有的 datasourcedashboard 等都需要重新创建。一个用户(User) 可以属于多个 Organization。

User 是 Grafana 里面的用户,用户可以有以下 角色

  • admin - 管理员权限,可以执行任何操作。
  • editor - p不可以创建用户不可以新增 Datasource可以创建 Dashboard**
  • viewer - 仅可以查看 Dashboard
  • read only editor - 允许用户修改 Dashboard,但是 不允许保存

数据源 Datasource

Grafana 中操作的数据集、可视化数据的来源

Dashboard

在 Dashboard 页面中,可以组织可视化数据图表。

  • Panel - 在一个 Dashboard 中,Panel 是最基本的可视化单元。通过 Panel 的 Query Editor 可以为每一个 Panel 添加查询的数据源以及数据查询方式。每一个 Panel 都是独立的,可以选择一种或者多种数据源进行查询。一个 Panel 中可以有多个 Query Editor 来汇聚多个可视化数据集
  • Row - 在 Dashboard 中,可以定义一个 Row,来组织和管理一组相关的 Panel

Variables

在 Dashboard 的设置页面中,有 Variables 页面,在其中可以为 Dashboard 配置变量,之后可以在 Panel 的 Query Editor 中使用这些预定义的变量。变量的值也可以是通过表达式获取的值。也可以在 Panel 的标题中使用变量

例如以下 Variables 配置

Node    label_values(kubernetes_io_hostname)

在 Dashboard 中定义了这些变量后,可以在 Panel 的 Query Editor 中使用,在 Query Editor 中使用了 Variables 中定义的变量后,在 Dashboard 的顶部下拉菜单中可以选择预定义的变量的值(需要在定义 Variables 时配置 Show on dashboardLabel and Value 以使在 Dashboard 顶部显示下拉菜单),Panel 中的 Query 表达式就会使用这些变量的值进行计算以及显示图表。

阅读全文 »

环境信息

  • centos7
  • python3
  • Django 4

ModuleNotFoundError: No module named ‘MySQLdb’

ModuleNotFoundError: No module named ‘MySQLdb’

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.

解决方法

pip3 install pymysql

编辑文件./python36/lib/python3.6/site-packages/django/db/backends/mysql/__init__.py, 输入以下内容

import pymysql 
pymysql.install_as_MySQLdb()
阅读全文 »

在 Python 中,可以使用 Prometheus Python Client Library 来输出 Metrics,供 Prometheus 采集。

安装 Prometheus 客户端库

pip install prometheus-client

以下是一个完整的示例,展示如何设置和暴露 Metrics:

from prometheus_client import start_http_server, Summary, Counter, Gauge, Histogram
import random
import time

# 定义 Metrics
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')
REQUEST_COUNT = Counter('request_count', 'Total number of requests')
IN_PROGRESS = Gauge('in_progress_requests', 'Number of requests in progress')
REQUEST_LATENCY = Histogram('request_latency_seconds', 'Histogram of request latency')

# 模拟一个请求处理函数
@REQUEST_TIME.time() # 使用 Summary 记录函数运行时间
def process_request():
REQUEST_COUNT.inc() # 记录请求次数
IN_PROGRESS.inc() # 增加正在进行的请求数

latency = random.random() # 模拟随机延迟
REQUEST_LATENCY.observe(latency) # 记录延迟
time.sleep(latency)

IN_PROGRESS.dec() # 减少正在进行的请求数

if __name__ == "__main__":
# 启动 HTTP Server,用于暴露 Metrics
start_http_server(8000) # 默认端口 8000
print("Prometheus metrics available at http://localhost:8000/metrics")

# 模拟请求处理
while True:
process_request()

Metrics 类型说明(对应 Prometheus 中相关的数据类型)

  • Summary : 用于记录事件的持续时间或大小(例如请求处理时间)。
  • Counter : 计数器,记录事件的总发生次数(例如请求总数)。
  • Gauge : 测量当前值,可以增加或减少(例如当前活跃请求数)。
  • Histogram : 创建直方图,记录数据分布(例如请求延迟分布)。

向 Prometheus 暴露 Metrics

start_http_server(8000) 会启动一个 HTTP 服务器,在 /metrics 路径下暴露指标数据。Prometheus 将通过该路径采集数据。

装饰器

使用 @REQUEST_TIME.time() 装饰器自动记录函数执行时间。

标签

可以在指标中添加 标签(Labels) 来提供多维度的指标。

REQUEST_COUNT.labels(method='GET').inc()
阅读全文 »

Program 是一个静态实体,只是存储在操作系统中的文件(集合)

Process 是操作系统上的活动(Active)实体,是 Program 由操作系统加载到内存并运行之后的实体。

Process 运行过程中需要操作系统为其分配各种资源,如 CPU、Memory、Files、IO 等来完成其运行。

如果一个 Program 被操作系统运行(启动)了多次,那么其产生的多个 Process 属于分割(单独)的实体。

Linux 系统中调度的进程/线程,通常被称为任务(Task)

Linux 中常见的进程状态如下表:

状态标识 状态名称 状态说明 示例
R task_running 进程处于运行或就绪状态
S task_interruptible
sleeping
可中断的睡眠状态
D task_uninterruptible 不可中断的睡眠状态
1. 它是一种睡眠状态,意味着处于此状态的进程不会消耗 CPU
2. 睡眠的原因是等待某些资源(比如锁或者磁盘 IO),这也是非常多 D 状态的进程都处在处理 IO 操作的原因
3. 是它不能被中断,这个要区别于 硬件中断 的中断,是指不希望在其获取到资源或者超时前被终止。因此他不会被信号唤醒,也就不会响应 kill -9 这类信号。这也是它跟 S(可中断睡眠)状态的区别
T task_stopped
task_traced
Traced
暂停状态或跟踪状态
Z task_dead
exit_zombie
zombie
退出状态,进程成为僵尸进程
X task_dead
exit_dead
退出状态,进程即将被销毁
I idle 空闲状态
+ 表示关联了前台操作(Foreground Operation),比如前台运行的进程

进程调度

Linux 中任务(Task)调度算法默认使用 CFS(Completely Fair Scheduler,内核版本 >= 2.6.23).

CFS 调度算法没有使用固定的进程(此处的进程可能是进程或者线程,Linux 中统称为 Task)优先级(Priority),而是根据进程的 nice value 为进程分配一定比例的(Proportional) CPU 计算时间。 [1]

nice 的取值从 -20 - +19,值越小,优先级越高,默认值为 0 。优先级高的进程会被分配到更高比例的 CPU 处理时间。

在 Linux 内核中,优先级分为两种:静态优先级和动态优先级。CFS 使用的是动态优先级,它是根据 nice 值计算出来的。

CFS Scheduler 不会直接设定 Priorities,而是为每个 Task 维护变量 vruntime(virtual runtime)(可以检查系统 /proc/<PID>/sched),这个值记录了每个 task 使用了多少 CPU 时间。

CFS 通过将进程的虚拟运行时间(vruntime)与其他进程的虚拟运行时间进行比较来决定调度优先级。权重越大,vruntime 增长越慢,意味着进程的优先级越高,能够更频繁地被调度。

假入一个任务有默认的 Priority(nice=0),那么他的 vruntime 和实际使用的 CPU 时间相同。例如一个 nice=0 的进程使用了 CPU 200ms,那么他的 vruntime=200ms低优先级(low priority,nice>0 的任务在 CPU 上运行了 200ms,那么他的 vruntime>200ms,相反的,一个 高优先级(high priority,nice<0 的任务在 CPU 上运行了 200ms,那么他的 vruntime<200ms基于此,CFS Scheduler 在选择下一个要执行的任务时,会选择 vruntime 最小的任务来运行。如果有高优先级的任务就绪(可执行),它会抢占(preemptive)正在执行的低优先级的任务。

假如 Linux 中有 2 个优先级一样的任务(nice=0),一个进程为 I/O-bound,另一个为 CPU-bound。通常情况下,在一个 CPU 周期内,I/O-bound 的任务会使用很少的 CPU 时间就会因等待 IO 而中断在 CPU 上的执行并进入 CPU 的等待调度队列(schedule queue),而 CPU-bound 的任务会用尽分配给它的整个 CPU 周期。执行一段时间之后, I/O-bound 的任务的 vruntime 的值会显著的小于 CPU-bound 的任务的 vruntime,导致 I/O-bound 的任务拥有比 CPU-bound 的任务更高的优先级,当 I/O-bound 的任务就绪(IO 返回数据)时,它会立即抢占 CPU 开始执行。

nice 只能针对单一的进程调整优先级,无法同时将优先级配置关联到相关进程,如 子进程或者同一个服务中的其他进程

修改 nice 的值

在 Linux 中,nice 值用于调整进程的优先级,数值范围从 -20最高优先级 )到 19最低优先级 )。较低的 nice 值表示进程有更高的优先级,会更频繁地获得 CPU 时间,而较高的 nice 值表示进程有较低的优先级。

你可以使用 nicerenice 命令来修改进程的 nice

普通用户(root 之外的用户)启动应用程序时默认只能配置初始 nice0-19

普通用户(root 之外的用户)修改正在运行的应用程序的 nice 值,只能改大,不能改小。比如进程启动时,其 nice10,拥有权限的普通用户只能将其 nice 值改为大于 10 的值。

nice 命令

nice 命令用于在启动进程时指定 nice 。如果不指定,默认 nice 值为 0

nice -n 10 command

renice 命令

renice 命令可以修改已经在运行的进程的 nice 值。需要提供进程 ID( PID )来指定要修改的进程。

renice <nice_value> -p <PID>

如果想要一次修改多个进程的 nice 值,可以传递多个 PID:

sudo renice 10 -p 1234 2345 3456

Linux 进程资源控制 cgroups

cgroups 可以将一个任务(task)标识(绑定)到一个特殊的 控制组(Control Group ,此任务启动的子进程可以继承父进程的 控制组(Control Group

控制组(Control Group 可以限制的资源类型如下:

Type Description
blkio Storage
限制到存储设备(如 Hard Disk、USB Drivers等)的请求
cpu CPU
限制 CPU 调度
cpuacct Process Accounting
上报 CPU 使用状态,可以用于统计客户端使用的处理量并进行收费
cpuset CPU Assignment
在多处理器的系统上,将 Task 分配到特定的处理器以及关联的内存
devices Device Access
限制 控制组(Control Group 中的 Task 对目标设备类型的使用
freezer Suspend/Resume
暂停/恢复 控制组(Control Group 中的 Task
memory Memory Usage
限制并报告 控制组(Control Group 中的 Task 使用的内存
net_cls Network Bandwidth
制并报告 控制组(Control Group 中的 Task 使用的网络流量,主要通过对网络流量打上 cgroups 相关标签实现
net_prio Network Traffic
控制 控制组(Control Group 中的网络流量的优先级
ns Namespaces
cgroups 分配到不同的 Namespaces,如此同一个 cgroups 中的进程只能看到本 Namespace 中的进程

Linux 进程管理

进程命令名和进程可执行文件名

在系统中遇到以下进程:

# ps -elf | grep 18686
5 S root 18686 1239 0 80 0 - 46620 pipe_w 15:50 ? 00:00:00 /usr/sbin/CROND -n
0 R root 18694 18686 7 80 0 - 610547 - 15:50 ? 00:00:02 /usr/local/php73/bin/php /home/www/admin/artisan PullData
0 S root 18754 18686 0 80 0 - 22453 pipe_w 15:50 ? 00:00:00 /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t -f root

其中 PID 为 18686 的进程名为 /usr/sbin/CROND,其启动了另外两个子进程。但是在系统中检查,并不存在路径 /usr/sbin/CROND

# ls -l /usr/sbin/CROND
ls: cannot access /usr/sbin/CROND: No such file or directory

出现此种现象,主要是因为 在启动时,进程的命令名是根据路径传递给 execve() 函数的参数决定的,而不是直接与系统中的文件进行匹配

在 Linux 系统中,ps 命令显示的进程信息是从 /proc 文件系统中获取的,而 /proc 文件系统包含有关正在运行的进程的信息,包括每个进程的命令名。因此,即使实际上系统中不存在 /usr/sbin/CROND 文件,但如果进程的命令名是 /usr/sbin/CROND,那么 ps 命令仍然会显示进程的命令名为 /usr/sbin/CROND

进程的命令名可以查看 /proc/<PID>/cmdline 文件,本示例中显示如下:

# cat /proc/18686/cmdline 
/usr/sbin/CROND-n

对应的系统上的可执行文件的名称可以查看 /proc/<PID>/stat/proc/<PID>/comm/proc/<PID>/status 等文件

# cat /proc/900/comm 
crond

# cat /proc/900/status
Name: crond
Umask: 0022
State: S (sleeping)
Tgid: 900
Ngid: 0
Pid: 900
PPid: 1239
TracerPid: 0

# cat /proc/900/stat
900 (crond) S 1239 1239 1239 0 -1 4202816 1627 0 0 0 0 0 0 0 20 0 1 0 139129633 190955520 1478 18446744073709551615 94685936058368 94685936118156 140733000396032 140733000262488 140427856103840 0 0 4096 16387 18446744071797306256 0 0 17 3 0 0 0 0 0 94685938219080 94685938221648 94685948321792 140733000400770 140733000400789 140733000400789 140733000400872 0

在本示例中,实际执行的命令为 crond

进程状态检查

top 命令

使用 top 命令可以查看系统负载、CPU 和 内存使用情况。也可以查看单个进程的具体信息。

htoptop 命令的一个变种,它提供了更多的交互性、定制性以及其他一些功能,但是它同时使用了相比 top 更多的资源(4 倍多的 syscalls

top 命令常用选项

选项 说明 示例
-H Threads Mode,线程模式。默认情况 top 展示进程的简要信息,使用此选项显示进程中的线程状态。
对应交互式命令 H

top 常用交互命令

命令 说明
P CPU Utilization 排序,默认排序方式
M Memory Utilization 排序
I Irix/Solaris-Mode 切换。
默认为 Itrix Mode,在这种模式下,如果某个进程使用了系统中的 2 个 CPU 的所有计算资源,则其 CPU 使用率展示为 200%,依此类推。
在 Solaris Mode 下,进程的 CPU 使用率是整体 CPU 使用的资源除于 CPU 数。如在 4 CPU 的系统中,Itrix 模式下,进程 CPU 使用率为 200%,在 Solaris 模式下,则为 50%
  • 显示单个进程的(线程)详细信息
    # top -H -p 1423
    top - 09:44:42 up 54 days, 23:53, 2 users, load average: 8.82, 6.84, 7.21
    Threads: 15 total, 0 running, 15 sleeping, 0 stopped, 0 zombie
    %Cpu(s): 40.9 us, 10.8 sy, 0.0 ni, 48.1 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
    KiB Mem : 15790488 total, 466056 free, 7761544 used, 7562888 buff/cache
    KiB Swap: 0 total, 0 free, 0 used. 3895716 avail Mem

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    1423 root 20 0 1477368 778788 4260 S 39.5 4.9 11999:41 [watchdog:1:6]
    2572 root 20 0 1477368 778788 4260 S 37.9 4.9 11363:48 [watchdog:1:6]
    1436 root 20 0 1477368 778788 4260 S 34.2 4.9 11286:08 [watchdog:1:6]
    1435 root 20 0 1477368 778788 4260 S 33.9 4.9 12059:53 [watchdog:1:6]
    1434 root 20 0 1477368 778788 4260 S 33.2 4.9 10249:00 [watchdog:1:6]
    1437 root 20 0 1477368 778788 4260 S 30.6 4.9 11717:47 [watchdog:1:6]
    1431 root 20 0 1477368 778788 4260 S 28.9 4.9 11222:06 [watchdog:1:6]
    21378 root 20 0 1477368 778788 4260 S 27.6 4.9 12143:35 [watchdog:1:6]
    1433 root 20 0 1477368 778788 4260 S 17.6 4.9 8738:21 [watchdog:1:6]
    1428 root 20 0 1477368 778788 4260 S 8.0 4.9 7650:56 [watchdog:1:6]
    1429 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.04 [watchdog:1:6]
    1430 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.05 [watchdog:1:6]
    1432 root 20 0 1477368 778788 4260 S 0.0 4.9 0:00.03 [watchdog:1:6]
    1438 root 20 0 1477368 778788 4260 S 0.0 4.9 12260:30 [watchdog:1:6]
    1529 root 20 0 1477368 778788 4260 S 0.0 4.9 11068:39 [watchdog:1:6]

pidstat

pidstat 命令用于检查 Linux 内核管理的进程的状态。帮助文档请查看 man pidstat。要查看特定的进程的信息,使用 -p [PID|ALL] 选项,查看子进程相关信息,参考 -T [TASK | CHILD | ALL] 选项

命令常用选项

选项 说明 示例
-d 报告 I/O statistics
-C comm 查看 command name 的详细信息, comm 可以是正则表达式 pidstat -C chrome -T CHILD 1 10
-p { pid[,...] / SELF / ALL } 查看指定的 PID 或者所有(ALL) 进程的统计信息,不指定默认使用 -p ALL
-e program args 使用指定的 args 运行 program 并使用 pidstat 监控其统计数据
-l 在统计输出中,包含详细的命令及其参数
-r 统计进程的 faults 和 Memory 使用情况
-T { TASK / CHILD / ALL }
-t
查看子进程相关统计信息
-u CPU 使用率
-w 统计进程上下文切换(Context Switch)信息

查看指定的 PID 以及关联的子进程的统计信息

# pidstat -t -p 22737 1 1
Linux 3.10.0-1160.49.1.el7.x86_64 (ecs-34a8) 11/26/2024 _x86_64_ (2 CPU)

04:04:52 PM UID TGID TID %usr %system %guest %CPU CPU Command
04:04:53 PM 0 22737 - 0.00 0.00 0.00 0.00 0 python3
04:04:53 PM 0 - 22737 0.00 0.00 0.00 0.00 0 |__python3
04:04:53 PM 0 - 22805 0.00 0.00 0.00 0.00 1 |__python3
04:04:53 PM 0 - 22806 0.00 0.00 0.00 0.00 0 |__python3
04:04:53 PM 0 - 22807 0.00 0.00 0.00 0.00 1 |__python3
04:04:53 PM 0 - 22808 0.00 0.00 0.00 0.00 0 |__python3
04:04:53 PM 0 - 22809 0.00 0.00 0.00 0.00 0 |__python3
04:04:53 PM 0 - 22810 0.00 0.00 0.00 0.00 1 |__python3

Average: UID TGID TID %usr %system %guest %CPU CPU Command
Average: 0 22737 - 0.00 0.00 0.00 0.00 - python3
Average: 0 - 22737 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22759 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22806 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22807 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22808 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22809 0.00 0.00 0.00 0.00 - |__python3
Average: 0 - 22810 0.00 0.00 0.00 0.00 - |__python3

查看进程使用的内存以及 faults 统计信息

# pidstat -r | more
Linux 6.8.0-1017-aws (U-3TSDMAL9IVFAQ) 11/26/2024 _x86_64_ (4 CPU)

03:17:25 PM UID PID minflt/s majflt/s VSZ RSS %MEM Command
03:17:25 PM 0 1 0.97 0.00 168668 13552 0.08 systemd
03:17:25 PM 0 147 1.85 0.00 228472 158232 0.98 systemd-journal
03:17:25 PM 0 189 0.00 0.00 289100 27392 0.17 multipathd
03:17:25 PM 0 199 0.00 0.00 12160 6376 0.04 systemd-udevd
03:17:25 PM 118 512 0.00 0.00 14624 6400 0.04 systemd-oomd
03:17:25 PM 100 551 0.00 0.00 16044 7296 0.05 systemd-network
03:17:25 PM 101 553 0.00 0.00 26512 10240 0.06 systemd-resolve
03:17:25 PM 0 643 0.00 0.00 2816 1920 0.01 acpid
03:17:25 PM 123 644 0.00 0.00 19088 3840 0.02 avahi-daemon
03:17:25 PM 102 645 0.00 0.00 31628 7436 0.05 dbus-daemon
03:17:25 PM 0 646 0.00 0.00 278416 14852 0.09 NetworkManager
03:17:25 PM 0 649 0.01 0.00 757540 17788 0.11 euc-analytics-a
03:17:25 PM 0 653 0.00 0.00 82768 3840 0.02 irqbalance
03:17:25 PM 0 656 0.00 0.00 48880 10368 0.06 networkd-dispat

查看进程使用的 CPU 统计信息

# pidstat -u -l | more
Linux 6.8.0-1017-aws (U-3TSDMAL9IVFAQ) 11/26/2024 _x86_64_ (4 CPU)

04:08:08 PM UID PID %usr %system %guest %wait %CPU CPU Command
04:08:08 PM 0 1 0.04 0.02 0.00 0.02 0.06 1 /sbin/init
04:08:08 PM 0 2 0.00 0.00 0.00 0.00 0.00 0 kthreadd
04:08:08 PM 0 15 0.00 0.00 0.00 0.01 0.00 0 ksoftirqd/0
04:08:08 PM 0 49 0.00 0.00 0.00 0.00 0.00 2 khugepaged
04:08:08 PM 0 63 0.00 0.00 0.00 0.00 0.00 3 kworker/3:1H-kblockd
04:08:08 PM 0 64 0.00 0.00 0.00 0.00 0.00 3 kswapd0
04:08:08 PM 0 75 0.00 0.00 0.00 0.00 0.00 2 kworker/2:1H-kblockd
04:08:08 PM 0 100 0.00 0.00 0.00 0.00 0.00 0 jbd2/nvme0n1p1-8
04:08:08 PM 0 102 0.00 0.00 0.00 0.00 0.00 1 kworker/1:1H-kblockd
04:08:08 PM 0 129 0.00 0.00 0.00 0.00 0.00 0 kworker/0:1H-kblockd
04:08:08 PM 0 147 0.00 0.00 0.00 0.00 0.01 3 /lib/systemd/systemd-journald
04:08:08 PM 0 189 0.00 0.00 0.00 0.00 0.01 3 /sbin/multipathd -d -s
04:08:08 PM 0 199 0.00 0.00 0.00 0.00 0.00 3 /lib/systemd/systemd-udevd
04:08:08 PM 0 382 0.00 0.00 0.00 0.00 0.00 2 jbd2/nvme1n1p1-8
04:08:08 PM 118 512 0.05 0.05 0.00 0.00 0.10 3 /lib/systemd/systemd-oom
d

在使用 pidstat 时如果未指定统计数据的采样间隔及采样次数,则 默认统计的是从系统启动以来的平均资源使用率,如果目标进程的运行时间相对于系统启动时间很短(例如仅几分钟),那么它的平均 CPU 使用率会趋近于 0 。比如以下示例:

未指定采样间隔及采样次数 ,统计结果显示进程的 CPU 使用率为 0

# pidstat  | grep gzip
01:37:06 PM 0 784 0.00 0.00 0.00 0.00 4 gzip

以下命令指定采样间隔和采样次数 ,统计结果显示 CPU 使用率较高

# pidstat 1 1 | grep gzip
01:37:14 PM 0 1974 15.69 1.96 0.00 87.65 7 gzip
Average: 0 1974 15.69 1.96 0.00 87.65 - gzip

参考链接|Bibliography

Operating System Concepts v10 Online
Linux进程状态说明

脚注


  1. 1.Operating System Concepts v10 Online 5.7.1 Example: Linux Scheduling

批量下载

Python3 批量下载文件中给定的 urls

假如需要下载的 urls 存在于给定的文件中(每行一个 url),本示例演示批量并发下载,假设 urls 存在于文件 urls.txt

import concurrent.futures
import requests

# 创建一个函数来下载图片
def download_image(url):
try:
response = requests.get(url)
response.raise_for_status() # 检查是否有 HTTP 错误
print(f"下载完成: {url}")
except requests.exceptions.RequestException as e:
print(f"下载失败: {url}, 错误: {e}")


# 从文件中读取图片链接
with open("urls.txt", "r") as file:
img_urls = file.read().splitlines()

# 使用 ThreadPoolExecutor 来限制并发线程数量为 10
max_concurrent_threads = 10
with concurrent.futures.ThreadPoolExecutor(max_concurrent_threads) as executor:
# 提交任务并下载图片
executor.map(download_image, img_urls)

print("所有图片下载完成")

shell 批量下载文件中给定的 urls

假如需要下载的 urls 存在于给定的文件中(每行一个 url),本示例演示批量并发下载,假设 urls 存在于文件 urls.txt

本示例中的 urls.txt 内容示例如下:

urls.txt
http://cdn-log-customer-bj4.obs.cn-north-4.myhuaweicloud.com:80/oversea/20241202/09/2024120209-domain-ov.gz?AccessKeyId=WWHEIFLWKIMHHDKIPRWLJJ40CFS&Expires=1733208754&response-content-disposition=attachment%3Bfilename%3D%222024120209-domain-ov.gz%22&Signature=bGHy1CPncaXJladgG9NdIRwASsdgSIvs%3D
http://cdn-log-customer-bj4.obs.cn-north-4.myhuaweicloud.com:80/mainland/20241202/09/2024120209-domain-cn.gz?AccessKeyId=WWHEIFLWKIMHHDKIPRWLJJ40CFS&Expires=1733208754&response-content-disposition=attachment%3Bfilename%3D%222024120209-domain-cn.gz%22&Signature=bGHy1CPncaXJladgG9NdIRwASsdgSIvs%3D
http://cdn-log-customer-bj4.obs.cn-north-4.myhuaweicloud.com:80/oversea/20241202/08/2024120208-domain-ov.gz?AccessKeyId=WWHEIFLWKIMHHDKIPRWLJJ40CFS&Expires=1733208754&response-content-disposition=attachment%3Bfilename%3D%222024120208-domain-ov.gz%22&Signature=ZT64%bGHy1CPncaXJladgG9NdIRwASsdgSIvs%3D
http://cdn-log-customer-bj4.obs.cn-north-4.myhuaweicloud.com:80/mainland/20241202/08/2024120208-domain-cn.gz?AccessKeyId=WWHEIFLWKIMHHDKIPRWLJJ40CFS&Expires=1733208754&response-content-disposition=attachment%3Bfilename%3D%222024120208-domain-cn.gz%22&Signature=bGHy1CPncaXJladgG9NdIRwASsdgSIvs%3D

简单的 串行下载命令 如下,从 url 中取出文件名作为下载文件名:

for i in `cat urls.txt`; do fn=`echo $i | cut -d'?' -f1  | cut -d'/' -f7` ; curl -o $fn "$i"; done

使用以上命令,如果下载内容太多,会比较慢,以下代码示例使用 xargs 命令批量下载,xargs 会自动分配任务,确保多个下载任务同时运行 。:

cat log-url | xargs -P 10 -I {} sh -c 'fn=$(echo "{}" | cut -d"?" -f1 | cut -d"/" -f7);  curl -o $fn "{}"'

xargs 参数说明:

  • -P 10 : 指定并发进程数为 10,可以根据系统资源调整。
  • -I {} : 替换(Pipe)输入中的每行(URL) 为 {}

如果无需提取文件名,可以 使用 wget 命令的并行下载功能

wget -i urls.txt -P output-dir -nc --max-threads=10

参数说明如下:

  • -i urls.txt : 从文件中读取下载 URL。
  • -P output-dir : 将下载文件存储到 output-dir
  • --max-threads=10 : 设置并发线程数为 10。
  • -nc : 跳过已下载的文件,避免重复下载。

CPU Architecture and Common Concepts

CPU Architecture

下图展示了一个简单的 CPU 架构图,有一个物理 CPU(Physical Processor),包含 4 个 CPU Cores,每个 CPU Core 包含 2 个 Hardware Threads,总计 8 个 CPUs。右侧的图是这 8 个 CPUs 在操作系统(Operating System)中的视图(也被称为 Logical CPU/Virtual Processor/Virtual Core) [1]

Operating System 可能对于 CPU 的拓扑结构(Topology)有一定程度的了解,如知道哪些 Logical CPU 位于同一个 CPU Core 或者 CPU Cache 是如何被共享的(Shared),这有助于 CPU Scheduler 做出更优的调度决策。

下图展示了通用 2 Core 处理器的组成。具体的组成取决于具体的处理器。

  • Control Unit 是 CPU 处理器的核心组件,负责指令(Instruction)的 FetchDecodingExecution 并负责保存执行结果(Storing Results)。
  • Shared Floating-Points Unit
  • Shared Level-3 Cache
  • MMU Memory Management Unit,负责将虚拟内存地址转换为物理内存地址(Virtual-to-Physical Address Translation)
  • TLB Translation Lookaside Buffer,用于 Cache 内存地址转换(Virtual-to-Physical Address Translation),未缓存的地址转换需要去 Main Memory 中的 Page Tables 中查询,MMU 的基本构成如下图

P-States and C-States

Intel 处理器的 ACPI(Advanced Configuration and Power Interface)标准定义了 P-States (Processor Performance States)S-States (Processor Power States)[4]

  • P-States 通过在 Processor 运行过程中提供不同的 Clock Rate 提供不同的性能选择。 P0 提供最高级别的性能,P1PN 提供较慢的 Clock Rate,这些级别通常可以被硬件(如依赖处理器温度)或者系统上的软件(如内核的节能策略)控制。
  • C-States 提供当 CPU 处于 Idle 时,提供不同的 idle states,用于节省电力(节能),下图列出了常见的 C-States

CPU Memory Caches

Processors(物理 CPU 处理器)提供了各种个样的硬件缓存(Hardware Caches)用于提高 CPU 读写 Memory I/O 的性能(CPU 的处理速度是 Memory I/O 处理速度的多个量级),下图展示了 Processor Caches 在大小(Sizes)和响应速度之间的关系, 离 Processor 越近,其响应速度越快,存储空间越小,成本越高(贵)

Clock Rate

CPU 时钟频率(Clock Rate) 是指 Processor 每秒跳动(转换)的次数,每一个 Processor 的时钟变化也称为一个 Clock Cycle 。 Processor 通常是以特定的时钟频率运行,比如 4GHz 的 CPU 每秒会进行 4 Billion Clock Cycles。每个 CPU 指令(Instruction)都需要通过一个或者多个 Clock Cycles 来完成。 现代 CPU 通常会变频,即调整 Processor 的 Clock Rate,如果提高 CLock Rate 以提升性能,降低 Clock Rate 以节能 。变频操作通常是通过 OS 请求 Processor 变频或者是 Processor 自动进行变频,比如内核中的 idle 线程通常会要求 CPU 降低其 Clock Rate 以节能。

Clock Rate 是衡量 Processor 能力(Capacity)的一个主要指标。通常来说,较高 Clock Rate 的 Processor 会有更高的性能,但是在性能分析的场景中,提高 CPU 的 Clock Rate 不一定会提高 CPU 性能,这主要取决于 CPU 的 Clock Cycles 到底是在忙于什么,如果 Clock Cycles 主要忙于等待 Memory Access,那么换成更高频率的处理器,并不会提高整个系统的性能。

SMT

Simultaneous Multi-Threading(SMT) 是一种由 Processor 支持的硬件多线程(Hardware Multithreading)技术,用于实现同一个 Processor Core 上的并发(Parallelism),它允许一个 CPU Core 运行多个 Thread,每个 Hardware Thread 从 Operating System 层面来看都是一个 CPU。这种技术的典型应用包括 Intel 的 Hyper-Threading 技术,允许每个 Processor Core 运行 2 个 Threads,以及 POWER8,允许每个 Processor Core 运行 8 个 Threads。

SMT 的实现通常基于 Core 运行指令(Instructions)的过程中的 Stall Cycles,当某个指令处于 Stall Cycles 时,Core 会允许调度另一个指令来运行。基于 Hardware Thread 的 CPU,其性能和单独的 CPU Core 是有区别的,这个差别取决于其上的工作负载(Workload),Stall Cycles 严重的 Workloads 会比 Instruction Cycles 严重的 Workloads 性能更好,因为 Stall Cycles 会减少竞争(Core Contention)

阅读全文 »

性能分析相关的术语

以下为性能分析中常用到的专业术语或指标 [2]

  • IOPS
    Input/Output Operations Per Second. 用于衡量数据传输操作(Rate of Data transfer operations)的频率。对于硬盘 I/O (Disk I/O)来说,指 每秒发生的读写请求

  • Throughput
    吞吐量 。在网络通信中,主要指 数据传输速率(Data Rate, bytes/bits per second) 。在其他上下文(Contexts,如 Databases),Throughput 通常指 Operation Rate(Operations Per Second or Transactions Per Second)

  • Response Time
    响应时间 。一个操作执行到结束的时间。这通常包括 请求等待时间(Waiting Time)被服务时间(Serviced Time)传输时间(Transfer Time)

  • Latency
    延迟 。可以指某个操作(Operation)消耗在 等待被处理/服务(Time Waiting to be Serviced/Processed)的时间 。在某些上下文中,等同于 Response Time

  • Utilization
    使用率 。衡量资源的忙的程度。

  • Saturation
    衡量一个资源(如 CPU)的待处理队列中未处理的任务数量。

  • Bottleneck

  • Workload
    一般是客户端请求

  • Cache

  • SUT
    System Under Test 。性能测试目标

  • Off-CPU
    Off-CPU 指的是当前不在 CPU 上运行的程序的一种状态。比如在性能分析过程中,需要分析当前不在 CPU 上运行的 Process/Threads 所处的状态,通常包括导致 Task 被 Block 的原因: Disk I/ONetwork I/OLock ContentionExplicit SleepsScheduler Preemption

Thread State Analysis

在进行性能分析的过程中,特别是 Application Performance Analysis,首先要清楚 Process/Thread/Task 所处的当前状态,至少可以将其划分为 2 个状态: [8]

  • On-CPU
  • Off-CPU

更详细的状态划分可以参考以下状态:

  • User : On-CPU ,用户模式(User Mode)
  • Kernel : On-CPU,Kernel 模式
  • Runnable : Off-CPU,正在等待调度到 CPU(等待变为 On-CPU)
  • Swapping : Runnable,但是被 Page-In 阻塞(Blocked)
  • Disk I/O : 等待 Block Device I/O
  • Network I/O : 等待网络 I/O,如 Sockets 读/写
  • Sleeping : 自愿睡眠状态(Voluntary Sleep)
  • Lock : 等待获取锁
  • Idle : 等待工作(Waiting for work)

Observability Tools

Linux 系统各个部分相关的监控工具如下图: [3]

Linux Static Performance Tuning Tools:

阅读全文 »

环境信息

  • Centos 7 kernel 5.4.221

启动过程

systemd 管理的系统中,提供了工具 systemd-analyze 用于分析具体的启动过程,使用 systemd-analyze --help 查看使用帮助

检查系统启动时间

使用 systemd-analyze 命令会显示系统启动所用的时间,等同于 systemd-analyze time

# systemd-analyze 
Startup finished in 1.830s (kernel) + 36.827s (userspace) = 38.657s
graphical.target reached after 12.604s in userspace

systemd-analyze blame 列出系统上各个 Unit 启动的时间

# systemd-analyze blame
6.414s wazuh-agent.service
3.161s dracut-initqueue.service
2.473s network.service
1.004s watchdog.service
...
45ms sysstat.service
14ms plymouth-switch-root.service
14ms systemd-journald.service
4ms systemd-logind.service
3ms sys-kernel-config.mount
3ms initrd-udevadm-cleanup-db.service
3ms systemd-random-seed.service
2ms google-shutdown-scripts.service

列出系统各个 Unit 启动消耗的时间

# systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.

graphical.target @12.604s
└─multi-user.target @12.601s
└─skylight-agent.service @36.639s
└─network.target @9.073s
└─NetworkManager.service @8.795s +275ms
└─dbus.service @8.788s
└─basic.target @8.774s
└─sockets.target @8.772s
└─snapd.socket @8.766s +5ms
└─sysinit.target @8.669s
└─cloud-init.service @6.850s +1.811s
└─systemd-networkd-wait-online.service @4.970s +1.871s
└─systemd-networkd.service @4.864s +91ms
└─network-pre.target @4.850s
└─cloud-init-local.service @3.228s +1.620s
└─systemd-remount-fs.service @1.113s +93ms
└─systemd-fsck-root.service @1.030s +69ms
└─systemd-journald.socket @853ms
└─-.mount @692ms
└─-.slice @692ms

查看内存信息

内存相关概念说明:

  • VSS ,Virtual Set Size , VIRT - 虚拟耗用内存(包含共享库占用的内存), 通常 VIRT 是系统承诺分配给应用的内存,不是实际使用的内存
  • RSS , Resident Set Size , RES - 实际使用物理内存(包含共享库占用的内存)
  • PSS , Proportional Set Size - 实际使用的物理内存(比例分配共享库占用的内存)。 top 命令中的 SHR 列展示的就是共享库按比例分配给进程的内存
  • USS , Unique Set Size - 进程独自占用的物理内存(不包含共享库占用的内存)

系统内存使用量统计

free

$ free -h
total used free shared buff/cache available
Mem: 15Gi 7.8Gi 707Mi 449Mi 7.0Gi 6.9Gi
Swap: 30Gi 1.0Gi 29Gi

ps

例如查看使用内存排名前十的进程:

ps aux | sort -k4,4nr | head -n 10

sar 命令

使用 sar 命令检查系统上的内存及 Swap 使用情况

查看某个进程使用的内存量

比如检查 docker 使用的内存量,首先通过 ps 命令查询到 docker 的 PID 信息

$ ps -elf | grep docker
4 S root 1243 1 4 80 0 - 1067527 futex_ Jan03 ? 15:14:45 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

例如此处的 docker 进程的 PID 为 1243

  • 使用 top 命令动态查看 docker 使用的内存信息

    $ top -p 1243
    top - 11:47:40 up 14 days, 2:09, 3 users, load average: 0.65, 1.42, 1.70
    Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
    %Cpu(s): 1.0 us, 0.6 sy, 0.0 ni, 98.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
    KiB Mem : 32068748 total, 2494500 free, 18536188 used, 11038060 buff/cache
    KiB Swap: 0 total, 0 free, 0 used. 9586340 avail Mem

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    1243 root 20 0 4270108 1.4g 53956 S 1.0 4.6 914:55.80 dockerd
  • 使用 ps aux 命令查看内存使用量

    $ ps aux | grep 1243
    root 1243 4.5 4.6 4270108 1486460 ? Ssl Jan03 914:57 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

    输出结果中

    • 第 3、4 列 (4.5 4.6) 分别表示 cpu 使用率内存使用率
    • 第 5、6 列 (4270108 1486460) 分别表示 虚拟内存使用量物理内存使用量,单位为 k
  • 通过进程的 status 文件查看内存使用

    $ cat /proc/1243/status
    Name: dockerd
    Umask: 0022
    State: S (sleeping)
    Pid: 1243
    PPid: 1
    VmPeak: 4270364 kB
    VmSize: 4270108 kB
    VmLck: 0 kB
    VmPin: 0 kB
    VmHWM: 1562204 kB
    VmRSS: 1492340 kB
    ...

    其中,VmRSS 为进程使用的物理内存

  • 使用 pmap 命令查看进程使用的内存信息

    pmap -x 1243
    pmap -p 1243
  • 使用 pidstat 命令查看进程使用的内存信息

    # pidstat -r -t -p 1424681 1 1
    Linux 6.8.0-1017-aws (U-3TSDMAL9IVFAQ) 11/26/2024 _x86_64_ (4 CPU)

    04:15:40 PM UID TGID TID minflt/s majflt/s VSZ RSS %MEM Command
    04:15:41 PM 408001114 1424681 - 0.00 0.00 34656400 256620 1.59 chrome
    04:15:41 PM 408001114 - 1424681 0.00 0.00 34656400 256620 1.59 |__chrome
    04:15:41 PM 408001114 - 1424696 0.00 0.00 34656400 256620 1.59 |__sandbox_ipc_thr
    04:15:41 PM 408001114 - 1424702 0.00 0.00 34656400 256620 1.59 |__chrome
    04:15:41 PM 408001114 - 1424703 0.00 0.00 34656400 256620 1.59 |__HangWatcher
    04:15:41 PM 408001114 - 1424704 0.00 0.00 34656400 256620 1.59 |__ThreadPoolServi
    04:15:41 PM 408001114 - 1424705 0.00 0.00 34656400 256620 1.59 |__ThreadPoolForeg
    04:15:41 PM 408001114 - 1424706 0.00 0.00 34656400 256620 1.59 |__ThreadPoolForeg

    阅读全文 »

本文档中的内容主要源于以下书籍:

memory

Main Memory

Main Memory 通常是 CPU 可以直接定位和访问的唯一的大存储设备。如果 CPU 要处理磁盘上的数据,数据必须首先被传输到 Main Memory,指令要能被 CPU 执行,也必须首先载入内存中。

Program 要能被运行,首先必须载入到内存中,并提供内存绝对地址给 CPU 以供加载指令和数据。

Logical Memory Address and Virtual Memory Address

通常情况下,CPU 生成的内存地址被称为 Logical Memory Address,也称为 Virtual Memory Address

Memory-address 注册器(memory-address register)加载的地址通常称为 Physical Memory Address

程序运行过程中,CPU 操作的是 Virtual Memory Address,需要由硬件设备 MMU(Memory Management Unit)负责将 Virtual Memory Address space 映射到对应的 Physical Memory Address space。程序或者是 CPU 运行过程中,不会直接操作(访问/access)物理内存地址空间。

阅读全文 »

进程的内存分层结构

进程的内存结构一般被分成多个 sections,包括

  • Text section - 存放程序代码(the executable code)
  • Data section - 存放全局变量(global variables)
  • Heap section - 程序运行过程中动态分配的内存
  • Stack section - 调用程序功能时的临时数据存储,如函数参数、返回地址、本地变量等。

下图展示了 C 程序(Program)在内存中的分层结构(layout of a C program in memory)

  • 其中, Data section 被分成了 2 部分,包括 (a) initialized data(b) uninitialized data

使用 GNU 工具 size 可以检查 Projram 在磁盘上的 内存布局。这些值在程序编译时确定,并不会在程序运行时变化,因此它们是固定不变的。

# size /usr/sbin/sshd
text data bss dec hex filename
817397 15460 37664 870521 d4879 /usr/sbin/sshd

输出信息中:

  • text : 代表 Text section 的大小
  • data : 初始化数据段(initialized data)的大小,包含已初始化的全局和静态变量。
  • bss : 未初始化数据段的大小,包含未初始化的全局和静态变量。
  • dec : 上述所有部分的总大小,以十进制表示。
  • hex : 上述所有部分的总大小,以十六进制表示。
阅读全文 »

systemd 是一种用于 Linux 操作系统的系统和服务管理器。它被广泛应用于许多现代 Linux 发行版中,如 CentOS、Fedora、Ubuntu 等。systemd 旨在替代传统的 SysVLSB init 系统,并提供更强大、灵活的系统启动和服务管理功能。

systemd 的关键概念和组件

  • Unit 文件

    • systemd 使用单元(Unit)文件来描述系统资源。常见的单元类型包括:
      • Service Unit (*.service) : 用于定义和管理服务。
      • Target Unit (*.target) : 用于分组和同步一组单元的启动,如 multi-user.target
      • Timer Unit (*.timer) : 用于定时任务,相当于 cron 的替代品。
      • Socket Unit (*.socket) :用于管理网络或 IPC 套接字。
      • Mount Unit (*.mount) : 用于定义挂载点。
      • Path Unit (*.path) : 监控文件或者目录的变化。当监控的文件或目录发生变化时,可以触发相应的 service 单元。
    • 单元(Unit)文件通常存储在以下目录
      • /etc/systemd/system/ :系统管理员定义的单位文件,优先级较高。
      • /lib/systemd/system/ :发行版提供的单位文件,优先级较低。
      • /run/systemd/system/ :运行时生成的单位文件,临时的。
  • Target 文件

    • systemd 使用 target 取代传统的运行级别(runlevel)。常见的目标包括:
      • multi-user.target : 相当于传统的运行级别 3,支持多用户、无图形界面。
      • graphical.target : 相当于传统的运行级别 5,支持多用户和图形界面。
      • rescue.target : 相当于传统的单用户模式,提供基本的系统恢复环境。
  • 日志管理

    • systemd 使用 `journald` 来管理系统日志。你可以使用 journalctl 命令查看日志
  • 服务间的依赖关系

    • systemd 处理服务间的依赖关系。你可以通过 After=Before=Requires= 等指令在单元文件中定义这些依赖。

systemd 的优势:

  • 并行启动systemd 可以并行启动服务,减少启动时间。
  • 依赖管理 : 能够自动处理服务间的依赖关系,保证系统按需启动服务。
  • 日志记录systemd 的日志管理功能强大,提供了统一的接口查看和分析日志。
  • 定时任务管理 : 通过 timer 单元文件可以灵活地配置定时任务,作为 cron 的替代方案。
  • 性能统计数据systemd 启动过程中记录了性能(如服务启动时间)相关的统计数据,可以使用命令 systemd-analyze 查看。
阅读全文 »

CPU 相关的 syscall 函数

Linux System Call 的帮助文档包含在内核文档中,可以通过 man 2 查看对应的手册,如 man 2 fork

system call function 说明 示例
fork 创建 Processes
Linux 中的 fork 是通过封装了 clone 函数实现的

内存相关的 syscall

system call function 说明 示例
shm open 使用 Shared Memory 方式的 IPC 进程使用此方法创建 shared memory,要相互通信的进程链接到此 FD(File Descripter) fd = shm open(name, O CREAT &#124; O RDWR, 0666);

环境信息

  • Centos 7
  • Prometheus 2.44.0
  • node_exporter-1.6.0

node_exporter 安装

安装 node_exporter 官方文档

以下步骤演示安装 node_exporter 并使用 systemd 管理服务

wget https://github.com/prometheus/node_exporter/releases/download/v1.6.0/node_exporter-1.6.0.linux-amd64.tar.gz

tar -xf node_exporter-1.6.0.linux-amd64.tar.gz

cp node_exporter-1.6.0.linux-amd64/node_exporter /usr/bin/

生成 systemd 服务配置文件 /usr/lib/systemd/system/node_exporter.service

/usr/lib/systemd/system/node_exporter.service
[Unit]
Description=node_exporter
After=syslog.target
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/node_exporter
Restart=always
RestartSec=10
StartLimitInterval=100

[Install]
WantedBy=multi-user.target

执行以下命令管理服务

$ systemctl daemon-reload

$ systemctl status node_exporter
● node_exporter.service - node_exporter
Loaded: loaded (/usr/lib/systemd/system/node_exporter.service; disabled; vendor preset: disabled)
Active: inactive (dead)

$ systemctl enable --now node_exporter
Created symlink from /etc/systemd/system/multi-user.target.wants/node_exporter.service to /usr/lib/systemd/system/node_exporter.service.

$ systemctl status node_exporter
● node_exporter.service - node_exporter
Loaded: loaded (/usr/lib/systemd/system/node_exporter.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2023-06-23 10:34:08 CST; 2s ago
Main PID: 28578 (node_exporter)
CGroup: /system.slice/node_exporter.service
└─28578 /usr/bin/node_exporter

node_exporter 配置信息

启动参数详解

参数 说明 示例
--web.listen-address=":9100" node_exporter 监听端口,默认 9100
--web.telemetry-path="/metrics" prometheus 获取 Metrics 访问的 url,默认 /metrics
--log.level="info" 日志级别
--log.format="logger:stderr" 日志打印格式
--collector.disable-defaults 关闭默认的采集项
--no-collector.${item} 关闭某一项默认开启的采集项 --no-collector.cpu
--collector.systemd.unit-include="(docker'|'sshd).service" 收集指定服务的指标

可以配置通过正则表达式屏蔽或者选择某些监控项 [1]

参数 说明 示例
--collector.diskstats.ignored-devices="^(ram'|'loop'|')\\d+$" 忽略某些磁盘的信息收集
--collector.filesystem.ignored-mount-points="^/(dev'|'proc'|')($'|'/)" 忽略某些文件系统挂载点的信息收集
--collector.filesystem.ignored-fs-types="^(autofs'|'proc)$" 忽略某些文件系统类型的信息收集
--collector.netclass.ignored-devices="^$" 忽略某些网络类的信息收集
--collector.netdev.ignored-devices="^$" 忽略某些网络设备的信息收集
--collector.netstat.fields="^$" 配置需要获取的网络状态信息
--collector.vmstat.fields="^(oom_kill'|'pgpg'|'pswp).*" 配置 vmstat 返回信息中需要收集的选项

功能对照表

默认开启的功能

默认开启的功能 [1]

名称 说明 系统
arp /proc/net/arp 中收集 ARP 统计信息 Linux
conntrack /proc/sys/net/netfilter/ 中收集 conntrack 统计信息 Linux
cpu 收集 cpu 统计信息 Darwin, Dragonfly, FreeBSD, Linux
diskstats /proc/diskstats 中收集磁盘 I/O 统计信息 Linux
edac 错误检测与纠正统计信息 Linux
entropy 可用内核熵信息 Linux
exec execution 统计信息 Dragonfly, FreeBSD
filefd /proc/sys/fs/file-nr 中收集文件描述符统计信息 Linux
filesystem 文件系统统计信息,例如磁盘已使用空间 Darwin, Dragonfly, FreeBSD, Linux, OpenBSD
hwmon /sys/class/hwmon/ 中收集监控器或传感器数据信息 Linux
infiniband 从 InfiniBand 配置中收集网络统计信息 Linux
loadavg 收集系统负载信息 Darwin, Dragonfly, FreeBSD, Linux, NetBSD, OpenBSD, Solaris
mdadm /proc/mdstat 中获取设备统计信息 Linux
meminfo 内存统计信息 Darwin, Dragonfly, FreeBSD, Linux
netdev 网口流量统计信息,单位 bytes Darwin, Dragonfly, FreeBSD, Linux, OpenBSD
netstat /proc/net/netstat 收集网络统计数据,等同于 netstat -s Linux
sockstat /proc/net/sockstat 中收集 socket 统计信息 Linux
stat /proc/stat 中收集各种统计信息,包含系统启动时间,forks, 中断等 Linux
textfile 通过 --collector.textfile.directory 参数指定本地文本收集路径,收集文本信息 any
time 系统当前时间 any
uname 通过 uname 系统调用, 获取系统信息 any
vmstat /proc/vmstat 中收集统计信息 Linux
wifi 收集 wifi 设备相关统计数据 Linux
xfs 收集 xfs 运行时统计信息 Linux (kernel 4.4+)
zfs 收集 zfs 性能统计信息 Linux

默认关闭的功能

默认关闭的功能 [1]

名称 说明 系统
bonding 收集系统配置以及激活的绑定网卡数量 Linux
buddyinfo /proc/buddyinfo 中收集内存碎片统计信息 Linux
devstat 收集设备统计信息 Dragonfly, FreeBSD
drbd 收集远程镜像块设备(DRBD)统计信息 Linux
interrupts 收集更具体的中断统计信息 Linux,OpenBSD
ipvs /proc/net/ip_vs 中收集 IPVS 状态信息,从 /proc/net/ip_vs_stats 获取统计信息 Linux
ksmd /sys/kernel/mm/ksm 中获取内核和系统统计信息 Linux
logind logind 中收集会话统计信息 Linux
meminfo_numa /proc/meminfo_numa 中收集内存统计信息 Linux
mountstats /proc/self/mountstat 中收集文件系统统计信息,包括 NFS 客户端统计信息 Linux
nfs /proc/net/rpc/nfs 中收集 NFS 统计信息,等同于 nfsstat -c Linux
qdisc 收集队列推定统计信息 Linux
runit 收集 runit 状态信息 any
supervisord 收集 supervisord 状态信息 any
systemd systemd 中收集设备系统状态信息 Linux
tcpstat /proc/net/tcp/proc/net/tcp6 收集 TCP 连接状态信息 Linux

配置示例

采集白名单配置

关闭默认的采集项,只开启指定的采集项(白名单)

node-exporter --collector.disable-defaults --collector.cpu --collector.meminfo
阅读全文 »

计算机启动(上电)后,需要固件(firmware)程序来 初始化硬件 以及选择 需要启动的 Operating System

曾经最流行的固件(firmware)程序是 BIOS(Basic Input Output System),最近 BIOS 正在逐渐被 UEFI(Unified Extensible Firmware Interface)取代。在同一个计算机上,他们只能使用一个(互斥)

UEFI 设计包含安全启动特性(Secure Boot Feature),他可以确保 只有所有组建都进行过有效签名的 OS 才能启动过程中被加载 。此特性也可以禁用以启动未经签名的 OS.

BIOS 或者 UEFI 的主要工作是 初始化硬件并将计算机控制权移交给 Boot Loader ,之后 Boot Loader 会寻找并启动 Operating System.

BIOS 或者 UEFI 通常可以在计算机上电启动后进行配置,一般通过特定的按键(如 F1、F2、F12 等)进入设定界面。主要包含 2 部分内容

  • Bios/UEFI 配置工具 : 可以修改 BIOS/UEFI 设定,如启用/禁止某些功能
  • Boot Order : 修改启动顺序,如从特定设备(CD、DVD、USB 等)启动

BIOS

BIOS 是位于主板(motherboard)上的一个芯片,负责初始化 CPU(Central Processing Unit)、RAM(Random Access Memory)、PCIE Card(Peripheral Component Interconnect Express Card)和 Network Devices. BIOS 通电后后会立即执行 POST(Power-on self test),确保硬件(CPU、RAM、PCIE、NIC等)配置正确并可正常运行。

  • BIOS 仅支持运行于 16 位处理器模式下 ,这限制了任一时刻固件上运行的软件命令(Software Commands)的数量。
  • BIOS 为任务运行只分配 1M 内存
  • BIOS 运行中的接口和设备(Interfaces and Devices)只能按顺序初始化 ,可能会导致开机缓慢
  • BIOS 查找 MBR(Master Boot Record)以确定 OS 的位置并运行 Boot Loader 。MBR 只有 32-bit 的空间用于描述分区(Partitions)的信息,这限制了基于 BIOS 启动的系统,最多支持 4 个主分区(3 个主分区和 1 个逻辑分区),并且分区最大不能超过 2TB.

UEFI

UEFI 最初由 Intel 提出并实现,包括 GPT(Globally Unique Identifier Partition Table),是 UEFI 的一部分。

UEFI 是一个软件(标准)或者说更像是一个微型的系统,用于连接 计算机固件(Computer’s firmware)OS(Operating System) 。UEFI 最终会替代 BIOS 但是向后兼容 BIOS。

UEFI 的功能是通过安装在主板(motherboard)上的特殊固件(firmware)实现的,和 BIOS 一样,其在(生产)出厂之时就已经安装,它是计算机开机(上电)运行的 第一个程序执行和 BIOS 同样的自检(POST)流程,但是它提供了比 BIOS 更多的灵活性 。 UEFI 解决了 BIOS 面临的一些限制,如 最大分区BIOS 执行任务的时间

大多数现代化的计算机都已经能同时支持 BIOS 和 UEFI(二选一)。

UEFI 定义了一种新的 OS 和 计算机固件(platform firmware)交流的方式,相比于 BIOS 只能提供 OS 启动过程中必须的信息,UEFI 可以提供更多的特性,如安全启动等。

UEFI 将其初始化数据存放在一个 EFI 分区中,而不是存放在固件(Firmware)中,这个 EFI 分区位于 Nonvolatile Flash Memory ,UEFI 甚至可以在启动时从一个共享网络位置加载。

UEFI 使用比 MBR 更灵活的分区方案,即著名的 GPT(Globally Unique Identifier Partition Table),GPT 使用了 64-bit 用于保留分区表信息,支持多达 128 个分区,并支持系统识别并安装于超过 2TB 的分区之上。

UEFI 能在一个 EFI 系统分区中支持多个 OS,如 Windows OS loader 或者 Debian-based OS loaders,BIOS 只允许存在一个 Boot Loader.

UEFI 是可编程的(Programmable),其相当于一个轻量的 OS.

UEFI 的安全启动(Secure Boot)支持计算机在启动时对其硬件和系统完整性进行校验,可以防止黑客在系统启动时安装 rootkits 并获得系统控制权,Secure Boot 甚至支持授权用户(网络)远程定位问题。

UEFI 包含了一个平台相关(platform-related)的 数据表(data table) ,以及 可以被 OS Loader 调用的 Boot and Runtime Service Calls 。这些信息定义了为支持 UEFI ,固件(Firmwares)或者计算机硬件(Hardwares)应该实现的接口和数据结构(Interfaces and Structures)。

阅读全文 »

boot loader 在计算机启动的时候决定如何运行(启动)安装于硬盘上的操作系统,GRand Unified Bootloader(GRUB)是 Linux 中最流行的 Bootloader 程序。目前主要有 2 个 GRUB 版本:

  • GRUB Legacy : 遗留版本 GRUB v1,在较旧的 Linux 发行版中使用
  • GRUB 2 : 当前最新版本,较新的 Linux 发行版中默认的 Bootloader. GRUB 2 的主要功能依旧是 查找并启动已安装在计算机上的操作系统 。同时 通过内嵌了一些工具和配置文件提供了更强大的功能以及灵活性

相比于 GRUB Legacy,GRUB 2 有以下优点:

  • 脚本支持 。支持脚本语言如 函数循环变量
  • 模块动态加载(Dynamic Module Loading)
  • 救援模式(Rescue Mode)
  • 自定义菜单(Custom Menus)
  • 主题(Thems)
  • 图形化的启动菜单
  • 直接从硬盘启动 LiveCD ISO
  • 全新的配置文件结构
  • Non-x86 架构支持,如 PowerPC
  • 全局 UUIDs 支持

GRUB 2 的配置文件是 /boot/grub/grub.cfg 或者 /boot/grub2/grub.cfg,关于其配置文件,要注意以下事项:

  • /boot/grub/grub.cfg 的内容是由工具 grub-mkconfig 或者 update-grub 根据 /etc/default/grub/etc/grub.d/ 中的内容自动生成,最好不要手动改动 /boot/grub/grub.cfg。要修改控制选项,可以修改 /etc/default/grub/etc/grub.d/*,然后使用 update-grub
  • /boot/grub/grub.cfg 的内容会因为 GRUB 2 的包升级(如内核升级导致新增内核或移除内核)而被覆盖,或者用户使用 update-grub 命令也会覆盖
  • 可用的启动内核列表(/boot/grub/grub.cfg 中的 menuentry)是由命令 update-grubupdate-grub2(一般是 grub 的软链接)自动生成
  • 用户可以在 OS 启动列表中自定义添加启动条目(menu entry),这个功能一般是通过修改 /etc/grub.d/40_custom 实现
  • 控制启动列表显示菜单的选项位于主配置文件 /etc/default/grub
  • 和 GRUB Legacy 不同,GRUB 2 分区(partitions)编号从 1 开始,而不是 0。但是 硬盘编号 依旧从 0 开始 。如系统上的第一个硬盘的第一个分区,GRUB Legacy 中是 hd(0, 0),GRUB 2 中是 hd(0, 1)
  • grub.cfg 中可以包含 Shell 脚本语法,如 函数,循环,变量等
  • 用于定位内核(Kernels)和 Initial RAM 位置的设备名称,最好是使用更加可靠的 标签(Labels) 或者是 UUIDs(Universally Unique Identifiers) ,而不是类似于 /dev/sda 的设备名称,这可以防止计算机系统新增硬盘后,/dev/sda 变成了 /dev/sdb 而导致系统启动时内核无法找到。
  • 配置文件更改后,只有执行了 update-grub 后才会最终生效

GRUB 2 配置流程

GRUB 2 的配置变更主要是通过修改主配置文件 /etc/default/grub 以及包含自定义脚本文件的目录 /etc/grub.d/,然后执行 update-grub 最终生效。update-grub 会从 /etc/default/grub/etc/grub.d/ 收集相关配置并将其更新到 /boot/grub/grub.cfg

启动菜单(Menu Display)的展示行为主要是通过 /etc/default/grub 进行控制

阅读全文 »

检查命令依赖的共享库

以下命令检查 crontab 命令依赖的共享库

# ldd `which crontab`
linux-vdso.so.1 (0x00007ffe2db17000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007063f4c00000)
/lib64/ld-linux-x86-64.so.2 (0x00007063f4fc3000)

PAM(Pluggable Authentication Modules) 由 Sun Microsystems 发明并最初在 Solarise OS 上实现,Linux 于 1997 年实现了 Linux-PAM.

PAM 简化了系统认证管理的过程,使用 PAM 提供了以下好处:

  • 使用中心化的方式简化了用户身份认证过程以及系统管理员的视角
  • 简化了应用在认证相关功能方面的开发,开发者无需重复写认证相关代码,只需要直接使用已有的认证模块即可
  • 认证灵活性
    • 可以根据用户身份或者是特定的时间,或者是对特定资源的使用进行允许/拒绝操作

包含 PAM 的应用中 PAM 使用流程大体如下

  • 一个主体(用户或者进程)请求访问(通过认证)一个应用
  • 此应用读取其 PAM 配置文件,其 PAM 配置文件中配置了认证策略,认证策略中一般包含要进行认证的 PAM 模块的列表,这个列表也称为 Stack
  • Stack 中配置的 PAM 模块被依次调用
  • 每个 PAM 模块会根据其配置返回 成功(Success) 或者 失败(Failure) 状态
  • 最后是否认证成功,由所有的 PAM 模块返回的结果组合得到 成功(Success) 或者 失败(Failure) ,具体组合方式取决于配置文件中的配置。

Linux 中,大多数包含 PAM 的应用的配置文件位于 /etc/pam.d/,通用的 PAM 配置文件格式使用:

context 'control flag' 'PAM module' [module options]

Linux 中,系统安全(如资源限制,用户登陆等)相关的 PAM 配置文件位于 /etc/security/*.conf

PAM Contexts

PAM 模块为不同的认证服务提供了标准的函数,这些 PAM 模块中的标准函数根据其函数类型称为 Contexts,也可以叫做 Module Interfaces 或者 Types,下列出了不同的 PAM Contexts 及其对应的 认证服务(Authentication Service)

Context Service Description
auth 提供认证管理服务(Authentication Management Services),如验证帐号密码
account 帐号验证服务(Account Validation Services),例如只允许特定时间登陆
password 管理帐号密码(Manages Account Passwords),如限制帐号密码长度

PAM Control Flags

PAM 中的 Control Flags 用于决定返回什么样的状态。下表列出了 PAM 配置中的相关的 Control Flags 及其响应

Control Flag Response Handle
required If failed, returns a failure status to the application, after the rest of the contexts have been run in the stack.
For example, a requisite control might cause a login to fail if someone types in an invalid user. But the user might not be told of the failure until after entering a password, hiding the fact that it was the bad username that caused the failure
requisite If failed, returns a failure status to the application immediately without running the rest of the stack(Be careful where you place this control in the stack.)
For example, a requisite control might require key-based authentication and fail immediately when a valid key is not provided . In that case, it could fail before even prompting for a username/password.
sufficient - If failed, the module status is ignored.
- If successful, then a success status is immediately returned to the application without running the rest of the stack.
(Be careful where you place this control in the stack.)
optional This control flag is important only for the final overall return status of success or failure . Think of it as a tiebreaker. When the other modules in the configuration file stack return statuses that are neither clear-cut failure nor success statuses, this optional module’s status is used to determine the final status or break the tie. In cases where the other modules in the stack are returning a clear-cut path of failure or success, this status is ignored.
include Get all the return statuses from this particular PAM configuration file’s stack to include in this stack’s overall return status. It’s as if the entire stack from the named configuration file is now in this configuration file
substack Similar to the include control flag, except for how certain errors and evaluations affect the main stack. This forces the included configuration file stack to act as a substack to the main stack. Thus, certain errors and evaluations affect only the substack and not the main stack.

PAM Modules

Linux 中,PAM Modules 其实是系统共享库(Shared Library Module)文件,RHEL 默认位于 /usr/lib64/security/pam*.so, Ubuntu 位于 /usr/lib/x86_64-linux-gnu/security/pam*.so

# ls /usr/lib/x86_64-linux-gnu/security/
pam_access.so pam_exec.so pam_gdm.so pam_listfile.so pam_nologin.so pam_securetty.so pam_stress.so pam_unix.so
pam_cap.so pam_extrausers.so pam_gnome_keyring.so pam_localuser.so pam_oddjob_mkhomedir.so pam_selinux.so pam_succeed_if.so pam_userdb.so
pam_dcvgraphicalsso.so pam_faildelay.so pam_group.so pam_loginuid.so pam_permit.so pam_sepermit.so pam_systemd.so pam_usertype.so
pam_debug.so pam_faillock.so pam_issue.so pam_mail.so pam_pwhistory.so pam_setquota.so pam_time.so pam_warn.so
pam_deny.so pam_filter.so pam_keyinit.so pam_mkhomedir.so pam_pwquality.so pam_shells.so pam_timestamp.so pam_wheel.so
pam_echo.so pam_fprintd.so pam_lastlog.so pam_motd.so pam_rhosts.so pam_sss_gss.so pam_tty_audit.so pam_xauth.so
pam_env.so pam_ftp.so pam_limits.so pam_namespace.so pam_rootok.so pam_sss.so pam_umask.so

要查看 PAM Module 的帮助文档,可以使用 man 手册,如 man pam_access

使用 PAM 的应用可以大概分为以下 2 类:

  • 普通程序(Application) 。默认使用 PAM 的应用的配置位于 /etc/pam.d,每个使用 PAM Module 的应用程序都应该有其自己的配置文件来定义它如何使用 PAM 模块。如果没有,这将是一个漏洞,为了防止这种情况出现,系统内置了一个 other 的配置(/etc/pam.d/other),所有使用了 PAM 但是没有指定其配置文件的应用都将使用此配置。 other 配置默认拒绝(Implicit Deny)所有的请求

    # ls /etc/pam.d/
    chfn common-auth cron gdm-autologin gdm-smartcard login polkit-1 samba sudo
    chpasswd common-password cups gdm-fingerprint gdm-smartcard-pkcs11-exclusive newusers ppp sshd sudo-i
    chsh common-session dcv gdm-launch-environment gdm-smartcard-sssd-exclusive other runuser sssd-shadowutils su-l
    common-account common-session-noninteractive dcv-graphical-sso gdm-password gdm-smartcard-sssd-or-password passwd runuser-l su vmtoolsd

  • 系统安全相关程序 。系统安全相关的 PAM 配置默认位于 /etc/security

    # ls /etc/security/
    access.conf faillock.conf limits.conf namespace.conf namespace.init pam_env.conf sepermit.conf
    capability.conf group.conf limits.d namespace.d opasswd pwquality.conf time.conf

阅读全文 »