Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Linux 命令(137)—— strace 命令

Linux 命令(137)—— strace 命令

作者头像
恋喵大鲤鱼
发布于 2020-04-13 04:53:43
发布于 2020-04-13 04:53:43
8.5K00
代码可运行
举报
文章被收录于专栏:C/C++基础C/C++基础
运行总次数:0
代码可运行

1.命令简介

strace 命令是一个集诊断、调试、统计于一体的工具,我们可以使用 strace 对程序的系统调用和信号传递的跟踪结果来对程序进行分析,以达到解决问题或者是了解程序工作过程的目的。当然strace 与专业的调试工具比如说 gdb 之类的是没法相比的,因为它不是一个专业的调试器

strace 的最简单的用法就是执行一个指定的命令,在指定的命令结束之后它也就退出了。在命令执行的过程中,strace 会记录和解析命令进程的所有系统调用以及这个进程所接收到的所有的信号值。

2.命令格式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace [OPTIONS] command [ARGS]

3.选项说明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-c
	统计每个系统调用的时间、次数和错误,并在程序退出时报告摘要
-C
	类似于 -c,但在进程运行时也打印常规输出
-D
	将跟踪进程作为分离的孙进程运行,而不是作为跟踪对象的父进程运行。这通过保持跟踪对象是调用进程的直接子进程来减少 strace 的可见效果
-d
	输出 strace 关于标准错误的调试信息
-f
	跟踪由 fork(2), vfork(2) and clone(2) 调用所产生的子进程
-ff
	如果提供 -o FILENAME,则所有进程的跟踪结果输出到相应的 FILENAME.pid 中,pid 是各进程的进程号 
-F
	该选项已废弃,作用等同于 -f
-h
	输出简要的帮助信息
-i
	在系统调用时打印指令指针
-q
	禁止附加、分离等信息。当输出被重定向到文件并直接运行命令而不是附加命令时,这将自动发生
-qq
	如果给定两次,则禁止关于进程退出状态的消息
-r
	在每次系统调用进入时打印相对时间戳。它记录连续系统调用开始之间的时间差
-t
	在输出中的每一行前加上时间信息
-tt
	如果给定两次,在输出中的每一行前加上微秒级的时间信息
-ttt
	如果给定三次,则打印的时间将包括微秒,并且开始部分将打印自纪元以来的秒数
-T
	显示每一系统调用所耗的时间 
-v
	输出所有的系统调用。一些调用关于环境变量,状态,输入输出等调用,由于使用频繁默认不输出
-V
	输出 strace 的版本信息.
-x
	以十六进制形式输出非标准字符串
-xx
	所有字符串以十六进制形式输出
-y
	与文件描述符参数关联的打印路径
-a COLUMN
	设置返回值的输出位置,默认为40
-b SYSCALL
	如果达到指定的系统调用,与跟踪进程分离。目前,只支持 execve。如果希望跟踪多线程进程,因此需要 -f,但不希望跟踪其(可能非常复杂的)子进程,则此选项非常有用
-e EXPR
	指定一个表达式,用来控制如何跟踪。格式如下: 
	[qualifier=][!]value1[,value2]... 
	qualifier 只能是 trace, abbrev, verbose, raw, signal, read, write 其中之一。value 是用来限定的符号或数字。默认的 qualifier 是 trace,感叹号是否定符号。例如:-e open 等价于 -e trace=open,表示只跟踪 open 调用。而 -etrace=!open 表示跟踪除了 ope 以外的所有其他调用。有两个特殊的符号 all 和 none,分别表示跟踪所有和不跟踪任何系统调用。注意有些 Shell 使用 ! 来执行历史记录里的命令,所以要使用反斜杠对 ! 进行转义
-e trace=SET
	只跟踪指定的系统调用。例如: -e trace=open,close,rean,write 表示只跟踪这四个系统调用,默认的为 trace=all 
-e trace=file
	只跟踪有关文件操作的系统调用
-e trace=process 
	只跟踪有关进程控制的系统调用
-e trace=network 
	跟踪与网络有关的所有系统调用
-e strace=signal
	跟踪所有与系统信号有关的系统调用 
-e trace=ipc 
	跟踪所有与进程通讯有关的系统调用
-e trace=desc
	跟踪所有与文件描述符相关的系统调用
-e trace=memory
	跟踪所有与内存映射相关的系统调用
-e abbrev=SET
	缩写打印大型结构的每个成员的输出。默认值是 abbrev=all。-v 选项的效果是 abbrev=none
-e verbose=SET
	为指定的系统调用集取消引用结构。默认是 verbose=all
-e raw=SET
	将指定的系统调用的参数以十六进制显示
-e signal=SET
	指定跟踪的系统信号,默认为 signal=all。如 signal=!SIGIO(或 signal=!io),表示不跟踪 SIGIO 信号
