把 Linux 想象成一家五星级酒店:你的程序是住客,内核是坐在总台的大堂经理。住客想办任何事——开门取文件、发网络请求、申请内存——都不能自己动手,必须拿起房间里的电话打给总台。这通电话,就是「系统调用」。可麻烦的是,当程序表现异常时——反复找不到文件、被拒绝访问、突然卡死——你根本不知道它到底在总台说了什么。strace 就是酒店装在电话线上的「录音设备」:它一字不漏地记下程序每次拨出的电话——调用了哪个系统调用、传了什么参数、总台回了什么结果。而 ltrace 则是更靠近住客的「随身记录仪」,记的是住客叫服务员跑腿的日常请求(库函数)。有了这两样工具,程序的一切“对外请求”都变成了可回放的通话记录,排查问题就像翻录音带一样简单——这正是后端工程师梦寐以求的“上帝视角”。

岗位关联

对 AI 从业者的价值:模型服务、数据处理脚本在生产环境崩溃时,strace 能把“程序到底卡在哪、缺什么、谁拒绝了它”一锤定音,是后端与运维岗位最实用的五分钟排障利器。

目录
  1. 可视化演示
  2. 追踪工具概览
  3. 基础使用入门
  4. 输出信息解读
  5. 过滤器应用
  6. 统计与耗时
  7. 实战问题排查
  8. ltrace库跟踪
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示strace拦截程序系统调用的过程,跟踪模式实时显示调用流,统计模式展示各调用次数和耗时分布。

本节配有交互动效(进入视口后自动加载,约 3KB)。

跟踪模式 strace拦截程序发出的每个系统调用 实时显示调用名、参数和返回值 strace -p PID

追踪工具概览

酒店的「电话系统」分两层:一层是住客直接打给总台的直线,记录的是必须请内核出面的大事——打开文件、收发网络包、申请内存,这叫「系统调用」;另一层是住客唤服务员来跑腿的日常吩咐,比如 printf 打印一行、malloc 申请一块内存,这叫「库函数」。strace 和 ltrace 这对工具,正好分别在这两层装上「录音设备」。前者盯大事,后者盯小事,分工明确。

在 Linux 的世界里,普通程序运行在「用户态」,凡是要碰文件、开网络、申请内存这些“需要特权”的操作,都必须通过系统调用请求内核代办。为了方便开发者,glibc 标准库把大量系统调用包装成了友好的库函数——你写代码时随手调用的 fopen、printf,其实只是“服务员”,它们内部最终会帮你拨通总台的电话,触发 open、write 这样的系统调用。

strace 利用 Linux 的 ptrace 机制附着到目标进程上:内核每收到一次系统调用,strace 就能截获它的名字、参数和返回值,像录音机一样逐条记录。ltrace 则更进一步,它跟踪的是动态库函数调用——比如 malloc、strlen、puts——这些调用发生在用户态,比系统调用更接近程序自身的逻辑。安装也很简单:Debian/Ubuntu 用 apt install strace ltrace,CentOS 用 yum install strace ltrace。

两套设备装好后,最直观的收获是:当程序报“文件不存在”时,你不再需要猜它找的是哪个路径、以什么方式打开——回放一下 open 那条通话记录,真相立刻摆在眼前。

Program→libcglibc→syscallKernel\text{Program} \xrightarrow{\text{libc}} \text{glibc} \xrightarrow{\text{syscall}} \text{Kernel}
这行公式描述了一条请求从用户程序出发,经过 glibc 库函数包装,最终以系统调用形式到达内核的完整链路。
  1. 程序起点请求从应用程序发出,此时程序还停留在用户态,不知道内核的细节。
    Program\text{Program}
  2. 库函数封装程序先调用 glibc 提供的函数,如 fopen、printf,这些函数在用户态完成参数整理和格式转换。
    →libcglibc\xrightarrow{\text{libc}} \text{glibc}
  3. 系统调用落地glibc 把任务包装成内核认识的请求,如 open、write,触发陷入内核态,由内核真正执行。
    →syscallKernel\xrightarrow{\text{syscall}} \text{Kernel}

基础使用入门

录音设备买回来,第一步是学会开机。最简单的用法就是把要监控的命令直接跟在 strace 后面,比如 strace ls -l,回车之后,ls 执行期间拨出的每一通“电话”都会一行行刷在屏幕上。这个动作就像把录音机接到总台线路上,等着住客打电话。

