系统调用追踪工具strace

当我们想知道某个进程为什么突然卡了,或者是某个工具到底是如何实现的时候。strace就派上了用场。它能将程序的调用信息打印出来。

strace原理:

它的实现基础是ptrace系统调用。ptrace系统调用提供了一种方法来跟踪和控制进程的执行,它可以读取和修改进程地址空间中的内容,包括寄存器的值。ptrace主要用于实现断点调试和跟踪系统调用。

ptrace系统调用

1 #include <sys/ptrace.h>
2 int ptrace(int request, int pid, int addr, int data);

Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。

一个被跟踪的进程运行中,在执行系统调用之前,内核会先检查当前进程是否处于被“跟踪”(traced)的状态。如果是的话,内核暂停当前进程并将控制权交给跟踪进程,使跟踪进程得以察看或者修改被跟踪进程的寄存器

在一个进程被跟踪之后,跟踪者进程会在某种意义上充当被跟踪进程的父进程(如使用ps命令就可以看到他们的父子关系),而子进程真正的父进程被保存在其task_struct结构的real_parent成员中。

当子进程产生系统调用时,就会被暂停,父进程会在每次调用wait()时得到子进程停止运行的通知,这时父进程就可以检测和修改子进程了,随后父进程可以让子进程继续运行。当父进程不想跟踪了,可以通过设置PTRACE_KILL标记来终止子进程的运行。也可以通过设置PTRACE_DETACH标记让子进程解除被跟踪,继续正常运行。
函数参数:

  1. request:用于选择一个操作。
  2. pid:目标进程即被跟踪进程的pid。
  3. addr用于修改和拷贝被跟踪进程的进程地址空间的数据。
  4. data功能同addr。

父进程跟踪一个进程的方式有两种:

  1. 调用fork(),然后子进程打上PTRACE_TRACEME标记,并执行exec。
  2. 父进程可以给自己打上PTRACE_ATTACH标记来跟踪一个已有进程。

所以我们看strace有两种用法,一个是strace直接加要运行的程序,一个是用-p指定一个正在运行的进程。

一个进程被跟踪后,他只要接收到一个信号(即使这个信号被设置为忽略)就会停止运行(SIGKILL除外),然后父进程会在每次调用wait()时得到子进程停止运行的通知,这时父进程就可以检测和修改子进程了,随后父进程可以让子进程继续运行。

当父进程不想跟踪了,可以通过设置PTRACE_KILL标记来终止子进程的运行。也可以通过设置PTRACE_DETACH标记让子进程解除被跟踪,继续正常运行。

strace用法:

最简单的用法其实就是trace后面直接跟着要执行的命令。比如:

1 strace ls -l

就能追踪到它的系统调用。

1 execve("/bin/ls", ["ls""-l"], [/* 22 vars */]) = 0
2 brk(0)                                  = 0x2073000
3 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fee98686000
4 access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
5 open("/etc/ld.so.cache", O_RDONLY)      = 3
6 .........

命令参数:

  • -p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。
  • -e 函数名:只追踪定义函数名。
  • -f :除了跟踪当前进程外,还跟踪其子进程。
  • -o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。

在linux中,很多性能查看命令都是通过读取/proc中的文件实现的。所以我们如果想学习它们的实现,就可以使用strace追踪,用-e筛选出open函数,就知道它读取了什么文件。比如mount命令。

01 strace -eopen mount
02 open("/etc/ld.so.cache", O_RDONLY)      = 3
03 open("/lib64/libblkid.so.1", O_RDONLY)  = 3
04 open("/lib64/libuuid.so.1", O_RDONLY)   = 3
05 open("/lib64/libselinux.so.1", O_RDONLY) = 3
06 open("/lib64/libsepol.so.1", O_RDONLY)  = 3
07 open("/lib64/libc.so.6", O_RDONLY)      = 3
08 open("/lib64/libdl.so.2", O_RDONLY)     = 3
09 open("/proc/filesystems", O_RDONLY)     = 3
10 open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
11 open("/dev/null", O_RDWR)               = 3
12 <span style="color: #ff0000;"><strong>open("/etc/mtab", O_RDONLY)             = 3
13 </strong></span>

参考资料:

http://blog.csdn.net/sealyao/article/details/6710772

转载请注明:旅途@KryptosX » 系统调用追踪工具strace

上一篇:TCP/IP协议资料图整理


下一篇:Go语言的可变(不定长)参数函数