如何使用ptrace()监控进程直至其退出?求跨OSX/Linux实现方案
解决ptrace()跨平台监控进程直至退出的问题
你遇到的问题很常见——OSX和Linux的ptrace()行为差异不小,尤其是在attach后的流程处理上。你的程序立即退出,大概率是因为attach到目标进程后没有进入等待循环,也没有正确处理进程的状态变化。下面我给你一套跨平台的实现方案,能让你的监控程序直到目标进程退出才终止。
核心思路
不管是OSX还是Linux,要持续监控进程直到它退出,都需要这几个步骤:
- 用
ptrace()附加到目标PID - 处理附加后的进程暂停状态,让目标进程继续运行
- 循环调用
waitpid()等待目标进程的状态变化 - 每次状态变化后,判断进程是否已经退出;如果没退出,就继续让进程运行,回到等待循环
跨平台实现代码
下面的代码用条件编译区分OSX和Linux,注释里标清楚了每个平台的差异点:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <unistd.h> #include <errno.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <target-pid>\n", argv[0]); exit(EXIT_FAILURE); } pid_t target_pid = atoi(argv[1]); int status; // Step 1: Attach to the target process #ifdef __APPLE__ if (ptrace(PT_ATTACH, target_pid, NULL, 0) == -1) { perror("ptrace PT_ATTACH failed"); exit(EXIT_FAILURE); } // OSX: Attach后进程会暂停,需要先waitpid捕获暂停状态,再让它继续 if (waitpid(target_pid, &status, WUNTRACED) == -1) { perror("waitpid after PT_ATTACH failed"); exit(EXIT_FAILURE); } // 让目标进程继续运行 if (ptrace(PT_CONTINUE, target_pid, (caddr_t)1, 0) == -1) { perror("ptrace PT_CONTINUE failed"); exit(EXIT_FAILURE); } #else // __linux__ if (ptrace(PTRACE_ATTACH, target_pid, NULL, 0) == -1) { perror("ptrace PTRACE_ATTACH failed"); exit(EXIT_FAILURE); } // Linux: Attach后进程会收到SIGSTOP,waitpid会捕获这个状态 if (waitpid(target_pid, &status, 0) == -1) { perror("waitpid after PTRACE_ATTACH failed"); exit(EXIT_FAILURE); } // 让目标进程继续运行,忽略之前的SIGSTOP if (ptrace(PTRACE_CONT, target_pid, NULL, 0) == -1) { perror("ptrace PTRACE_CONT failed"); exit(EXIT_FAILURE); } #endif // Step 2: 循环等待目标进程状态变化,直到它退出 while (1) { // 等待进程状态变化 if (waitpid(target_pid, &status, 0) == -1) { perror("waitpid in loop failed"); exit(EXIT_FAILURE); } // 判断进程是否已经退出 if (WIFEXITED(status)) { printf("Target process %d exited normally with code %d\n", target_pid, WEXITSTATUS(status)); break; } if (WIFSIGNALED(status)) { printf("Target process %d exited due to signal %d\n", target_pid, WTERMSIG(status)); break; } // 如果进程只是暂停(比如收到信号),让它继续运行 #ifdef __APPLE__ if (ptrace(PT_CONTINUE, target_pid, (caddr_t)1, 0) == -1) { perror("ptrace PT_CONTINUE in loop failed"); exit(EXIT_FAILURE); } #else // Linux下,需要传递导致暂停的信号,让进程继续处理 if (ptrace(PTRACE_CONT, target_pid, NULL, WSTOPSIG(status)) == -1) { perror("ptrace PTRACE_CONT in loop failed"); exit(EXIT_FAILURE); } #endif } // 最后 detach(可选,不过进程已经退出的话,detach可能没必要,但好习惯) #ifdef __APPLE__ ptrace(PT_DETACH, target_pid, NULL, 0); #else ptrace(PTRACE_DETACH, target_pid, NULL, 0); #endif return EXIT_SUCCESS; }
关键细节说明
平台差异处理:
- OSX使用
PT_ATTACH/PT_CONTINUE,Linux使用PTRACE_ATTACH/PTRACE_CONT,通过__APPLE__和__linux__宏区分 - OSX在attach后必须先调用
waitpid捕获暂停状态,再执行PT_CONTINUE;Linux则是attach后进程自动暂停,waitpid捕获后用PTRACE_CONT恢复 - Linux下继续进程时,需要传递导致暂停的信号(
WSTOPSIG(status)),让进程能正确处理这个信号
- OSX使用
退出判断:
- 用
WIFEXITED(status)判断进程是否正常退出 - 用
WIFSIGNALED(status)判断进程是否因信号终止(比如被kill)
- 用
权限问题:
- Linux下,普通用户只能attach到同用户的进程;如果要监控其他用户的进程,需要root权限
- OSX下,现代版本有系统完整性保护(SIP),可能需要临时关闭SIP(
csrutil disable重启后生效),才能让ptrace正常工作
编译与运行
- Linux下编译:
gcc monitor.c -o monitor,运行:sudo ./monitor <pid>(如果监控其他用户进程) - OSX下编译:
clang monitor.c -o monitor,运行:sudo ./monitor <pid>(需要关闭SIP或者有足够权限)
内容的提问来源于stack exchange,提问作者Alexander Mills




