Linux内核分析之进程管理-12

韩乔落

第12章 进程调试与监控

基于 Linux 6.12.38 源码分析


12.1 ptrace 机制

12.1.1 ptrace 概述

定义: ptrace 是 Linux 提供的进程跟踪机制,允许一个进程观察和控制另一个进程的执行。

用途:

  • 调试器 (gdb)
  • 系统调用追踪 (strace)
  • 进程监控

12.1.2 ptrace 系统调用

位置: kernel/ptrace.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
SYSCALL_DEFINE4(ptrace, long, request, long, pid,
unsigned long, addr, unsigned long, data)
{
struct task_struct *child;
long ret;

// 查找目标进程
child = find_get_task_by_vpid(pid);
if (!child)
return -ESRCH;

// 检查权限
if (!may_attach(child)) {
put_task_struct(child);
return -EPERM;
}

// 根据请求类型处理
ret = ptrace_check_attach(child, request == PTRACE_KILL);

switch (request) {
case PTRACE_TRACEME:
// 当前进程被父进程跟踪
ret = ptrace_traceme();
break;

case PTRACE_ATTACH:
// 附加到目标进程
ret = ptrace_attach(child, data);
break;

case PTRACE_SETOPTIONS:
// 设置跟踪选项
ret = ptrace_set_options(child, data);
break;

case PTRACE_GETREGS:
// 获取寄存器
ret = ptrace_get_regs(child, (void __user *)data);
break;

case PTRACE_SETREGS:
// 设置寄存器
ret = ptrace_set_regs(child, (void __user *)data);
break;

case PTRACE_CONT:
// 继续执行
ret = ptrace_resume(child, data);
break;

case PTRACE_SINGLESTEP:
// 单步执行
ret = ptrace_singlestep(child, data);
break;

case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
// 读取内存
ret = ptrace_peek_data(child, addr, (void __user *)data);
break;

case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
// 写入内存
ret = ptrace_poke_data(child, addr, (void __user *)data);
break;

default:
ret = arch_ptrace(child, request, addr, data);
break;
}

put_task_struct(child);
return ret;
}

12.1.3 ptrace 请求类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// include/uapi/linux/ptrace.h
#define PTRACE_TRACEME 0 /* 被父进程跟踪 */
#define PTRACE_PEEKTEXT 1 /* 读取代码段 */
#define PTRACE_PEEKDATA 2 /* 读取数据段 */
#define PTRACE_PEEKUSER 3 /* 读取用户区 */
#define PTRACE_POKETEXT 4 /* 写入代码段 */
#define PTRACE_POKEDATA 5 /* 写入数据段 */
#define PTRACE_POKEUSER 6 /* 写入用户区 */
#define PTRACE_CONT 7 /* 继续执行 */
#define PTRACE_KILL 8 /* 杀死进程 */
#define PTRACE_SINGLESTEP 9 /* 单步执行 */
#define PTRACE_ATTACH 16 /* 附加到进程 */
#define PTRACE_DETACH 17 /* 分离进程 */
#define PTRACE_SETOPTIONS 0x4200 /* 设置选项 */
#define PTRACE_GETREGS 0x4201 /* 获取寄存器 */
#define PTRACE_SETREGS 0x4202 /* 设置寄存器 */

12.1.4 ptrace 选项

1
2
3
4
5
6
7
8
9
// PTRACE_O_* 选项
#define PTRACE_O_TRACESYSGOOD 0x00000001 /* 生成 SIGTRAP | 0x80 */
#define PTRACE_O_TRACEFORK 0x00000002 /* 跟踪 fork */
#define PTRACE_O_TRACEVFORK 0x00000004 /* 跟踪 vfork */
#define PTRACE_O_TRACECLONE 0x00000008 /* 跟踪 clone */
#define PTRACE_O_TRACEEXEC 0x00000010 /* 跟踪 exec */
#define PTRACE_O_TRACEVFORKDONE 0x00000020 /* 跟踪 vfork 完成 */
#define PTRACE_O_TRACEEXIT 0x00000040 /* 跟踪 exit */
#define PTRACE_O_TRACESECCOMP 0x00000080 /* 跟踪 seccomp */

