- 文档
- 教程
【免费下载链接】linux-command
Linux命令大全搜索工具,内容包含Linux命令手册、详解、学习、搜集。https://git.io/linux
本篇文章基于 linux-command 仓库中的 strace 命令文档 展开,系统讲解这一「集诊断、调试、统计于一体」的系统调用跟踪工具:从语法与全部选项入手,通过追踪系统调用、跟踪信号传递、系统调用统计三个经典实例,再到分析 Oracle lgwr 进程写入行为的综合实战,让读者掌握用 strace 定位故障、理解程序工作过程、量化系统调用开销的完整方法论。
strace 是什么:系统调用与信号的双重观察窗口
strace 命令是一个集诊断、调试、统计于一体的工具,它通过对应用的系统调用和信号传递进行跟踪,帮助工程师解决问题或了解应用的工作过程。需要明确的是,strace 与专业的调试工具(如 gdb)定位不同:它不是一个专业调试器,不会提供断点、单步执行、变量查看等交互式调试能力,而是以「旁观者」的视角记录程序与内核之间的每一次交互。
Linux 用户态程序的一切行为最终都要通过系统调用(syscall)到达内核,例如读写文件、分配内存、创建进程、网络通信;同时,进程也会接收来自内核或其他进程的信号(signal)。strace 恰好把这两类事件全部记录下来并解析成人类可读的文本,这就构成了观察程序行为的两个最佳窗口。
在 linux-command 仓库中,strace 与几个工具形成互补关系,便于横向对比理解:
- ltrace 命令文档:跟踪进程调用库函数的情况,与 strace 跟踪系统调用恰好互补,二者常搭配使用;
- gdb 命令文档:功能强大的程序调试器,支持断点、单步、变量查看,属于真正的「专业调试器」,strace 不与其竞争。
strace 最简单的用法就是执行一个指定的命令,在命令结束之后它也就退出。在命令执行的过程中,strace 会记录并解析命令进程的所有系统调用,以及这个进程所接收到的所有信号值。
语法
strace [ -dffhiqrtttTvxx ] [ -acolumn ] [ -eexpr ] ... [ -ofile ] [-ppid ] ... [ -sstrsize ] [ -uusername ] [ -Evar=val ] ... [ -Evar ]... [command [ arg ... ] ] strace -c [ -eexpr ] ... [ -Ooverhead ] [ -Ssortby ] [ command [ arg... ] ]两种语法形式的区别在于:第一种是常规跟踪模式,直接跟踪并实时输出每个系统调用;第二种固定以-c开头,进入统计模式,只输出系统调用的汇总统计报表。-Ooverhead用于设置每个系统调用的开销值(微秒级),以便-c统计时扣除该开销得到更真实的耗时;-Ssortby指定统计结果的排序字段(如 time、calls、errors 等)。
选项详解
strace 的选项覆盖面很广,下面是文档中给出的完整选项清单:
-c 统计每一系统调用的所执行的时间,次数和出错的次数等. -d 输出strace关于标准错误的调试信息. -f 跟踪由fork调用所产生的子进程. -ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号. -F 尝试跟踪vfork调用.在-f时,vfork不被跟踪. -h 输出简要的帮助信息. -i 输出系统调用的入口指针. -q 禁止输出关于脱离的消息. -r 打印出相对时间关于,,每一个系统调用. -t 在输出中的每一行前加上时间信息. -tt 在输出中的每一行前加上时间信息,微秒级. -ttt 微秒级输出,以秒了表示时间. -T 显示每一调用所耗的时间. -v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出. -V 输出strace的版本信息. -x 以十六进制形式输出非标准字符串 -xx 所有字符串以十六进制形式输出. -a column 设置返回值的输出位置.默认 为40. -e expr 指定一个表达式,用来控制如何跟踪.格式:[qualifier=][!]value1[,value2]... qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open 表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\. -e trace=set 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all. -e trace=file 只跟踪有关文件操作的系统调用. -e trace=process 只跟踪有关进程控制的系统调用. -e trace=network 跟踪与网络有关的所有系统调用. -e strace=signal 跟踪所有与系统信号有关的 系统调用 -e trace=ipc 跟踪所有与进程通讯有关的系统调用 -e abbrev=set 设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all. -e raw=set 将指定的系统调用的参数以十六进制显示. -e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号. -e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5 -e write=set 输出写入到指定文件中的数据. -o filename 将strace的输出写入文件filename -p pid 跟踪指定的进程pid. -s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出. -u username 以username的UID和GID执行被跟踪的命令面对这么多选项,可以从四个维度来组织理解:
输出控制类:-o filename将输出写入文件(对应下文的「重定向输出」);-a column对齐返回值的输出位置;-s strsize限制字符串输出长度;-q抑制附加消息;-v展开所有调用细节(如环境变量、状态、输入输出等因调用频繁默认不展开的内容);-x/-xx控制非标准字符串的十六进制显示;-i打印系统调用入口地址。
进程跟踪类:-f跟踪 fork 出的子进程(守护进程排障常用);-ff配合-o时按filename.pid为每个进程单独输出;-F尝试跟踪 vfork;-p pid挂接到已运行进程;-u username以指定用户身份运行被跟踪命令。
时间类:-r相对时间;-t/-tt/-ttt三种时间戳精度;-T单次调用耗时;-c汇总统计;-Ooverhead扣除系统调用开销;-Ssortby指定统计排序字段。
诊断辅助类:-d调试信息;-h帮助;-V版本信息。
其中最重要的是-e expr,其格式为[qualifier=][!]value1[,value2]...。qualifier 只能是trace、abbrev、verbose、raw、signal、read、write之一,默认是trace;value 是用于限定的符号或数字;感叹号表示否定。例如-e open等价于-e trace=open,表示只跟踪 open 调用;而-e trace!=open表示跟踪除 open 以外的所有调用。另有all和none两个特殊值。注意:有些 shell 会用!执行历史命令,因此在命令行中需要加反斜杠转义。trace限定符还提供了几个预置分类,非常实用:
-e trace=file:只跟踪文件操作相关系统调用;-e trace=process:只跟踪进程控制相关系统调用;-e trace=network:跟踪所有网络相关系统调用;-e trace=signal:跟踪所有与信号相关的系统调用;-e trace=ipc:跟踪所有与进程间通信相关的系统调用。
read/write限定符则用于输出指定文件描述符上读写的数据内容,例如-e read=3,5表示输出从 fd 3、5 读出的数据——这在分析协议交互时非常有用。
实例一:追踪系统调用,读懂程序的一举一动
做一个很简单的程序来演示 strace 的基本用法。C 语言代码如下:
# filename test.c #include <stdio.h> int main() { int a; scanf("%d", &a); printf("%09d\n", a); return 0; }程序逻辑极其简单:从标准输入读一个整数,再按 9 位宽度补零输出。用gcc -o test test.c编译得到可执行文件 test,然后用 strace 调用执行:
strace ./test执行期间程序会要求输入一个整数,输入 99,得到结果:
// 直接执行test的结果 oracle@orainst[orcl]:~ $./test // 执行的结果 99 000000099 // 通过strace执行test的结果 oracle@orainst[orcl]:~ $strace ./test // strace的trace结果 execve("./test", ["./test"], [/* 41 vars */]) = 0 uname({sys="Linux", node="orainst.desktop.mycompany.com", ...}) = 0 brk(0) = 0x8078000 fstat64(3, {st_mode=S_IFREG|0644, st_size=65900, ...}) = 0 old_mmap(NULL, 65900, PROT_READ, MAP_PRIVATE, 3, 0) = 0xbf5ef000 close(3) = 0 open("/lib/tls/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200X\1"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1571692, ...}) = 0 old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ee000 old_mmap(NULL, 1275340, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xa02000 old_mmap(0xb34000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x132000) = 0xb34000 old_mmap(0xb37000, 9676, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb37000 close(3) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0xbf5ee740, limit:1048575, seg_32bit:1, contents:0, read_only_exec:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0xbf5ef000, 65900) = 0 fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ff000 read(0, 99 "99\n", 1024) = 3 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5fe000 write(1, "000000099\n", 10000000099 ) = 10 munmap(0xbf5fe000, 4096) = 0 exit_group(0) = ?从 trace 结构可以看到程序完整的工作过程:系统首先调用execve开始一个新的进程,接着进行环境初始化——uname获取内核信息、brk调整堆、一系列old_mmap映射动态库(libc.so.6)代码段与数据段、set_thread_area设置线程本地存储,最后程序停顿在read(0, ...)上——这对应scanf等待输入。输入 99 后,程序调用write将格式化后的数值000000099输出到屏幕(fd 1),最后调用exit_group退出进程,完成整个执行过程。
这里值得留意的是每个系统调用末尾的= 0、= 3、= 10,它们是调用的返回值:例如read(0, "99\n", 1024) = 3表示从标准输入实际读到了 3 个字节,write(1, "000000099\n", ...) = 10表示写出了 10 个字节。返回值是 strace 输出中最关键的信息之一,排障时经常据此判断调用是否失败(负数即出错)。
实例二:跟踪信号传递,观察进程如何被终止
仍使用上面的 test 程序观察进程接收信号的情况。先执行strace ./test,等到等待输入的画面时不要输入任何内容,然后打开另一个终端窗口,输入:
killall test可以看到程序随即退出,最后的 trace 结果如下:
oracle@orainst[orcl]:~ $strace ./test execve("./test", ["./test"], [/* 41 vars */]) = 0 uname({sys="Linux", node="orainst.desktop.mycompany.com", ...}) = 0 brk(0) = 0x9ae2000 old_mmap(NULL, 65900, PROT_READ, MAP_PRIVATE, 3, 0) = 0xbf5ef000 close(3) = 0 open("/lib/tls/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200X\1"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1571692, ...}) = 0 old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ee000 old_mmap(NULL, 1275340, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x2e9000 old_mmap(0x41b000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x132000) = 0x41b000 old_mmap(0x41e000, 9676, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x41e000 close(3) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0xbf5ee740, limit:1048575, seg_32bit:1, contents:0, read_only_exec:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0xbf5ef000, 65900) = 0 fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ff000 read(0, 0xbf5ff000, 1024) = ? ERESTARTSYS (To be restarted) --- SIGTERM (Terminated) @ 0 (0) --- +++ killed by SIGTERM +++这次的关键变化在最后三行:阻塞在read(0, ...)上的进程收到 SIGTERM 信号,read 系统调用被中断返回? ERESTARTSYS (To be restarted)(该返回值表示系统调用将被重启),紧接着 strace 打印出--- SIGTERM (Terminated) @ 0 (0) ---标记信号到达,并以+++ killed by SIGTERM +++明确告知 test 进程是被 SIGTERM 信号杀死的。killall命令默认发送的正是 SIGTERM(可参考仓库中 killall 命令文档,它把「按名称查找进程 + 发送信号」两步合并为一步)。
这一实例展示了 strace 的典型排障用法:当程序「莫名其妙」退出时,strace 能明确告诉你是被哪个信号终止的——这在排查 OOM Killer、外部监控脚本误杀、服务重启等场景中尤其有用。
实例三:系统调用统计,量化程序行为
strace 不光能追踪系统调用,通过-c参数,它还能将进程所有的系统调用做一个统计分析。执行带-c参数的 strace:
strace -c ./test得到的统计结果:
oracle@orainst[orcl]:~ $strace -c ./test execve("./test", ["./test"], [/* 41 vars */]) = 0 % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 45.90 0.000140 5 27 25 open 34.43 0.000105 4 24 21 stat64 7.54 0.000023 5 5 old_mmap 2.62 0.000008 8 1 munmap 1.97 0.000006 6 1 uname 1.97 0.000006 2 3 fstat64 1.64 0.000005 3 2 1 read 1.31 0.000004 2 2 close 0.98 0.000003 3 1 brk 0.98 0.000003 3 1 mmap2 0.66 0.000002 2 1 set_thread_area ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000305 68 47 total统计表清晰地揭示了程序的行为特征:open被调用了 27 次却出现 25 次错误(error 列),stat64调用 24 次出现 21 次错误——这是因为动态链接器在搜索共享库时,会对多个候选路径逐一open/stat64,找不到就返回错误,直到命中正确的路径,这是正常现象而非故障。% time列展示每个系统调用的耗时占比,seconds是该类调用的累计耗时,usecs/call是单次调用平均耗时,calls是调用次数,errors是出错次数。末尾的total行给出全局汇总:本次运行共 68 次调用、47 次出错、总耗时 0.000305 秒。
这个功能对分析程序性能热点极有价值:哪类系统调用最耗时、出错最频繁,一眼即知,从而为优化方向(如减少文件系统访问、增加缓存、合并小写入)提供数据支撑。
常用参数实战
除了-c,strace 还提供了其他常用参数,便于按需获取信息。
重定向输出
参数-o用于将 strace 的结果输出到文件中。如果不指定-o,默认输出设备是 STDERR,也就是说-o filename和2>filename的结果是一样的:
# 这两个命令都是将strace结果输出到文件test.txt中 strace -c -o test.txt ./test strace -c ./test 2>test.txt理解这一点的实际价值在于:strace 结果默认走 stderr 而非 stdout,因此既可以用-o让工具自己管理输出文件,也可以借助 shell 的重定向机制把输出导入任意位置(例如配合grep过滤、tee同时查看和保存)。跟踪产生大量输出时,务必使用文件保存,避免终端刷屏影响观察。
对系统调用进行计时
strace 可以使用-T参数将每个系统调用所花费的时间打印出来,每个调用的时间开销显示在调用行最右边的尖括号内:
oracle@orainst[orcl]:~ $strace -T ./test // 这里只摘录部分结果 read(0, 1 "1\n", 1024) = 2 <2.673455> fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000014> mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5fe000 <0.000017> write(1, "000000001\n", 10000000001 ) = 10 <0.000016> munmap(0xbf5fe000, 4096) = 0 <0.000020> exit_group(0) = ?注意read调用耗时 2.673455 秒——这是它阻塞等待键盘输入的时间,与程序执行逻辑一致;而后续调用都是微秒级。这一参数把「哪次调用慢」精确到单个系统调用级别,是定位 IO 瓶颈、锁等待、网络阻塞的利器。
系统调用的时间
strace 会将每次系统调用的发生时间记录下来,使用-t/-tt/-ttt三个参数即可看到效果:
| 参数名 | 输出样式 | 说明 |
|---|---|---|
-t | 10:33:04 exit_group(0) | 输出结果精确到秒 |
-tt | 10:33:48.159682 exit_group(0) | 输出结果精确到微秒 |
-ttt | 1262169244.788478 exit_group(0) | 精确到微秒,且时间表示为 Unix 时间戳 |
三种精度的选择取决于分析粒度:粗看调用序列用-t足够;分析毫秒级延迟或调用频率用-tt;需要与其他日志时间戳(如应用日志、数据库 trace 文件)做跨源对齐时,-ttt输出的 Unix 时间戳最方便计算时间差。
截断输出
-s参数用于指定 trace 结果每一行输出的字符串长度。下面看看对 test 程序中-s参数的影响,指定-s 20,然后在 read 时输入一个超过 20 个字符的数字串:
strace -s 20 ./test read(0, 2222222222222222222222222 // 我们输入的2一共有25个 "22222222222222222222"..., 1024) = 26 // 而我们看到的结果中2只有20个默认情况下字符串最多显示 32 个字符(文件名除外,文件名一直全部输出)。当关心调用参数中的大块数据(如完整的 SQL 文本、协议报文)时,就需要调大-s;反之,当输出中夹杂大量二进制数据影响阅读时,可以调小-s让输出更清爽。
跟踪一个现有的进程
strace 不仅能自己初始化一个进程进行跟踪,还能挂接到现有进程上,参数-p就是起这个作用的:
strace -p pid挂接运行中的进程是生产环境排障的典型姿势:进程已经出现异常(卡死、高负载、句柄泄漏)时才介入,无需重启进程,直接观察其当前行为。挂接后按 Ctrl+C 即可停止跟踪并恢复进程正常运行。
综合实战:strace 分析 Oracle lgwr 进程
前面介绍了不少功能和参数,下面来看一个实用的综合案例:研究 Oracle 的 lgwr(Log Writer,日志写进程)进程,验证它是否像文档所述那样每 3 秒写一次 log 文件。考虑到 lgwr 写日志的触发条件比较多,需要找一个空闲的 Oracle 实例做这个实验。
第一步,获取 lgwr 进程的 PID:
ps -ef|grep lgwr oracle 5912 1 0 Nov12 ? 00:14:56 ora_lgwr_orcl得到 lgwr 的 PID 是 5912(ps -ef | grep的常规找进程方式可参考仓库中 ps 命令文档)。启动 strace,将跟踪结果输出到 lgwr.txt 文件中:
strace -tt -s 10 -o lgwr.txt -p 5912这里综合使用了三个参数:-tt输出微秒级时间戳(用于精确观察写入间隔)、-s 10截断字符串输出(lgwr 写入的数据块很大,只关心前 10 个字符即可)、-o lgwr.txt将结果写入文件(避免长时间跟踪刷屏)。过一会儿停止 strace,然后查看结果。由于输出较多,为了方便只查看 Oracle 写入 log 文件时用到的 pwrite 函数调用:
grep pwrite\(20 lgwr.txt为什么 grep 时用pwrite(20呢?因为当前机器打开的 log 文件句柄编号从 2 开始。具体查找方法是先找出当前活动的日志文件都有哪些,在 SQL*Plus 中执行:
select member, v$log.status from v$log, v$logfile where v$log.group#=v$logfile.group#;得到:
MEMBER STATUS -------------------------------------------------- ---------------- /db/databases/orcl/redo-01-a/redo-t01-g03-m1.log INACTIVE /db/databases/orcl/redo-03-a/redo-t01-g03-m2.log INACTIVE /db/databases/orcl/redo-02-a/redo-t01-g02-m1.log CURRENT /db/databases/orcl/redo-04-a/redo-t01-g02-m2.log CURRENT /db/databases/orcl/redo-01-a/redo-t01-g01-m1.log INACTIVE /db/databases/orcl/redo-03-a/redo-t01-g01-m2.log INACTIVE /db/databases/orcl/redo-02-a/redo-t01-g04-m1.log INACTIVE /db/databases/orcl/redo-04-a/redo-t01-g04-m2.log INACTIVE然后到 /proc 中查看该进程打开的文件句柄:
ll /proc/.5912/fd/得到:
lrwx------ 1 oracle dba 64 Dec 30 10:55 18 -> /db/databases/orcl/redo-01-a/redo-t01-g01-m1.log lrwx------ 1 oracle dba 64 Dec 30 10:55 19 -> /db/databases/orcl/redo-03-a/redo-t01-g01-m2.log lrwx------ 1 oracle dba 64 Dec 30 10:55 20 -> /db/databases/orcl/redo-02-a/redo-t01-g02-m1.log lrwx------ 1 oracle dba 64 Dec 30 10:55 21 -> /db/databases/orcl/redo-04-a/redo-t01-g02-m2.log lrwx------ 1 oracle dba 64 Dec 30 10:55 22 -> /db/databases/orcl/redo-01-a/redo-t01-g03-m1.log lrwx------ 1 oracle dba 64 Dec 30 10:55 23 -> /db/databases/orcl/redo-03-a/redo-t01-g03-m2.log lrwx------ 1 oracle dba 64 Dec 30 10:55 24 -> /db/databases/orcl/redo-02-a/redo-t01-g04-m1.log lrwx------ 1 oracle dba 64 Dec 30 10:55 25 -> /db/databases/orcl/redo-04-a/redo-t01-g04-m2.log对照 SQL 查询结果可知,状态为 CURRENT 的两个活动日志文件(group 2 的两个成员)对应的句柄是 20 和 21。至此,grep pwrite\(20 lgwr.txt的过滤条件就有了依据。得到的结果如下:
11:13:55.603245 pwrite(20, "\1\"\0\0J!"..., 1536, 4363264) = 1536 11:13:55.603569 pwrite(21, "\1\"\0\0M!"..., 1536, 4363264) = 1536 11:13:55.606888 pwrite(20, "\1\"\0\0M!"..., 1536, 4364800) = 1536 11:13:55.607172 pwrite(21, "\1\"\0\0M!"..., 1536, 4364800) = 1536 11:13:55.607934 pwrite(20, "\1\"\0\0P!"..., 1536, 4366336) = 1536 11:13:55.608199 pwrite(21, "\1\"\0\0P!"..., 1536, 4366336) = 1536 11:13:55.610260 pwrite(20, "\1\"\0\0S!"..., 1536, 4367872) = 1536 11:13:55.610530 pwrite(21, "\1\"\0\0S!"..., 1536, 4367872) = 1536 11:14:00.602446 pwrite(20, "\1\"\0\0V!"..., 1536, 4369408) = 1536 11:14:00.602750 pwrite(21, "\1\"\0\0V!"..., 1536, 4369408) = 1536 11:14:00.606386 pwrite(20, "\1\"\0\0Y!"..., 1536, 4370944) = 1536 11:14:00.606676 pwrite(21, "\1\"\0\0Y!"..., 1536, 4370944) = 1536 11:14:00.607900 pwrite(20, "\1\"\0\0\\"..., 1024, 4372480) = 1024 11:14:00.608161 pwrite(21, "\1\"\0\0\\"..., 1024, 4372480) = 1024 11:14:00.608816 pwrite(20, "\1\"\0\0^!"..., 1024, 4373504) = 1024 11:14:00.609071 pwrite(21, "\1\"\0\0^!"..., 1024, 4373504) = 1024 11:14:00.611142 pwrite(20, "\1\"\0\0`!"..., 1536, 4374528) = 1536 11:14:00.611454 pwrite(21, "\1\"\0\0`!"..., 1536, 4374528) = 1536 11:14:05.602804 pwrite(20, "\1\"\0\0c!"..., 1024, 4376064) = 1024 11:14:05.603119 pwrite(21, "\1\"\0\0c!"..., 1024, 4376064) = 1024 11:14:05.607731 pwrite(20, "\1\"\0\0e!"..., 1024, 4377088) = 1024 11:14:05.608020 pwrite(21, "\1\"\0\0e!"..., 1024, 4377088) = 1024 11:14:05.608690 pwrite(20, "\1\"\0\0g!"..., 1024, 4378112) = 1024 11:14:05.608962 pwrite(21, "\1\"\0\0g!"..., 1024, 4378112) = 1024 11:14:05.611022 pwrite(20, "\1\"\0\0i!"..., 1536, 4379136) = 1536 11:14:05.611283 pwrite(21, "\1\"\0\0i!"..., 1536, 4379136) = 1536从输出可以清楚读出:11:13:55 有一组写操作,随后 11:14:00、11:14:05 又各出现一组——两次写日志的时间间隔正好是 5 秒。每组写操作中,lgwr 对成员文件 20 和 21 交替以pwrite写入 1024/1536 字节的数据块(pwrite 的第三个参数是写入长度,第四个参数是文件偏移,返回值等于写入长度表示写入成功)。这验证了 lgwr 的周期性写入行为,也完整展示了一个「strace 挂接生产进程 + SQL 查询定位文件 + /proc 确认句柄 + 定向 grep 过滤」的组合排障方法论。
小结:何时使用 strace
回顾全文,strace 的典型使用场景可以归纳为四类:
- 程序「凭空」退出或行为异常:用
strace(或strace -f跟踪子进程)观察卡在哪个系统调用、收到什么信号,参考实例二的 SIGTERM 定位法; - 性能热点定位:用
-c得到系统调用统计表,或-T定位单次慢调用,参考实例三与常用参数一节; - 理解陌生程序的内部工作过程:用默认跟踪模式完整记录其启动、初始化、主循环的每一步系统调用,参考实例一;
- 生产环境诊断运行中的进程:用
-p挂接 +-o落盘 +-tt精确时间戳,配合 grep 过滤感兴趣的系统调用,参考综合实战中的 lgwr 案例。
掌握 strace 后,Linux 上「程序做了什么」不再是一个黑盒。它和 ltrace 命令文档(库函数层)、gdb 命令文档(交互调试层)共同构成了从系统调用到用户态代码的完整观察链条,是 Linux 工程师排障工具箱中不可或缺的一环。
- 文档
- 教程
【免费下载链接】linux-command
Linux命令大全搜索工具,内容包含Linux命令手册、详解、学习、搜集。https://git.io/linux
相关推荐
MATHC向量操作完全指南:从基础到高级的2D/3D向量处理
MATHC向量操作完全指南:从基础到高级的2D/3D向量处理 MATHC是一个纯C语言编写的轻量级数学库,专注于2D和3D向量处理,为游戏开发、图形编程等场景提
终极指南:如何在xv6-riscv中实现系统调用跟踪与strace工具功能
终极指南:如何在xv6 riscv中实现系统调用跟踪与strace工具功能 xv6 riscv是一个基于RISC V架构的教学操作系统,通过学习它可以深入理解操
操作系统嵌入式img2threejs 细节清单(Detail Inventory)实战指南:把参考图中的身份细节变成可门禁的结构化数据
img2threejs 细节清单(Detail Inventory)实战指南:把参考图中的身份细节变成可门禁的结构化数据 导读 detailInventory
AI 技能3D渲染代码生成
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考