strace是Linux系统下的一个用来跟踪系统调用的工具,它的实现基础是ptrace系统调用。使用strace工具可以跟踪一个程序执行过程中发生的系统调用。
我这里讲到的内容有一点点和mips体系相关,不过不熟悉mips也不影响阅读。
ptrace系统调用
ptrace系统调用提供了一种方法来跟踪和控制进程的执行,它可以读取和修改进程地址空间中的内容,包括寄存器的值。ptrace主要用于实现断点调试和跟踪系统调用。该系统调用的原型如下:
long ptrace(enum __ptrace_request request, pid_t pid, void *addr,void *data);
ptrace的四个参数的含义为:
1. request:用于选择一个操作,见下文。
2. pid:目标进程即被跟踪进程的pid。
3. addr和data用于修改和拷贝被跟踪进程的进程地址空间的数据。
下面的内容中将用父进程指代跟踪者,用子进程指代被跟踪者。实际上,在一个进程被跟踪之后,跟踪者进程会在某种意义上充当被跟踪进程的父进程(如使用ps命令就可以看到他们的父子关系),而子进程真正的父进程被保存在其task_struct结构的real_parent成员中。
使用ptrace跟踪进程
父进程跟踪一个进程的方式有两种:1.调用fork(),然后子进程打上PTRACE_TRACEME标记,并执行exec。2.父进程可以给自己打上PTRACE_ATTACH标记来跟踪一个已有进程。
一个进程被跟踪后,他只要接收到一个信号(即使这个信号被设置为忽略)就会停止运行(SIGKILL除外),然后父进程会在每次调用wait()时得到子进程停止运行的通知,这时父进程就可以检测和修改子进程了,随后父进程可以让子进程继续运行。
当父进程不想跟踪了,可以通过设置PTRACE_KILL标记来终止子进程的运行。也可以通过设置PTRACE_DETACH标记让子进程解除被跟踪,继续正常运行。
常用的request
PTRACE_TRACEME
进程设置这个request目的是让自己被父进程跟踪。任何发送到该子进程的信号(除了SIGKILL)都会导致他停下来,并在父进程wait()的时候通知到父进程。另外,子进程后续调用exec会导致子进程自己收到一个SIGTRAP信号,这是为了让父进程有机会在exec的新程序开始执行前获得控制权。
除非一个进程知道父进程要跟踪他,一般不会去设置这个request。设置这个请求时,pid,addr和data三个参数都会被忽略。
这个request只供子进程设置,其他的request都是只供父进程使用的。相应的,下面的request中,参数pid为被跟踪的子进程的pid。
PTRACE_ATTACH
将pid指定的进程作为自己要跟踪的进程,并开始跟踪。这和子进程自己调用PTRACE_TRACEME的效果相同。
设置这个request时,子进程会首先收到一个SIGSTOP信号,但并不会停止,只是导致跟踪者进程第一次被中断,从而开始跟踪,否则只能等到子进程接收到第一个信号时才开始跟踪。之后当子进程有待决信号时,进程总是会暂停,这时父进程通过SIGCHLD信号得到通知。在子进程暂停前,父进程可使用wait函数等待。
参数addr和data会被忽略。
PTRACE_CONT
让被停掉的子进程继续运行,而当子进程再次接收到信号时会暂停。
参数data如果被设置为一个非零值并且不是SIGSTOP,那data就是父进程发送给子进程的信号,否则,不给子进程发送信号。这样一来,父进程可以控制是否向子进程发送一个信号。
参数addr会被忽略。
PTRACE