12.2 进程状态监控

12.2.1 /proc/[pid]/stat

1
2
3
4
5
6
7
8
9
10
11
# 查看进程状态
$ cat /proc/1234/stat
1234 (sleep) S 1000 1000 1000 0 -1 4194304 45 0 0 0 12 8 0 0 20 0 1 0 123456789 123456 18446744073709551615

# 状态字符含义:
# R (Running) - 正在运行或可运行
# S (Sleeping) - 可中断睡眠
# D (Disk sleep) - 不可中断睡眠
# Z (Zombie) - 僵尸进程
# T (Stopped) - 已停止
# t (Tracing) - 被跟踪

12.2.2 /proc/[pid]/status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看详细状态
$ cat /proc/1234/status
Name: sleep
State: S (sleeping)
Tgid: 1234
Pid: 1234
PPid: 1000
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000

# 性能统计
voluntary_ctxt_switches: 123
nonvoluntary_ctxt_switches: 45

12.2.3 /proc/[pid]/stack

1
2
3
4
5
6
# 查看进程内核栈
$ cat /proc/1234/stack
[<0>] do_wait+0x1a3/0x260
[<0>] sys_wait4+0x8a/0xe0
[<0>] do_syscall_64+0x3a/0x80
[<0>] entry_SYSCALL_64_after_hwframe+0x44/0xae

12.2.4 /proc/[pid]/wchan

1
2
3
# 查看进程等待的内核函数
$ cat /proc/1234/wchan
do_wait

12.3 strace 系统调用追踪

12.3.1 strace 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 追踪系统调用
$ strace ls

# 追踪特定系统调用
$ strace -e trace=open,close,read,write ls

# 统计系统调用时间
$ strace -c ls

# 追踪子进程
$ strace -f command

# 追踪特定进程
$ strace -p 1234

# 显示时间戳
$ strace -t ls
$ strace -tt ls # 微秒级
$ strace -T ls # 显示调用耗时

# 追踪信号
$ strace -e trace=signal ls

12.3.2 strace 输出示例

1
2
3
4
5
6
7
8
9
10
$ strace ls
execve("/bin/ls", ["ls"], 0x7ffeb1234567 /* 73 vars */) = 0
brk(NULL) = 0x567890000000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1234567000
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=12345, ...}) = 0
mmap(NULL, 12345, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1234543000
close(3) = 0
...

12.4 ftrace 函数追踪

12.4.1 ftrace 接口

1
2
3
4
5
6
7
8
9
10
11
# 检查 ftrace 可用性
$ mount | debugfs
debugfs on /sys/kernel/debug type debugfs

# 查看可用追踪器
$ cat /sys/kernel/debug/tracing/available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

# 查看当前追踪器
$ cat /sys/kernel/debug/tracing/current_tracer
nop

12.4.2 函数追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 启用函数追踪
$ echo function > /sys/kernel/debug/tracing/current_tracer

# 查看追踪结果
$ cat /sys/kernel/debug/tracing/trace
# tracer: function
#
# entries-in-buffer/entries-written: 1234/1234 #P:4
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
bash-1234 [001] .... 12345.678901: sys_write <-syscall_return_slowpath
bash-1234 [001] .... 12345.678902: vfs_write <-sys_write
bash-1234 [001] .... 12345.678903: ksys_write <-vfs_write

# 追踪特定函数
$ echo sched_fork > /sys/kernel/debug/tracing/set_ftrace_filter
$ echo do_fork >> /sys/kernel/debug/tracing/set_ftrace_filter

# 清空追踪
$ echo > /sys/kernel/debug/tracing/trace

# 停止追踪
$ echo nop > /sys/kernel/debug/tracing/current_tracer

12.4.3 函数图追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 启用函数图追踪
$ echo function_graph > /sys/kernel/debug/tracing/current_tracer

# 查看追踪结果
$ cat /sys/kernel/debug/tracing/trace
2) | sys_fork() {
2) | do_fork() {
2) | copy_process() {
2) 0.123 us | _copy_from_user();
2) 0.456 us | dup_task_struct();
2) | alloc_pid() {
2) 0.789 us | spin_lock();
2) 0.234 us | spin_unlock();
2) 1.234 us | }
2) | }
2) 5.678 us | }
2) + 12.345 us | }

