ptrace() - Unix 和 Linux 系统调用
Tutorials Point


  Unix 初学者指南
  Unix Shell 编程
  高级 Unix
  Unix 有用参考
  Unix 有用资源
  精选阅读

版权所有 © 2014 tutorialspoint



  首页     参考     讨论论坛     关于 TP  

ptrace() - Unix 和 Linux 系统调用


previous next AddThis Social Bookmark Button

广告

名称

ptrace - 进程跟踪

概要

#include <sys/ptrace.h> 

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

描述

ptrace() 系统调用提供了一种方法,允许父进程观察和控制另一个进程的执行,并检查和更改其核心映像和寄存器。它主要用于实现断点调试和系统调用跟踪。

父进程可以通过调用fork(2) 并让生成的子进程执行 PTRACE_TRACEME,然后(通常)执行exec(3) 来启动跟踪。或者,父进程可以使用 PTRACE_ATTACH 开始跟踪现有进程。

在被跟踪期间,子进程每次收到信号时都会停止,即使该信号被忽略。(例外情况是 SIGKILL,它具有其通常的效果。)父进程将在其下一个wait(2) 中收到通知,并且可以在子进程停止时检查和修改子进程。然后,父进程使子进程继续执行,可以选择忽略已传递的信号(甚至传递不同的信号)。

当父进程完成跟踪时,它可以使用 PTRACE_KILL 终止子进程,或者通过 PTRACE_DETACH 使其以正常、未跟踪的模式继续执行。

request 的值决定要执行的操作

标签描述
PTRACE_TRACEME
 指示此进程将由其父进程跟踪。传递给此进程的任何信号(除 SIGKILL 外)都会导致其停止,并通过wait() 通知其父进程。此外,此进程随后对exec() 的所有调用都将导致向其发送 SIGTRAP,从而使父进程有机会在新的程序开始执行之前获得控制权。如果父进程没有预期跟踪它,则进程可能不应该发出此请求。(pidaddrdata 被忽略。)
上述请求仅由子进程使用;其余请求仅由父进程使用。在以下请求中,pid 指定要对其进行操作的子进程。对于 PTRACE_KILL 之外的请求,子进程必须已停止。
PTRACE_PEEKTEXT,PTRACE_PEEKDATA
 读取子进程内存中地址 addr 处的一个字,并将该字作为ptrace() 调用的结果返回。Linux 没有单独的文本和数据地址空间,因此这两个请求目前等效。(参数 data 被忽略。)
PTRACE_PEEKUSR
 读取子进程USER区域中偏移量 addr 处的一个字,该区域保存有关进程的寄存器和其他信息(参见<linux/user.h> 和 <sys/user.h>)。该字作为ptrace() 调用的结果返回。通常,偏移量必须与字对齐,尽管这可能因体系结构而异。参见注释。(data 被忽略。)
PTRACE_POKETEXT,PTRACE_POKEDATA
 将字 data 复制到子进程内存中地址 addr 处。如上所述,这两个请求目前等效。
PTRACE_POKEUSR
 将字 data 复制到子进程USER区域中偏移量 addr 处。如上所述,偏移量通常必须与字对齐。为了维护内核的完整性,不允许对USER区域进行某些修改。
PTRACE_GETREGS,PTRACE_GETFPREGS
 分别将子进程的通用寄存器或浮点寄存器复制到父进程中的地址 data 处。有关此数据格式的信息,请参见<linux/user.h>。(addr 被忽略。)
PTRACE_GETSIGINFO(自 Linux 2.3.99-pre6 起)
 检索导致停止的信号的相关信息。将一个siginfo_t 结构(参见sigaction(2))从子进程复制到父进程中的地址 data 处。(addr 被忽略。)
PTRACE_SETREGS,PTRACE_SETFPREGS
 分别将子进程的通用寄存器或浮点寄存器从父进程中的地址 data 处复制。与 PTRACE_POKEUSER 一样,某些通用寄存器修改可能会被禁止。(addr 被忽略。)
PTRACE_SETSIGINFO(自 Linux 2.3.99-pre6 起)
 设置信号信息。将一个siginfo_t 结构从父进程中的地址 data 处复制到子进程。这将仅影响通常传递给子进程并被跟踪器捕获的信号。可能难以将这些正常信号与ptrace() 本身生成的合成信号区分开来。(addr 被忽略。)