命令的基本格式是 strace [选项] 命令 [参数]。不带任何选项时,跟踪结果默认输出到标准错误流 stderr,而不是标准输出 stdout——这是为了避免监听日志混进程序的正常输出。如果担心刷屏淹没现场,可以加 -o log.txt 把全部记录写进文件,结束后再慢慢回放。

实际使用中有两种追踪场景:追踪一个新启动的程序,直接在 strace 后面跟命令名;附加到一个正在运行的进程,则要用 -p PID,比如 strace -p 1234。多进程程序要加 -f,让 strace 跟着 fork 出来的子进程一起跟踪;想停止录音就按 Ctrl+C,此时 strace 会打印统计信息并退出。

就像录音机要选对磁带一样,-o 指定输出文件能避免海量输出刷屏,-f 则确保不放过“住客让服务员代拨”的子进程通话。学会这四项基础操作,你就已经能独立录制“通话磁带”了。

strace [options] command [args]\text{strace} \ [\text{options}] \ \text{command} \ [\text{args}]
这是 strace 的核心命令骨架:strace 本身固定,方括号内是可选的监听开关,后面是你要“录音”的目标程序。
  1. strace 命令启动监听工具本身,它是整个排障动作的入口。
    strace\text{strace}
  2. 方括号选项可选的开关集合,如 -o 指定输出文件、-f 跟踪子进程、-p 附加已有进程。
    [options][\text{options}]
  3. 目标命令要被录制通话的程序名,strace 会启动它并全程监控。
    command\text{command}
  4. 程序参数传给目标程序的命令行参数,会原样转发给被跟踪的程序。
    [args][\text{args}]

输出信息解读

录音带倒出来,上面是一行行格式工整的通话记录。看懂格式,破案就成功了一半。每一条记录长这样:open("/etc/passwd", O_RDONLY) = 3。左边是系统调用名和参数,等号右边是返回值——整个格式就像电话账单上的“拨打号码、通话时长、结果状态”。

返回值是破案的关键线索。如果返回值是非负整数,比如 3,表示调用成功,程序拿到了文件描述符 3,后续的 read、write 都靠这个编号认路。如果返回 -1,说明调用失败,同时内核会设置一个叫 errno 的错误码。strace 很贴心,会直接把错误名挂在返回值后面,例如 = -1 ENOENT (No such file or directory)。

常见 errno 需要牢记:ENOENT 表示文件或目录不存在;EACCES 表示权限不足;ECONNREFUSED 表示网络连接被拒绝;ETIMEDOUT 表示连接超时。这些错误码就像酒店总台给你的“拒绝理由编号”,每个编号对应一种明确的拒绝对策。

就像听到电话里“您拨打的号码是空号”一样,ENOENT 告诉你路径拼错了或文件压根不在;EACCES 则说明门锁着、你没有钥匙。把 errno 这本“拒绝理由密码本”装进脑子,绝大多数现场都能当场定性。

syscall(args)=ret,ret=−1⇒errno\text{syscall}(\text{args}) = \text{ret}, \qquad \text{ret} = -1 \Rightarrow \text{errno}
一行通话记录的结构:左边是调用的名字和参数,右边是结果;一旦结果是 -1,必须去查 errno 这个“密码本”才能知道失败原因。
  1. 调用名与参数等号左边记录的是系统调用名和括号内的参数,参数决定了这次请求的内容。
    syscall(args)\text{syscall}(\text{args})
  2. 成功返回值返回非负整数,对文件类调用来说就是文件描述符,是后续操作的凭证。
    =ret= \text{ret}
  3. 失败与 errno返回 -1 表示失败,真正的失败原因记录在 errno 变量中,如 ENOENT。
    ret=−1⇒errno\text{ret} = -1 \Rightarrow \text{errno}

过滤器应用

录音设备若不筛选,一天能录上千条通话。真正的高手会先设置“关键词过滤”,只听想听的。strace 的 -e 参数就是这个筛选开关,它决定了哪些通话进录音带、哪些直接丢弃。

最常用的是 -e trace=调用名 白名单,例如 strace -e trace=open,read ./app 只显示 open 和 read 两条调用,其他一律静音。如果想按类别过滤,strace 还预置了几组“主题套餐”:trace=file 看所有文件类调用,trace=network 看网络类,trace=process 看进程管理类,trace=signal 看信号处理类。反过来,用感叹号取反:trace=!write 表示忽略所有 write 调用。

