6

ウィキペディアの「ptrace」記事に、Linux では、ptrace されたプロセス自体が別のプロセスを ptrace できないと主張する人が追加されました。私は、それが事実であるかどうか (また、そうである場合はその理由) を判断しようとしています。以下は、これをテストするために考案した簡単なプログラムです。私のプログラムは失敗します (サブサブプロセスが正しく実行されません) が、それは私のエラーであり、基本的なものではないと確信しています。

本質的に、最初のプロセスAはプロセスBを fork し、プロセス B はCを fork します。Aはその子Bを ptrace し、Bはその子Cを ptrace します。セットアップが完了すると、3 つのプロセスすべてが、 1 秒ごとに print AB、またはstdout に書き込まれます。C

実際には、ABは問題なく動作しますが、Cは 1 回しか印刷されず、スタックしてしまいます。で確認すると、 Cがカーネル関数でスタックしていることがps -eo pid,cmd,wchan示されますが、残りは3 つすべてが期待される場所にあります。ptrace_stophrtimer_nanosleep

非常にまれに 3 つすべてが機能する (したがって、プログラムは A と B だけでなく C も出力します)。

何が間違っているかについての私の推測は次のとおりです。

  • AがBSIGCHLDに関連するaを見て、 a がCへのシグナルと関係があることを確認し、 wait(2) が両方をBから来ていると報告している(ただし、両方の pid への PTRACE_CONT のハッキーな呼び出しは問題を解決しません)?SIGCHLD
  • CはBによって ptrace されるべきです- Cは代わりにAによって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

あなたは確かに競合状態を見ています。2 番目の呼び出しsleep(1);の直前に配置することで、繰り返し発生させることができます。 fork()

競合状態は、プロセス A がプロセス B にシグナルを正しく渡していないために発生します。つまり、プロセス A がプロセス B のトレースを開始した後にプロセス 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 に答える