strace
我们通常使用 strace 命令跟踪一个命令的执行,strace常用来跟踪进程执行时的系统调用和所接收的信号
strace的语法很简单:
strace [参数] <命令>strace 会拦截<命令>中和内核的交互并且打印,其中每一行代表一个系统调用,包括它的名字、参数以及返回值
常用参数表速查
| 参数 | 功能描述 | 典型用途 |
|---|---|---|
-p <PID> | 附加(Attach)到正在运行的进程 | 诊断运行中突然卡住的后台服务 |
-o <file> | 将输出保存到文件 | 方便之后用 vim 或 grep 详细分析 |
-e <expr> | 过滤特定的系统调用 | 只看文件操作:-e trace=openat,read |
-f | 跟踪子进程(Follow forks) | 调试会产生多个进程的复杂程序(如 Nginx) |
-c | 统计模式 | 汇总每个系统调用的次数、错误和耗时 |
-T | 显示每个调用消耗的时间 | 寻找性能瓶颈,看哪一步最慢 |
下面举个例子讲解 strace 如何展现程序与内核的通信
我们用一个go语言程序实现一个Hello world的打印
通过这些输出我们能发现:
- 所有命令的开头都是
execve,这是linux加载程序的标准方式 - go程序会预先分配很多的虚拟内存,所以会出现很多的
mmap向内核申请空间 - go会在运行前观察自己的环境,会尝试去读取 Cgroups 配置从而知道自己被分配了多少CPU,从而自动决定开启多少线程
- go几乎接管了所有可能的系统信号,从而实现抢占式调度和GC触发
- 在接近最后的
write(1,"Hello world\n",12)才是代码中的fmt.Println