其他常用参数组合:-f 递归跟踪子进程,让录音覆盖整个“家族”;-o 把输出写进文件,避免终端刷屏;-s 控制字符串参数显示的截断长度,默认 32 字节;-p PID 附加到指定进程。多个条件可以用逗号组合,灵活得像给录音机装了智能语音识别。

好比大堂经理只想听“投诉电话”这类关键词,过滤后录音带里只留下有用的片段。尤其是排查线上问题时,全量跟踪会产生几十 MB 日志,而一条精准的 -e trace=network 就能把视野锁定在网络层,效率翻倍。

−e trace=open,read,write-e \ \text{trace} = \text{open}, \text{read}, \text{write}
这是一条典型的过滤表达式:只录制 open、read、write 这三个系统调用的通话,其余全部跳过。
  1. -e 选项进入“筛选设置”模式的开关,后面跟的表达式决定监听范围。
    −e-e
  2. trace 字段按系统调用名过滤,等号右边是要保留的调用名单。
    trace=名单\text{trace} = \text{名单}
  3. 逗号名单多个调用名用逗号分隔,表示这些调用全部进入录音范围。
    open,read,write\text{open}, \text{read}, \text{write}

统计与耗时

光看通话内容还不够,有时你想知道“哪通电话耗时最长、哪路电话打得最频繁”。每到月底,酒店会打印一份服务账单:每个服务项目被叫了多少次、累计占用了多久、失败了多少回。strace 的 -c 参数就是这张账单。

用法是 strace -c 命令,程序跑完后 strace 会输出一张汇总表,内容包括:每个系统调用的调用次数(calls)、累计耗时(seconds)、单次平均耗时(usecs/call)、耗时占比(% time)和错误次数(errors)。想更细致地看单次调用耗时,用 -T 给每条调用后面追加快耗时秒数;用 -r 则打印相对上一条调用的时间戳,适合观察调用节奏。

性能分析时推荐两步走:先 -c 看占比,找到最耗时的调用类型;再用 -T 定位具体哪一次调用异常慢。例如发现 write 占比高达 80%,往往是程序频繁小写导致系统调用开销过大,改成批量写入就能显著提速。

这张“账单”就像酒店给你列出“客房服务耗时 3 小时、叫车仅 30 分钟”,哪项服务最拖时间一目了然。性能瓶颈不会藏在代码注释里,它就写在 strace 的统计表中——占比最高的那个调用,就是你要下刀的地方。

ratio=ti∑j=1ntj×100%\text{ratio} = \frac{t_i}{\sum_{j=1}^{n} t_j} \times 100\%
这是统计表中“耗时占比”的计算方式:某类调用的总耗时除以所有调用的总耗时,再换算成百分比。
  1. 分子 t_i第 i 类系统调用的累计耗时,把这类调用每次消耗的时间全部相加。
    tit_i
  2. 分母求和所有系统调用耗时之和,作为占比的基准总量。
    ∑j=1ntj\sum_{j=1}^{n} t_j
  3. 百分比转换乘以 100% 得到该类调用的耗时占比,占比越大越值得优化。
    ×100%\times 100\%

实战问题排查

工具齐了,来看四个最常见的“客人投诉”案例。第一个:程序启动就报文件找不到。跑 strace -f -o log.txt ./app,然后在日志里搜 ENOENT,看到 open("config.yaml", O_RDONLY) = -1 ENOENT——原来是工作目录不对,程序根本没找到它以为存在的配置文件。路径问题的根源往往藏在相对路径里。

第二个:权限拒绝。日志中出现 EACCES,用 ls -l 检查文件属主和权限,发现运行用户不是文件属主,也没有读权限。这就像住客拿着一张别人的房卡去开门,总台当然拒绝。第三个:网络连接异常。strace -e trace=network ./client 看到 connect 返回 ECONNREFUSED,说明目标端口没人监听;返回 ETIMEDOUT,多半是防火墙丢了包。

第四个:程序卡死。这是线上最惊险的场景。strace -p <pid> 附加后,屏幕停在最后一条调用上不再前进——卡在哪就停在哪。如果是 read 等待输入,说明在等数据;如果是 futex(FUTEX_WAIT),基本可以判定锁竞争或死锁。这时再看代码里对应的锁逻辑,往往一击即中。

