6

有人在维基百科的“ptrace”文章中添加了声称,在 Linux 上,ptraced 进程本身不能 ptrace 另一个进程。我正在尝试确定是否(如果是,为什么)是这种情况。下面是我设计的一个简单程序来测试它。我的程序失败(子子进程无法正常运行),但我非常确信这是我的错误,而不是根本问题。

本质上,初始进程A分叉了进程B,而 B 又分叉了CA ptrace 其子BB ptrace 其子C。设置完成后,所有三个进程都会每秒 写入一次 print AB或stdout 。C

在实践中,AB工作正常,但C只打印一次然后卡住。检查ps -eo pid,cmd,wchan显示C卡在内核函数ptrace_stop中,而其余的都在hrtimer_nanosleep我希望所有三个都出现的位置。

偶尔这三个都可以工作(所以程序会打印 Cs 以及 As 和 Bs),这让我相信在初始设置中存在一些竞争条件。

我对可能出错的猜测是:

  • A看到 aSIGCHLD相关的 a 与B看到 aSIGCHLDC的信号有关,并且 wait(2) 报告两者都来自B(但是对两个 pid 的 PTRACE_CONT 的 hacky 调用并不能解决问题)?
  • C应该由B跟踪- 是否C继承了A的 ptrace (并且B对 ptrace 的调用既没有出错也没有覆盖它)?

谁能弄清楚我做错了什么?谢谢。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

static void a(){
  while(1){
    printf ("A\n");
    fflush(stdout);
    sleep(1);
  }
}

static void b(){
  while(1){
    printf ("B\n");
    fflush(stdout);
    sleep(1);
  }
}

static void c(){
  while(1){
    printf ("C\n");
    fflush(stdout);
    sleep(1);
  }
}

static void sigchld_handler(int sig){
  int result;
  pid_t child_pid = wait(NULL); // find who send us this SIGCHLD

  printf("SIGCHLD on %d\n", child_pid);
  result=ptrace(PTRACE_CONT, child_pid, sig, NULL);
  if(result) {
    perror("continuing after SIGCHLD");
  }
}

int main(int  argc,
         char **argv){

  pid_t mychild_pid;
  int   result;

  printf("pidA = %d\n", getpid());

  signal(SIGCHLD, sigchld_handler);

  mychild_pid = fork();

  if (mychild_pid) {
    printf("pidB = %d\n", mychild_pid);
    result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
    if(result==-1){
      perror("outer ptrace");
    }
    a();
  }
  else {
    mychild_pid = fork();

    if (mychild_pid) {
      printf("pidC = %d\n", mychild_pid);

      result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
      if(result==-1){
        perror("inner ptrace");
      }
      b();
    }
    else {
      c();
    }
  }

  return 0;
}
4

2 回答 2

5

您确实看到了比赛条件。您可以通过在第二次调用sleep(1);之前立即放置来使其重复发生。 fork()

竞争条件是因为进程 A 没有正确地将信号传递给进程 B。这意味着如果进程 B 在进程 A 开始跟踪进程 B 之后开始跟踪进程 C,则进程 B 永远不会收到SIGCHLD表明进程 C 已停止的信号,所以它永远无法继续它。

要解决此问题,您只需要修复您的SIGCHLD处理程序:

static void sigchld_handler(int sig){
    int result, status;
    pid_t child_pid = wait(&status); // find who send us this SIGCHLD

    printf("%d received SIGCHLD on %d\n", getpid(), child_pid);
    if (WIFSTOPPED(status))
    {
        result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
        if(result) {
            perror("continuing after SIGCHLD");
        }
    }
}
于 2010-03-01T22:52:22.237 回答
0

在调用 ptrace 本身的子进程上执行某些 ptrace 功能是“可能的”。真正的困难在于,一个跟踪器进程在附加到后者时会成为被跟踪者的父进程。如果您的跟踪器进程想要跟踪所有(直接和间接)子进程的所有行为(例如,当调试器程序需要调试多线程程序时),它自然会破坏原始进程层次结构,以及所有进程间/所有子进程之间的线程间通信(即线程同步、信号发送/接收……)需要由跟踪器进程模拟/多路复用。它仍然是“可能的”,但更加困难和低效。

于 2011-11-29T00:41:50.530 回答