-e read=SET
	输出从指定文件描述符中读出的数据。例如:-e read=3,5
-e write=SET
	输出写入到指定文件中的数据
-o FILENAME
	将 strace 的输出写入指定文件
-O OVERHEAD
	将跟踪系统调用的开销设置为指定的微秒
-p PID
	跟踪指定的进程
-P PATH
	只跟踪系统调用的访问路径。多个 -P 选项可用于指定多个路径
-s STRSIZE
	指定输出的字符串的最大长度,默认为 32。注意,文件名不被认为是字符串,总是全部打印
-S SORTBY
	根据指定的条件对 -c 选项打印的直方图的输出进行排序。SORTBY 合法值是 time、calls、name 和 nothing,默认值是 time
-u USERNAME
	以指定用户的 UIDGID 和补充组执行被跟踪的命令
-E VAR=VAL
 	为命令设置环境变量
 -E VAR
 	从继承的环境变量列表中删除变量 VAR,然后将其传递给命令

4.常用示例

现在我们做一个很简单的程序来演示 strace 的基本用法。这个程序的 C 语言代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
int main()
{
        int a = 0;
        printf("please input:\n");
        scanf("%d", &a);
        printf("%10d\n", a);      
        return 0;
}

通过 gcc 编译,默认生成名为 a.out 的可执行程序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc main.c

(1)追踪系统调用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace -o strace.out ./a.out