四个案例就像酒店的四类投诉:走错门(路径问题)、没钥匙(权限问题)、对方不接电话(网络问题)、电话占线(卡死)。有了通话录音带,每类投诉都能当场破案,不需要再猜。

ENOENT⇒not found,EACCES⇒permission denied,ECONNREFUSED⇒refused\text{ENOENT} \Rightarrow \text{not found}, \qquad \text{EACCES} \Rightarrow \text{permission denied}, \qquad \text{ECONNREFUSED} \Rightarrow \text{refused}
三个高频错误码对应三种常见故障:文件缺失、权限不足、连接被拒——看到一个错误码就知道该往哪个方向查。
  1. ENOENT 判断看到这个错误码,直接检查路径拼写、文件是否存在、工作目录是否正确。
    ENOENT⇒not found\text{ENOENT} \Rightarrow \text{not found}
  2. EACCES 判断看到这个错误码,检查文件权限、属主身份、SELinux 等访问控制。
    EACCES⇒permission denied\text{EACCES} \Rightarrow \text{permission denied}
  3. ECONNREFUSED 判断网络层被对端拒绝,检查端口监听状态和防火墙规则。
    ECONNREFUSED⇒refused\text{ECONNREFUSED} \Rightarrow \text{refused}

ltrace库跟踪

有时候问题出在“服务员”身上而不是“大堂经理”——比如字符串处理出错、内存悄悄膨胀。这时候需要随身记录仪 ltrace,它记录的是住客与服务员之间的日常互动,也就是库函数调用。

ltrace 的用法和 strace 几乎同构:ltrace 命令 跟踪库函数调用;-e 按函数名过滤;-c 输出统计;-p PID 附加运行进程。它显示的是 malloc、strlen、printf 这类动态库函数。一个典型的输出长这样:printf("hello") = 6,表示 printf 这个库函数被调用,返回了 6 个字符。

实战中经常两级配合:先用 ltrace 看程序调了哪些库函数,发现可疑点后再下沉到 strace 看对应的系统调用。比如 ltrace 显示 malloc 被疯狂调用且内存曲线暴涨,strace 发现底层 brk 和 mmap 频繁扩大堆区,就能顺藤摸瓜定位到内存分配失控的代码路径。

有了 strace(总台录音)和 ltrace(服务员记录仪)两套设备,从程序逻辑到内核动作全程可追溯——这就是 Linux 排障的完整监控链条。先看服务员怎么跑腿,再看总台怎么批示,任何异常行为都无处遁形。

printf(fmt,… )→glibcwrite(fd,buf,len)\text{printf}(\text{fmt}, \dots) \xrightarrow{\text{glibc}} \text{write}(\text{fd}, \text{buf}, \text{len})
这行公式展示了库函数与系统调用的“上下级关系”:printf 只是台前的服务员,真正把数据送出去的是幕后的 write 系统调用。
  1. 库函数调用程序调用 printf,这是用户态可见的库函数,ltrace 在这一层监听。
    printf(fmt,… )\text{printf}(\text{fmt}, \dots)
  2. 内部转换glibc 负责把格式化后的数据整理成缓冲区,再交给内核接口。
    →glibc\xrightarrow{\text{glibc}}
  3. 系统调用落地write 真正把缓冲区内容写入文件描述符,strace 在这一层监听。
    write(fd,buf,len)\text{write}(\text{fd}, \text{buf}, \text{len})

AI 应用场景

ML 训练脚本“找不到文件”的迷案

阶段:数据准备 / 故障诊断

解决的问题:训练脚本在 IDE 里能跑、放到定时任务就报 FileNotFoundError,日志却没给出真实路径。用 strace -f -o train.log python train.py 后在日志里搜索 ENOENT,看到 open("data/train.csv") = -1,发现调度系统的工作目录是 /root,而相对路径 data 根本不存在——这就是典型的“走错门”问题。

具体案例:某 AI 平台的定时训练任务凌晨 3 点失败,运维用 strace 定位到 open("/root/data/train.csv") 返回 ENOENT,原来是 cron 没有切换工作目录。把脚本里的相对路径改成绝对路径后,任务恢复稳定运行。

推理服务接口“慢得像蜗牛”

阶段:模型部署 / 性能分析

解决的问题:接口 P99 延迟高,应用日志看不出瓶颈。用 strace -c -p <pid> 采样后发现 read 调用占比 68%,再用 -T 看到每次 read 都只读 4KB 小数据,磁盘 IOPS 被打满——这是“账单”直接指认的瓶颈。