12.5 perf 性能分析

12.5.1 perf 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 列出可用事件
$ perf list

# 统计 CPU 事件
$ perf stat ls

# 记录性能数据
$ perf record ls
$ perf report

# 实时监控
$ perf top

# 查看调度事件
$ perf sched record -- sleep 60
$ perf sched latency
$ perf sched map

12.5.2 perf sched 延迟分析

1
2
3
4
5
6
7
8
9
10
11
12
13
# 记录调度事件
$ perf sched record -- sleep 60

# 分析调度延迟
$ perf sched latency
Task | Runtime ms | Switches | Avg delay ms | Max delay ms
firefox:1234 | 1234.56 | 5678 | 0.123 | 45.678
chrome:2345 | 2345.67 | 6789 | 0.234 | 56.789

# 分析调度映射
$ perf sched map
*A0 123456.123456 1234 A0:2345/234567
*B0 123456.123567 2345 B0:3456/345678

12.5.3 perf trace

1
2
3
4
5
# 追踪系统调用 (类似 strace)
$ perf trace

# 追踪特定事件
$ perf trace -e sched:sched_switch

12.6 eBPF/BCC 动态追踪

12.6.1 BCC 工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 追踪进程创建
$ execsnoop
PCOMM PID PPID RET ARGS
bash 1234 1000 0 ls
bash 1235 1000 0 sleep

# 追踪进程退出
$ exitsnoop
PCOMM PID PPID RET ARGS
sleep 1234 1000 0
ls 1235 1000 0

# 追踪调度延迟
$ runqlat
usecs : count distribution
0 -> 1 : 123 |*********** |
2 -> 3 : 456 |********************************************** |
4 -> 7 : 789 |**************************************************************************|
8 -> 15 : 1234 |******************************************************************************************************************|

# 追踪 CPU 使用
$ profile

12.6.2 bpftrace

1
2
3
4
5
6
7
8
# 追踪系统调用频率
$ bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'

# 追踪进程调度
$ bpftrace -e 'tracepoint:sched:sched_switch { @[comm] = count(); }'

# 追踪页面错误
$ bpftrace -e 'software:fault:.* { @[comm] = count(); }'

12.7 /proc/sys/kernel 调优参数

12.7.1 进程相关参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 最大 PID
/proc/sys/kernel/pid_max
# 默认: 32768 (32位) 或 4194304 (64位)
echo 4194304 > /proc/sys/kernel/pid_max

# 最大线程数
/proc/sys/kernel/threads-max
# 默认: 根据内存计算
echo 123456 > /proc/sys/kernel/threads-max

# 地址空间随机化
/proc/sys/kernel/randomize_va_space
# 0 = 关闭, 1 = 部分随机化, 2 = 完全随机化
echo 2 > /proc/sys/kernel/randomize_va_space

12.7.2 调度器参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 实时调度周期 (微秒)
/proc/sys/kernel/sched_rt_period_us
echo 1000000 > /proc/sys/kernel/sched_rt_period_us

# 实时调度运行时间 (微秒)
/proc/sys/kernel/sched_rt_runtime_us
echo 950000 > /proc/sys/kernel/sched_rt_runtime_us
echo -1 > /proc/sys/kernel/sched_rt_runtime_us # 无限制

# CFS 调度参数
/proc/sys/kernel/sched_min_granularity_ns
/proc/sys/kernel/sched_wakeup_granularity_ns
/proc/sys/kernel/sched_latency_ns

# RR 时间片 (毫秒)
/proc/sys/kernel/sched_rr_timeslice_ms
echo 100 > /proc/sys/kernel/sched_rr_timeslice_ms

12.7.3 信号相关参数

1
2
3
4
5
6
7
8
9
10
11
# Core 文件名模式
/proc/sys/kernel/core_pattern
echo /var/crash/core.%e.%p > /proc/sys/kernel/core_pattern

# Core 文件大小限制
/proc/sys/kernel/core_uses_pid
echo 1 > /proc/sys/kernel/core_uses_pid