PTRACE_SETOPTIONS(自 Linux 2.4.6 起;有关注意事项,请参见错误)
 从父进程中的 data 设置 ptrace 选项。(addr 被忽略。)data 被解释为选项的位掩码,这些选项由以下标志指定
标签描述
PTRACE_O_TRACESYSGOOD(自 Linux 2.4.6 起)
 在传递系统调用陷阱时,设置信号编号中的第 7 位(即,传递 (SIGTRAP | 0x80) 这使得跟踪器很容易区分正常陷阱和由系统调用引起的陷阱。(PTRACE_O_TRACESYSGOOD 可能不适用于所有体系结构。)
PTRACE_O_TRACEFORK(自 Linux 2.5.46 起)
 在下一个fork() 调用处使用 SIGTRAP | PTRACE_EVENT_FORK << 8 停止子进程,并自动开始跟踪新分叉的进程,该进程将以 SIGSTOP 开始。新进程的 PID 可以使用 PTRACE_GETEVENTMSG 检索。
PTRACE_O_TRACEVFORK(自 Linux 2.5.46 起)
 在下一个vfork() 调用处使用 SIGTRAP | PTRACE_EVENT_VFORK << 8 停止子进程,并自动开始跟踪新 vfork 的进程,该进程将以 SIGSTOP 开始。新进程的 PID 可以使用 PTRACE_GETEVENTMSG 检索。
PTRACE_O_TRACECLONE(自 Linux 2.5.46 起)
 在下一个clone() 调用处使用 SIGTRAP | PTRACE_EVENT_CLONE << 8 停止子进程,并自动开始跟踪新克隆的进程,该进程将以 SIGSTOP 开始。新进程的 PID 可以使用 PTRACE_GETEVENTMSG 检索。此选项可能无法在所有情况下都捕获clone() 调用。如果子进程使用 CLONE_VFORK 标志调用clone(),则如果设置了 PTRACE_O_TRACEVFORK,则将传递 PTRACE_EVENT_VFORK;否则,如果子进程使用设置为 SIGCHLD 的退出信号调用clone(),则如果设置了 PTRACE_O_TRACEFORK,则将传递 PTRACE_EVENT_FORK。
PTRACE_O_TRACEEXEC(自 Linux 2.5.46 起)
 在下一个exec() 调用处使用 SIGTRAP | PTRACE_EVENT_EXEC << 8 停止子进程。
PTRACE_O_TRACEVFORKDONE(自 Linux 2.5.60 起)
 在下一个vfork() 调用完成后使用 SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8 停止子进程。
PTRACE_O_TRACEEXIT(自 Linux 2.5.60 起)
 在退出时使用 SIGTRAP | PTRACE_EVENT_EXIT << 8 停止子进程。子进程的退出状态可以使用 PTRACE_GETEVENTMSG 检索。此停止将在进程退出早期完成时进行(此时寄存器仍然可用),允许跟踪器查看退出发生的位置,而正常的退出通知在进程完成退出后完成。即使上下文可用,跟踪器此时也无法阻止退出发生。
PTRACE_GETEVENTMSG(自 Linux 2.5.46 起)
 检索有关刚刚发生的 ptrace 事件的消息(作为unsigned long),将其放置在父进程中的地址 data 处。对于 PTRACE_EVENT_EXIT,这是子进程的退出状态。对于 PTRACE_EVENT_FORK、PTRACE_EVENT_VFORK 和 PTRACE_EVENT_CLONE,这是新进程的 PID。(addr 被忽略。)
PTRACE_CONT
 重新启动已停止的子进程。如果 data 非零且不是 SIGSTOP,则将其解释为要传递给子进程的信号;否则,不传递信号。因此,例如,父进程可以控制是否传递发送给子进程的信号。(addr 被忽略。)
PTRACE_SYSCALL,PTRACE_SINGLESTEP
 如 PTRACE_CONT 一样重新启动已停止的子进程,但安排子进程在进入或退出系统调用的下一时刻停止,或在执行单个指令后停止,分别。(子进程通常也会在收到信号时停止。)从父进程的角度来看,子进程似乎已因收到 SIGTRAP 而停止。因此,例如,对于 PTRACE_SYSCALL,想法是在第一次停止时检查系统调用的参数,然后执行另一个 PTRACE_SYSCALL 并检查第二次停止时系统调用的返回值。(addr 被忽略。)
PTRACE_SYSEMU,PTRACE_SYSEMU_SINGLESTEP(自 Linux 2.6.14 起)
 对于 PTRACE_SYSEMU,继续并在进入下一个系统调用时停止,该系统调用将不会执行。对于 PTRACE_SYSEMU_SINGLESTEP,执行相同的操作,但如果这不是系统调用,则也进行单步执行。此调用由 User Mode Linux 等希望模拟子进程所有系统调用的程序使用。(addrdata 被忽略;并非所有体系结构都支持。)
PTRACE_KILL
 向子进程发送 SIGKILL 以终止它。(addrdata 被忽略。)
PTRACE_ATTACH
 附加到 pid 中指定的进程,使其成为当前进程的被跟踪“子进程”;子进程的行为就像它执行了 PTRACE_TRACEME 一样。当前进程实际上成为子进程的大多数用途的父进程(例如,它将收到子进程事件的通知,并在ps(1) 输出中显示为子进程的父进程),但子进程的getppid(2) 仍将返回原始父进程的 PID。子进程将收到 SIGSTOP,但在此调用的完成时不一定已停止;使用wait() 等待子进程停止。(addrdata 被忽略。)
PTRACE_DETACH
 如 PTRACE_CONT 一样重新启动已停止的子进程,但首先与进程分离,撤消 PTRACE_ATTACH 的重父进程效果以及 PTRACE_TRACEME 的效果。尽管可能并非有意,但在 Linux 下,无论使用哪种方法启动跟踪,都可以以这种方式分离被跟踪的子进程。(addr 被忽略。)

注释

尽管ptrace() 的参数根据给出的原型进行解释,但 GNU libc 目前将ptrace() 声明为仅具有 request 参数固定的可变参数函数。这意味着可以省略不需要的尾随参数,尽管这样做使用了未记录的gcc(1) 行为。

init(8)(PID 为 1 的进程)可能无法被跟踪。

内存内容和 USER 区域的布局非常依赖于操作系统和体系结构。提供的偏移量和返回的数据可能与struct user 的定义不完全匹配。

“字”的大小由 OS 变体确定(例如,对于 32 位 Linux,它是 32 位,依此类推)。

追踪导致跟踪进程的语义出现一些细微的差异。例如,如果一个进程使用 PTRACE_ATTACH 附加,则其原始父进程将无法再通过 wait() 函数在它停止时收到通知,并且新父进程无法有效地模拟此通知。

此页面记录了 ptrace() 调用在 Linux 中当前的工作方式。它的行为在其他 Unix 版本上存在明显的差异。无论如何,ptrace() 的使用高度依赖于操作系统和体系结构。

SunOS 手册页将 ptrace() 描述为“独特且神秘的”,确实如此。Solaris 2 中存在的基于 proc 的调试接口以更强大和统一的方式实现了 ptrace() 功能的超集。

返回值

成功时,PTRACE_PEEK* 请求返回请求的数据,而其他请求返回零。发生错误时,所有请求都返回 -1,并且 errno 被相应地设置。由于成功 PTRACE_PEEK* 请求返回的值可能为 -1,因此调用者必须在这些请求之后检查 errno 以确定是否发生了错误。

错误

在具有 2.6 内核头文件的宿主机上,PTRACE_SETOPTIONS 的声明值与 2.4 的不同。这会导致使用此类头文件编译的应用程序在 2.4 内核上运行时失败。如果定义了 PTRACE_OLDSETOPTIONS,可以通过将其重新定义为 PTRACE_OLDSETOPTIONS 来解决此问题。

错误

标签描述
EBUSY (仅限 i386)分配或释放调试寄存器时出错。
EFAULT 尝试读取或写入父进程或子进程内存中的无效区域,可能是因为该区域未映射或不可访问。不幸的是,在 Linux 下,此错误的不同变体将或多或少任意地返回 EIO 或 EFAULT。
EINVAL 尝试设置无效选项。
EIO request 无效,或尝试读取或写入父进程或子进程内存中的无效区域,或存在字对齐违规,或在重启请求期间指定了无效信号。
EPERM 无法跟踪指定的进程。这可能是因为父进程权限不足(所需的权限是 CAP_SYS_PTRACE);出于显而易见的原因,非 root 进程无法跟踪它们无法向其发送信号的进程或正在运行 set-user-ID/set-group-ID 程序的进程。或者,该进程可能已经在被跟踪,或者为 init(PID 1)。
ESRCH 指定的进程不存在,或当前未被调用者跟踪,或未停止(对于需要此条件的请求)。

符合标准

SVr4, 4.3BSD

参见



previous next Printer Friendly

广告


  

广告



广告