具体案例:某推荐系统的向量文件用单条 read 逐条加载,strace 显示 read 调用高达 12 万次/分钟。改为 mmap 内存映射后,P99 延迟从 800ms 降到 90ms,模型加载速度提升近 9 倍。

线上服务“卡死不动”的应急定位

阶段:推理服务 / 线上应急

解决的问题:服务无响应、CPU 占用低,直接重启会丢失现场。strace -p <pid> 附加后,输出停在 futex(..., FUTEX_WAIT) 不再前进,说明线程在等待锁——配合 pstack 打印线程栈,即可确认死锁链条。

具体案例:某大模型推理服务偶发假死,strace 停在 futex 等待,定位到缓存组件的锁未释放,升级版本后彻底解决。整个过程不到 10 分钟,避免了盲目重启导致的事故升级。

Python 演示

python55 行
import reimport subprocess  def run_strace(target_cmd):    """在 strace 下运行目标命令,返回逐行原始输出."""    trace_log = "/tmp/strace_demo.log"    try:        subprocess.run(            ["strace", "-o", trace_log, "-f", "-e", "trace=file"] + target_cmd,            capture_output=True,            timeout=10,        )    except FileNotFoundError:        print("未找到 strace,请先安装: sudo apt install strace")        return []    with open(trace_log, "r", errors="ignore") as fh:        return fh.readlines()  def parse_calls(lines):    """解析形如 open(\"/etc/hostname\", O_RDONLY) = 3 的调用行."""    pattern = re.compile(r"^(\w+)\((.*)\)\s*=\s*(-?\w+)\s*(.*)")    stats = {}    for line in lines:        m = pattern.match(line.strip())        if not m:            continue        name, args, ret, extra = m.groups()        item = stats.setdefault(name, {"count": 0, "errors": 0})        item["count"] += 1        if ret == "-1":            item["errors"] += 1    return stats  def print_report(stats):    """按次数排序打印调用统计表."""    total = sum(v["count"] for v in stats.values())    print("系统调用统计:")    print("%-16s %-8s %-8s %s" % ("调用名", "次数", "失败", "占比"))    for name, v in sorted(stats.items(), key=lambda kv: -kv[1]["count"]):        ratio = 100.0 * v["count"] / total if total else 0.0        print("%-16s %-8d %-8d %.1f%%" % (name, v["count"], v["errors"], ratio))  def main():    import sys    cmd = sys.argv[1:] or ["cat", "/etc/hostname"]    stats = parse_calls(run_strace(cmd))    print_report(stats)  if __name__ == "__main__":    main()
目的:这段脚本自动运行 strace 捕获目标命令的文件类系统调用,解析成“调用名、参数、返回值”结构,并输出调用次数、失败数和占比的统计报表——相当于把文章的 -c 统计能力搬进 Python,方便二次分析。
  1. ["strace", "-o", trace_log, "-f", "-e", "trace=file"] + target_cmd组装 strace 命令,-f 跟踪子进程,-e 只保留文件类调用,减少噪音
  2. pattern = re.compile(r"^(\w+)\((.*)\)\s*=\s*(-?\w+)\s*(.*)")用正则匹配“调用名(参数) = 返回值”的标准输出格式
  3. stats.setdefault(name, {"count": 0, "errors": 0})第一次见到某调用时自动初始化计数器,避免手动判断
  4. item["count"] += 1每出现一次调用计数加一,等价于 strace -c 里的 calls 列
  5. if ret == "-1": item["errors"] += 1返回值是 -1 就记一次失败,对应 errors 列
  6. sorted(stats.items(), key=lambda kv: -kv[1]["count"])按调用次数降序排列,让最频繁的调用排在最前面
  7. ratio = 100.0 * v["count"] / total if total else 0.0计算每种调用占比,对应正文里的耗时占比公式

常见误区

误区听说 strace 用 ptrace 拦截所有系统调用,程序会慢十倍以上,线上千万不能用,用了会被领导骂。

正解strace 确实有性能开销,因为每个系统调用都要经过 ptrace 拦截,但开销并没有夸张到“慢十倍”。用 -e trace= 过滤只保留关心的调用、用 -p 短暂附加观察几秒、结束后立即分离,开销完全可控。相比盲目猜测故障原因浪费的几小时,这几十毫秒的代价非常值得。

为何常见:ptrace 机制确实会让目标进程在每次系统调用时暂停,大家以讹传讹把“有开销”放大成“不能碰”,忽略了过滤器这个救命的开关。

误区程序卡死、算错、崩溃,strace 一下就能看到全部原因,它就是程序界的监控摄像头。

正解strace 只能看到系统调用这一层——程序什么时候打开文件、读写网络、申请内存。它看不到程序内部的变量值、循环逻辑、函数调用栈。内存越界、死循环这些纯粹发生在用户态的运算,strace 是瞎的,需要配合 gdb、perf 等工具才能看清。

为何常见:初学者看到 strace 能输出大量信息,误以为“系统调用 = 程序全部行为”,其实这只是程序与内核交互的边界。

误区打开日志一看全是 `= -1 EAGAIN`,吓得赶紧重启服务,以为程序已经病入膏肓。

正解非阻塞 IO 场景下,EAGAIN 和 EWOULDBLOCK 是正常现象,表示“现在没数据,先干别的去”,程序恰恰是健康地在轮询。真正要警惕的是 ENOENT、EACCES、EPERM 这类不可重试的错误。判断标准不是“返回 -1”,而是“错误码是否致命、上下文是否合理”。

为何常见:把“调用失败”和“程序 bug”直接画等号,忽略了 errno 本身分可重试与不可重试两类,这是对错误码体系理解不透彻。

误区都是跟踪工具,名字也像,随便用一个就行,反正输出看起来差不多。

正解两者跟踪层级完全不同。strace 看的是内核边界上的系统调用,ltrace 看的是用户态的库函数调用。很多库函数压根不触发系统调用——比如 strlen 纯粹在内存里数数,strace 永远看不到它。想定位内存分配风暴要用 ltrace,想定位文件访问路径要用 strace。

为何常见:二者输出格式相似、安装方式相同,加上“工具名字长得像”,让很多人误以为只是不同的发行版。

误区程序自己会打日志,日志里写了“文件不存在”我就信它,直接去检查文件就行。

正解程序日志是程序“自己写的自白”,会隐瞒细节、甚至撒谎。比如程序报了“配置文件缺失”,但真实原因可能是它先尝试打开了一个错误的相对路径。strace 是内核视角的客观录音,能看到程序没说出口的动作——实际打开了几次、每次用的什么路径、被谁拒绝。应用日志负责告诉你“出事了”,strace 负责告诉你“到底发生了什么”。

为何常见:过度信任应用层日志,不知道有“第三方视角”的客观记录存在,这也正是 strace 最独特的价值所在。

面试题

Q1. strace 默认把跟踪结果输出到哪里?

单选题 · easy

  • A.输出到标准输出设备
  • B.输出到标准错误设备← 正确答案
  • C.输出到系统的日志文件
  • D.直接存入内核的缓冲区

答案:B。strace 默认把跟踪结果输出到标准错误流 stderr,而不是标准输出流 stdout,这是为了方便把程序的正常输出重定向走,避免监听日志污染结果。就像录音设备的“外放喇叭”不走住客常用的通道,防止干扰正常通话。C 和 D 都不符合默认行为。

Q2. 想追踪一个新启动的程序的系统调用,正确的用法是?

单选题 · easy

  • A.在 strace 后面跟程序名← 正确答案
  • B.用 strace -p 跟程序名
  • C.把 strace 放在程序名后面
  • D.用 strace --pid 跟程序名

答案:A。追踪新启动程序的用法是 strace 程序名,把目标程序直接跟在 strace 后面。-p 后面只能跟进程号 PID,不能跟程序名;把 strace 放程序名后面是错误语法;--pid 并不是 strace 的合法长选项。这就像开录音机要先放目标“磁带”一样,命令顺序决定了监听对象。

Q3. 要附加到 PID 为 2024 的正在运行进程进行跟踪,应使用?

单选题 · mid

  • A.使用 strace 2024 进程号
  • B.使用 strace -p 2024 进程号← 正确答案
  • C.使用 strace -f 2024 进程号
  • D.使用 strace -a 2024 进程号

答案:B。附加到已运行进程必须用 -p PID 参数,适合线上程序卡死时“临时接上录音”。strace 2024 会把 2024 当成程序名去执行而报错;-f 是跟踪子进程,不是附加;-a 不是合法选项。记住核心口诀:新程序直接跟,老进程用 -p。

Q4. 想统计程序各系统调用的次数和耗时占比,应使用哪个参数?

