代码之家  ›  专栏  ›  技术社区  ›  Finlay McWalter

在ptraced linux进程中调用ptrace

  •  6
  • Finlay McWalter  · 技术社区  · 14 年前

    有人添加到 Wikipedia "ptrace" article 声称在Linux上,一个ptraced进程本身不能ptrace另一个进程。我正试图确定是否(如果是,为什么)是这样。下面是一个简单的程序,我设计来测试这个。我的程序失败了(子进程没有正常运行),但我非常确信这是我的错误,而不是根本原因。

    实质上,最初的过程 叉子法 依次分叉 C . 让它的孩子 , 让它的孩子 C . 一旦设置好,所有三个进程都将被写入 A , B C 每秒一次。

    实际上发生的是 工作很好,但是 C 只打印一次然后就卡住了。核对 ps -eo pid,cmd,wchan 显示 C 陷入核心函数 ptrace_stop 其他人在的时候 hrtimer_nanosleep 我想这三个人都会在那里。

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

    我的 猜测 至于可能的问题是:

    • 有点关系 看到一个 SIGCHLD 有关 看到一个 西格尔德 用信号做 C ,并等待(2)报告两者都来自 (但是ptrace_cont对两个pids的一个恶意调用并不能解决问题)?
    • C 应该由 -有 C 继承了ptrace 取而代之(和) 打给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;
    }
    
    2 回复  |  直到 13 年前
        1
  •  5
  •   caf    14 年前

    你确实看到了一个种族状况。你可以通过放置 sleep(1); 就在 第二 fork() 打电话。

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

    要解决这个问题,你只需要 西格尔德 汉德勒:

    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");
            }
        }
    }
    
        2
  •  0
  •   liuyu    13 年前

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