# 打印致命信号
/proc/sys/kernel/print_fatal_signals
echo 1 > /proc/sys/kernel/print_fatal_signals

12.8 进程转储 (Core Dump)

12.8.1 生成 Core Dump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <sys/resource.h>
#include <signal.h>

int main(void)
{
struct rlimit lim;

// 启用 core dump
lim.rlim_cur = RLIM_INFINITY;
lim.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &lim);

// 触发段错误生成 core
raise(SIGSEGV);

return 0;
}

12.8.2 Core 文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 配置 core 文件路径
echo "/var/crash/core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern

# %e = 可执行文件名
# %p = PID
# %h = 主机名
# %t = 时间戳
# %u = UID

# 在 core 文件名中包含 PID
echo 1 > /proc/sys/kernel/core_uses_pid

# 限制 core 文件大小
ulimit -c unlimited
ulimit -c 1000000

12.8.3 分析 Core Dump

1
2
3
4
5
6
# 使用 gdb 分析 core 文件
$ gdb executable core
(gdb) bt # 回溯
(gdb) info registers
(gdb) info threads
(gdb) thread apply all bt

12.9 进程监控工具

12.9.1 ps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看所有进程
ps aux

# 查看进程树
ps axjf

# 查看特定进程
ps -p 1234

# 查看线程
ps -eLf

# 查看调度信息
ps -eo pid,tid,pri,psr,comm

12.9.2 top

1
2
3
4
5
6
7
8
9
10
11
# 实时进程监控
top

# 显示线程
top -H

# 显示 CPU 数
top -p 1234

# 批处理模式
top -b -n 1 > top.log

12.9.3 htop

1
2
3
4
5
6
7
8
# 更友好的进程监控
htop

# 显示进程树
htop -t

# 特定用户进程
htop -u username

12.9.4 pidstat

1
2
3
4
5
6
7
8
9
10
11
# CPU 统计
$ pidstat -p 1234

# 上下文切换
$ pidstat -w -p 1234

# 缺页统计
$ pidstat -r -p 1234

# 堆栈使用
$ pidstat -s -p 1234

12.10 内核调试

12.10.1 Magic SysRq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启用 Magic SysRq
echo 1 > /proc/sys/kernel/sysrq

# 常用命令
Alt + SysRq + t # 显示任务状态
Alt + SysRq + m # 显示内存信息
Alt + SysRq + p # 显示寄存器
Alt + SysRq + c # 触发系统崩溃 (用于 kdump 测试)
Alt + SysRq + s # 同步所有文件系统
Alt + SysRq + u # 重新挂载只读
Alt + SysRq + b # 立即重启

# 通过 /proc 触发
echo t > /proc/sysrq-trigger

12.10.2 kprobe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 内核模块中使用 kprobe
#include <linux/kprobes.h>

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
printk("pre_handler: p->addr = 0x%p, ip = %lx\n",
p->addr, regs->ip);
return 0;
}

static struct kprobe kp = {
.symbol_name = "do_fork",
.pre_handler = handler_pre,
};

static int __init kprobe_init(void)
{
register_kprobe(&kp);
return 0;
}

12.11 本章小结

本章介绍了 Linux 进程调试与监控:

  1. ptrace 机制:进程跟踪、断点、单步执行
  2. strace:系统调用追踪
  3. ftrace:内核函数追踪
  4. perf:性能分析工具
  5. eBPF/BCC:动态追踪
  6. /proc 接口:stat、status、stack、wchan
  7. 调优参数:/proc/sys/kernel
  8. Core Dump:进程转储与分析
  9. 监控工具:ps、top、htop、pidstat
  10. 内核调试:Magic SysRq、kprobe

至此,Linux 6.12.38 进程管理的内容已全部介绍完毕,后续会进行补充其他相关知识。

  • Title: Linux内核分析之进程管理-12
  • Author: 韩乔落
  • Created at : 2026-01-14 19:20:39
  • Updated at : 2026-01-19 13:40:22
  • Link: https://jelasin.github.io/2026/01/14/Linux内核分析之进程管理-12/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments