运维知识
悠悠
2026年4月23日

线上系统出问题了?strace工具让你分分钟定位根因!

说到线上排查问题,我想起前段时间遇到的一个头疼事。有个服务突然变得特别慢,用户投诉一大堆,领导也在催。各种监控指标看起来都正常,CPU、内存、网络都没啥异常,但就是慢得要死。

这种时候你说慌不慌?表面上风平浪静,实际上程序内部不知道在搞什么鬼。传统的监控工具告诉你的信息有限,日志也没发现啥异常。这时候就需要深入到系统调用层面去看看到底发生了什么。

今天我就来聊聊strace这个神器,它可以说是我们运维人员手里的"透视镜",能够清楚地看到程序和操作系统之间的每一次交互。

什么是strace?

strace就是system call trace的缩写,顾名思义就是用来跟踪系统调用的工具。你可以把它理解成一个"窃听器",专门监听程序和内核之间的对话。

每当程序需要做一些操作,比如读写文件、网络通信、创建进程等等,都需要通过系统调用来请求内核帮忙。而strace就能把这些"请求"全部记录下来,包括调用了什么函数、传递了什么参数、返回了什么结果,甚至连耗时多长都能告诉你。

我记得刚开始接触strace的时候,看到那密密麻麻的输出还挺懵的。但用熟了之后真的是爱不释手,很多看似复杂的问题都能通过它快速定位。

基础使用方法

最简单的用法就是直接跟踪一个程序的执行过程:

strace ./my_program

这样就能看到my_program运行过程中的所有系统调用了。不过输出信息会很多,刷屏速度特别快,建议把结果重定向到文件里:

strace -o output.txt ./my_program

这样所有的跟踪信息就保存到output.txt文件中了,可以慢慢分析。

有时候我们不关心所有的系统调用,只想看特定的几种,比如只关心文件操作相关的调用:

strace -e open,read,write ./my_program

这个参数特别有用,能够过滤掉很多无关的噪音,让你专注于关心的部分。

实战案例:排查文件访问问题

我来分享一个真实的案例。之前有个应用启动特别慢,用户反馈说要等好几分钟才能正常使用。

一开始我们怀疑是数据库连接的问题,各种调优数据库配置,结果没什么效果。后来想到用strace来看看启动过程中到底在干什么:

strace -o startup.log -e trace=file ./myapp

这里我用了trace=file参数,只关注文件相关的系统调用。分析日志后发现了问题所在:

openat(AT_FDCWD, "/usr/share/fonts/font1.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/fonts/font2.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/fonts/font3.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
...

原来程序在启动时要加载几百个字体文件,但这些文件在服务器上根本不存在!每次尝试打开文件都会失败,然后程序还会重试,导致启动时间被大大延长。

找到根因后解决就简单了,要么安装对应的字体包,要么修改程序配置不加载这些字体。最终选择了后者,启动时间从几分钟缩短到了几秒钟。

这个案例说明了strace的威力,有时候问题的根因可能完全超出你的想象。

跟踪运行中的进程

除了跟踪新启动的程序,strace还能attach到已经运行的进程上:

strace -p <PID>

这个功能在生产环境中特别有用。比如某个服务突然CPU使用率飙高,但又不能重启,这时候就可以用strace attach上去看看它在干什么。

我遇到过一个Java应用突然CPU占用100%的情况,用strace attach上去后发现:

futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
...

大量的futex调用在疯狂执行,这通常表示线程间的锁竞争非常激烈。后来配合jstack等工具进一步分析,发现是代码中的一个死锁导致的。

分析网络相关问题

strace在排查网络问题时也很给力。比如程序连接数据库很慢,可以这样跟踪:

strace -e trace=network ./myapp

或者更具体一点:

strace -e connect,send,recv ./myapp

我曾经遇到过一个奇怪的问题,应用连接Redis总是超时。从监控看Redis服务器完全正常,网络也没问题。用strace跟踪后发现:

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("192.168.1.100")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT}], 1, 5000) = 0 (Timeout)

发现程序确实在尝试连接Redis,但connect调用返回了EINPROGRESS,然后poll等待5秒钟后超时。这说明网络层面的连接建立就有问题。

进一步排查发现是防火墙规则的问题,某个安全策略阻止了到Redis服务器的连接。修改防火墙规则后问题立即解决。

性能分析技巧

strace还有一个很实用的功能就是统计系统调用的耗时:

strace -c ./myapp

这会在程序结束后显示一个统计表格,告诉你哪些系统调用耗时最多、调用次数最频繁等信息。对于性能优化来说特别有价值。

比如某次分析一个批处理程序时,发现write调用占用了大部分时间:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 89.23    2.456780        2457      1000           write
  5.12    0.141023         141      1000           read
  3.45    0.094856          95      1000           open
  2.20    0.060541          61      1000           close

这说明程序在I/O写入上花费了太多时间。进一步分析发现程序每次只写很少的数据,但调用次数很频繁。后来通过缓冲机制批量写入,性能提升了好几倍。

过滤和定制输出

实际使用中,strace的输出往往非常多,需要合理过滤才能找到有用信息。除了前面提到的-e参数,还有一些其他的过滤选项:

只看失败的系统调用:

strace -e trace=all -e status=failed ./myapp

只看特定的信号:

strace -e signal=all ./myapp

显示时间戳:

strace -t ./myapp

显示相对时间:

strace -r ./myapp

这些选项可以组合使用,根据具体需求来定制输出格式。

注意事项和最佳实践

使用strace时有几个需要注意的地方:

性能影响:strace会显著降低程序的执行速度,因为每个系统调用都需要被拦截和记录。在生产环境使用时要谨慎,最好在低峰期或者测试环境中使用。

权限问题:要跟踪其他用户的进程,通常需要root权限。而且有些系统调用可能涉及敏感信息,使用时要注意安全性。

输出量大:对于长时间运行的程序,strace的输出可能会非常庞大。建议使用-o参数将输出重定向到文件,并且合理使用过滤选项。

我个人的经验是,刚开始使用strace时不要被复杂的输出吓到。多看几次就会发现规律,很多系统调用的含义其实很直观。比如open就是打开文件,read就是读取数据,connect就是建立连接等等。

结合其他工具使用

strace虽然强大,但单独使用有时候还不够。我通常会结合其他工具一起使用:

  • 配合ps、top查看进程状态
  • 配合netstat、ss查看网络连接
  • 配合lsof查看打开的文件
  • 配合tcpdump分析网络包

这样能够从多个角度全面分析问题,提高排查效率。

比如某次排查一个内存泄漏问题时,先用top发现内存使用率持续增长,然后用strace跟踪发现程序在不停地分配内存但很少释放,最后配合valgrind等内存分析工具定位到了具体的代码位置。

实战演练

我建议大家平时多练练手,可以用strace跟踪一些常见的命令看看它们的行为:

strace ls /tmp
strace cat /etc/passwd
strace ping -c 3 baidu.com

通过观察这些简单命令的系统调用,能够加深对Linux系统工作原理的理解。

还可以写一些简单的测试程序,故意制造一些问题,然后用strace来排查。比如:

#include <stdio.h>
#include <unistd.h>

int main() {
    FILE *fp = fopen("/nonexistent/file.txt", "r");
    if (fp == NULL) {
        perror("fopen failed");
        return 1;
    }
    fclose(fp);
    return 0;
}

编译运行这个程序,然后用strace跟踪:

gcc test.c -o test
strace ./test

你会看到程序尝试打开一个不存在的文件,系统调用返回错误,这样就能直观地理解程序的执行流程。

总的来说,strace是每个运维人员都应该掌握的重要工具。它能够帮助我们深入理解程序的行为,快速定位各种奇怪的问题。虽然刚开始可能觉得输出信息有点复杂,但只要多练习几次,很快就能上手。

在实际工作中,我经常把strace和其他监控工具结合起来使用,形成一套完整的问题排查流程。遇到问题时不要慌,先用基础监控工具看看大概情况,然后用strace深入到系统调用层面分析,基本上大部分问题都能找到根因。

记住,工具只是手段,关键还是要理解背后的原理。多实践、多思考,相信大家都能成为排查问题的高手!

如果觉得这篇文章对你有帮助,欢迎转发分享给更多的小伙伴。我们一起在运维的道路上互相学习,共同进步!

公众号:运维躬行录
个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码