输入 4 然后回车生成 strace 的输出文件 strace.out,其内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execve("./a.out", ["./a.out"], [/* 28 vars */]) = 0
brk(0)                                  = 0x1e79000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9feb000
access("/etc/ld.so.preload", R_OK)      = 0
open("/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0
mmap(NULL, 18, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7ff4e9fea000
close(3)                                = 0
readlink("/proc/self/exe", "/root/test/c++/strace/a.out", 4096) = 27
open("/lib64/libonion.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\20\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=42880, ...}) = 0
mmap(NULL, 1072448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9ee4000
mprotect(0x7ff4e9ee7000, 1048576, PROT_NONE) = 0
mmap(0x7ff4e9fe7000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7ff4e9fe7000
mmap(0x7ff4e9fe8000, 7488, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9fe8000
close(3)                                = 0
munmap(0x7ff4e9fea000, 18)              = 0
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=74350, ...}) = 0
mmap(NULL, 74350, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff4e9ed1000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2122016, ...}) = 0
mmap(NULL, 3944896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9a07000
mprotect(0x7ff4e9bc1000, 2093056, PROT_NONE) = 0
mmap(0x7ff4e9dc0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b9000) = 0x7ff4e9dc0000
mmap(0x7ff4e9dc6000, 16832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9dc6000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9fea000
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\16\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=19344, ...}) = 0
mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9803000
mprotect(0x7ff4e9805000, 2097152, PROT_NONE) = 0
mmap(0x7ff4e9a05000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7ff4e9a05000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9ecf000
arch_prctl(ARCH_SET_FS, 0x7ff4e9ecf740) = 0
mprotect(0x7ff4e9dc0000, 16384, PROT_READ) = 0
mprotect(0x7ff4e9a05000, 4096, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7ff4e9fec000, 4096, PROT_READ) = 0
munmap(0x7ff4e9ed1000, 74350)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9ee3000
write(1, "please input:\n", 14)         = 14
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff4e9ee2000
read(0, "4\n", 1024)                    = 2
write(1, "         4\n", 11)            = 11
exit_group(0)                           = ?
+++ exited with 0 +++

从跟踪的结果可以看到,系统首先调用 execve 开始一个新的进程,接着进行环境的初始化操作,最后停顿在read(0, "4\n", 1024) = 2,这也就是执行到了 scanf 函数,等待我们输入数字。在输入完 4 之后,再调用 write 函数将格式化后的数值4输出到屏幕,最后调用 exit_group 退出进程,完成整个程序的执行过程。

(2)跟踪信号传递。我们还是使用上面的 a.out 程序,来观察进程接收信号的情况,即跟踪进程和信号相关的系统调用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#开启跟踪
strace -e trace=signal -o strace.out ./a.out

#查找进程 ./a.out 进程 ID
ps -ef | grep a.out | grep -v "grep\|strace"
root     10787 10784  0 22:46 pts/1    00:00:00 ./a.out

#根据上一步查到的进程 ID 通过 kill 命令发送信号 SIGKILL
kill -9 10787

再次查看 strace 的输出文件 strace.out 的内容。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
+++ killed by SIGKILL +++

进程被杀退出时,strace 会输出 killed by SIGX(SIGX 代表发送给进程的信号)等,那么进程自己退出时会输出什么呢?

这里有个叫做 test_exit 的程序,其代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
       exit(1);
}

我们 strace 看下它退出时 strace 上能看到什么痕迹。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace -tt -e trace=process -f ./test_exit

-e trace=process 表示只跟踪和进程管理相关的系统调用。

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
23:07:24.672849 execve("./test_exit", ["./test_exit"], [/* 35 vars */]) = 0
23:07:24.674665 arch_prctl(ARCH_SET_FS, 0x7f1c0eca7740) = 0
23:07:24.675108 exit_group(1)           = ?
23:07:24.675259 +++ exited with 1 +++

可以看出,进程自己退出时(调用 exit 函数,或者从 main 函数返回), 最终调用的是 exit_group系统调用, 并且 strace 会输出 exited with X(X为退出码)。

可能有人会疑惑,代码里面明明调用的是 exit, 怎么显示为 exit_group?

这是因为这里的 exit 函数不是系统调用,而是 glibc 库提供的一个函数,exit 函数的调用最终会转化为 exit_group 系统调用,它会退出当前进程的所有线程。实际上,有一个叫做 _exit() 的系统调用(注意 exit 前面的下划线)线程退出时最终会调用它。

(3)系统调用统计。strace 不光能追踪系统调用,通过使用参数 -c,它还能将进程所有的系统调用做一个统计分析给你,下面来看看 strace 对系统调用的统计。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace -c ./a.out
please input:
4
         4
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000038           3        15           mmap
  0.00    0.000000           0         4           read
  0.00    0.000000           0         2           write
  0.00    0.000000           0         5           open
  0.00    0.000000           0         5           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0         7           mprotect
  0.00    0.000000           0         2           munmap
  0.00    0.000000           0         1           brk
  0.00    0.000000           0         1           access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           readlink
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000038                    52           total

这里很清楚的告诉你调用了那些系统函数,调用次数多少,消耗了多少时间等等这些信息,这个对我们分析一个程序来说是非常有用的。

(4)trace 一个现有的进程。strace 不光能自己初始化一个进程进行 trace,还能使用 -p 选项追踪现有的进程。具体用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace -p PID

参考文献

[1] strace(1) manual [2] Linux 命令大全.strace [3] 马昌伟.strace命令详解

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/04/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
十个例子让你了解 strace 的使用技巧
tcpdump 作为计算机网络排查的一大神器,掌握了上文所说的技巧,可以让你随时随地得心应手的掌握网络应用的一举一动。
用户3147702
2022/06/27
4.9K0
十个例子让你了解 strace 的使用技巧
df 和 ls 命令执行夯主
其实他说第二点问题的时候我就已经猜到问题所在了,那不就是远程挂载的磁盘非正常的掉了,然后就会造成这个问题。但是他说 ISCSI 这个玩意的时候我不知道是啥,于是查了一下,有兴趣的同学可以看看这是:https://zhuanlan.zhihu.com/p/60986068,看的出来他是一个网络存储,那么就更加坚定我的想法了,开始指挥解决问题。
张琳兮
2020/05/25
2.1K0
df 和 ls 命令执行夯主
线程的创建以及线程的本质
上节详细学习了进程的创建,通过实例学习了fork和vfork的区别。本节将学习线程的创建,只涉及应用层的线程,内核线程的创建在后面学习。
DragonKingZhu
2020/03/24
1.7K0
线程的创建以及线程的本质
这条命令有可能断送DBA职业生涯,我今天真的执行了
这个是从库,没有读业务和其他下游同步,风险可控。但是大家还是要谨慎。我执行这个命令是因为我搜到的菜鸟教程的split命令案例错误导致我生成了大量小文件。没想到大名鼎鼎的菜鸟教程也会有问题,大家还用man或者tldr查看帮助手册吧。
DBA札记
2024/06/03
1220
这条命令有可能断送DBA职业生涯,我今天真的执行了
Linux系统下刨析hello world背后的秘密
linux系统上使用gcc生成可执行程序:gcc -g -W helloworld.c -o helloworld
Linux兵工厂
2023/02/28
6320
Linux系统下刨析hello world背后的秘密
使用golang的net包进行域名解析过程分析
我们都知道,在计算机的世界,建立连接都是需要依靠五元组的(源ip,源端口,目的ip,目的端口,协议),而在实际用户使用过程中,浏览器会帮我们识别和管理源ip和端口以及协议(http,https),协议确定后其实目的端口也就确定了(80或443). 因此整个DNS系统要解决的问题就是将用户在浏览器中输入的域名最终转换成可识别的目的ip,进而进行连接通信。下面以一个简单例子来分析下dns解析的过程.
BGBiao
2019/09/11
13.6K0
futex验证_fulvic
#include <semaphore.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
全栈程序员站长
2022/11/08
3050
无法获取指向控制台的文件描述符 (couldn't get a file descriptor referring to the console)
最近收拾东西,从一堆杂物里翻出来尘封四年多的树莓派 3B 主机来,打扫打扫灰尘,接上电源,居然还能通过之前设置好的 VNC 连上。欣慰之余,开始 clone 我的 git 项目,为它们拓展一个新的平台。在执行 cnblogs 项目 (参考《博客园排名预测 》) 对应的绘图命令时,趋势图、预测图是生成了,但没有自动打开图片,这个问题经过一番探索居然解决了,这篇文章就来分享一下解决问题的过程。
海海
2022/08/31
3.7K0
无法获取指向控制台的文件描述符 (couldn't get a file descriptor referring to the console)
Linux 进程管理
进程是 UNIX/Linux 用来表示正在运行的程序的一种抽象概念,所有系统上面运行的的数据都会以进程的形态存在。
用户1679793
2020/05/06
7.1K0
ls 不显示,rm 删不掉,怎么办?
有个叫atest的东西 ls -l atest 查不出来是什么 下面删也删不掉 但是可以用mv改名字,它放在/目录下,用ls /导致不能显示 如果操作,请大侠指点, 顺便问下什么时候会导致ls /   不显示,谢谢! # s
三杯水Plus
2018/11/14
1K0
CVE-2021-44731 linux snap 本地提权漏洞分析
Snap是Canonical为使用Linux内核的操作系统开发的软件打包和部署系统。这些包(称为 snaps)和使用它们的工具 snapd 可在一系列 Linux 发行版中工作。
枪哥四海为家
2022/03/05
3.5K0
CVE-2021-44731 linux snap 本地提权漏洞分析
sqlplus执行错误的问题探究
今天碰到个很诡异的问题,在装了Oracle Clinet的机器,不同路径下执行指令sqlplus,回显不同。
bisal
2020/07/25
8930
Swoole 4.4:支持 CURL 协程化
在4.4之前的版本中,Swoole一直不支持CURL协程化,在代码中无法使用curl。由于curl使用了libcurl库实现,无法直接hook它的socket,4.4版本使用Swoole\Coroutine\Http\Client模拟实现了curl的API,并在底层替换了curl_init等函数的C Handler。
猿哥
2019/06/12
6040
tools-tcp
如在我的电脑上strace -o 1.txt pkill goldendict,strace就给出了3287行的信息。。。如果加上参数-e trace=process,那么就只有4行:
Heeler-Deer
2023/02/22
4670
使用strace分析exp的奇怪问题(r3笔记第41天)
exp算是一个经典的数据导出工具了。对于小数量的表来说,个人还是比较钟爱exp。毕竟expdp还需要配置directory而且还在服务端。exp在数据量小的情况下速度还是很理想的。 关于exp导出的这个问题,已经拖了很久了,自己也排查了各种方法。通过查看wait event,查看exp的debug日志,都没有得出一些很有说服力的内容,今天下定决心来细细琢磨琢磨这个问题。有了一点收获。 之前在测试系统中碰到一个问题,导出一个比较大的分区表,分区数很多,其中有些分区里面没有数据,但是通过exp导出这些没有数据
jeanron100
2018/03/14
7870
Linux 内核监控在 Android 攻防中的应用
在日常分析外部软件时,遇到的反调试/反注入防护已经越来越多,之前使用的基于 frida 的轻量级沙盒已经无法满足这类攻防水位的需要,因此需要有一种更加深入且通用的方式来对 APP 进行全面的监测和绕过。本文即为对这类方案的一些探索和实践。
evilpan
2023/02/12
3.2K0
Linux 内核监控在 Android 攻防中的应用
Swoole v4.7 版本预览之支持 c-ares
c-ares 是一个异步 DNS 解析库。它适用于需要在不阻塞的情况下执行 DNS 查询或需要并行执行多个 DNS 查询的应用程序。
沈唁
2021/07/23
8110
故障分析 | 哪些情况下 MySQL 配置文件会被截断?
作者:龚唐杰,爱可生 DBA 团队成员,主要负责 MySQL 技术支持,擅长 MySQL、PG、国产数据库。
爱可生开源社区
2024/10/10
1730
故障分析 | 哪些情况下 MySQL 配置文件会被截断?
Linux内存映射函数mmap与匿名内存块
gcc -o main1 -Wall -g -ggdb -O0 -g3 -gdwarf-2 main1.c
mingjie
2022/11/28
2.3K0
Linux内存映射函数mmap与匿名内存块
10张图22段代码,万字长文带你搞懂虚拟内存模型和malloc内部原理
摊牌了,不装了,其实我是程序喵辛苦工作一天还要回家编辑公众号到大半夜的老婆,希望各位大哥能踊跃转发,完成我一千阅读量的KPI(梦想),谢谢!
C语言与CPP编程
2020/12/02
2050
10张图22段代码,万字长文带你搞懂虚拟内存模型和malloc内部原理
相关推荐
十个例子让你了解 strace 的使用技巧
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档