单选题 · mid

  • A.使用 strace -c 统计汇总← 正确答案
  • B.使用 strace -T 逐次计时
  • C.使用 strace -r 精度计时
  • D.使用 strace -s 输出截断

答案:A。-c 会在程序结束后输出汇总表,包含每个系统调用的次数、累计耗时和耗时占比,是性能分析的首选。-T 是给每条调用追加耗时,-r 是相对时间戳,-s 控制字符串截断长度。就像酒店月底打印服务账单,-c 直接给你“哪项服务最耗时”的结论。

Q5. 关于 strace -e trace=open,read 的说明,正确的是?

多选题 · hard

  • A.只显示 open 和 read 两种调用← 正确答案
  • B.open、read 之外的调用被忽略← 正确答案
  • C.等效于跟踪 file 类别全部调用
  • D.同时启用调用统计汇总模式

答案:AB。-e trace=open,read 是白名单过滤,只显示 open 和 read 两个系统调用,其他调用一律不显示,所以 A 和 B 正确。它不等同于 trace=file——file 类别包含更多文件相关调用;也不会启用统计,统计需要单独的 -c 参数。这就像给录音机设了关键词,只保留你关心的通话。

Q6. strace 输出 open("/a.txt", O_RDONLY) = -1 ENOENT,说明什么?

单选题 · easy

  • A.打开成功且描述符为 -1
  • B.文件不存在导致打开失败← 正确答案
  • C.文件无读权限被拒绝
  • D.文件被其它进程占用中

答案:B。ENOENT 是 “No such file or directory“ 的缩写,表示文件或目录不存在,打开操作失败。权限不足会返回 EACCES,文件被占用通常返回 EAGAIN 或 EBUSY。就像电话里听到”您拨打的号码是空号”,ENOENT 就是告诉你这个路径根本不存在。

Q7. ltrace 与 strace 跟踪层级的关键区别是?

单选题 · mid

  • A.ltrace 跟踪库函数而非系统调用← 正确答案
  • B.ltrace 跟踪系统调用而非库函数
  • C.ltrace 只能应用于编译型语言
  • D.ltrace 能查看函数内部的计算

答案:A。ltrace 跟踪的是动态库函数调用(如 printf、malloc),发生在用户态;strace 跟踪的是系统调用,发生在内核边界。二者层级不同,不能互相替代。ltrace 并不局限于编译型语言,也看不到函数内部的计算过程——它只记录“调用了谁”。

Q8. 线上服务无响应时,用 strace 定位卡顿点的合理做法是?

多选题 · hard

  • A.用 strace -p 附加进程观察← 正确答案
  • B.看最后一条调用是否阻塞← 正确答案
  • C.立即杀掉进程再重启观察
  • D.用 ltrace 查看内核侧状态

答案:AB。定位卡死要先用 strace -p 附加观察,屏幕停在哪个系统调用上,就说明卡在哪里——比如停在 futex 是等锁,停在 read 是等数据。杀掉重启会丢失现场,属于最后的兜底手段而非定位手段;ltrace 看的是库函数,不是内核状态。先录下“卡住的那一帧”,才能对症下药。

Q9. strace 的 -f 选项起什么作用?

单选题 · mid

  • A.跟随子进程一并跟踪← 正确答案
  • B.表示只跟踪文件类调用
  • C.强制以管理员身份运行
  • D.输出完整的不截断参数

答案:A。-f(follow)让 strace 同时跟踪 fork、exec 出来的子进程。多进程程序(如 nginx、Java 应用)如果不加 -f,只能看到主进程的调用,子进程的“通话”全部漏掉。它不负责文件过滤、权限提升或参数截断,那些是 -e、sudo、-s 的职责。

Q10. 关于 strace -c 汇总表中 calls 列的解读,正确的是?

多选题 · extreme

  • A.记录该调用的调用总次数← 正确答案
  • B.包含返回错误的调用计数← 正确答案
  • C.表示该调用的累计消耗时间
  • D.表示每秒完成的调用数量

答案:AB。-c 汇总表中的 calls 列是该系统调用被调用的总次数,既包含成功的调用也包含返回错误的调用(错误数单独显示在 errors 列),所以 A 和 B 正确。累计耗时对应 seconds 列,每秒调用数量不是 strace 的标准输出列。读懂这一列,才能正确判断“哪个调用最频繁”与“哪个调用最耗时”是两个不同维度的瓶颈。