6 Stimmen

Rufen von ptrace innerhalb eines ptraced Linux-Prozesses

Jemand hat dem Wikipedia-Artikel "ptrace" hinzugefügt, dass auf Linux ein durch ptrace überwachter Prozess keinen anderen Prozess ptracen kann. Ich versuche herauszufinden, ob das stimmt und falls ja, warum. Hier ist mein einfaches Programm, das ich erstellt habe, um das zu testen. Mein Programm schlägt fehl (der Unterunterprozess wird nicht ordnungsgemäß ausgeführt), aber ich bin ziemlich überzeugt, dass es mein Fehler ist und nicht etwas Grundlegendes.

Im Wesentlichen erzeugt der Ausgangsprozess A den Prozess B, der wiederum den Prozess C erzeugt. A überwacht sein Kind B, B überwacht sein Kind C. Sobald sie eingerichtet sind, sind alle drei Prozesse so geschrieben, dass sie einmal pro Sekunde einfach A, B oder C auf stdout ausgeben.

In der Praxis ist es so, dass A und B funktionieren, aber C nur einmal druckt und dann stecken bleibt. Wenn ich mit ps -eo pid,cmd,wchan überprüfe, zeigt sich, dass C in der Kernelfunktion ptrace_stop stecken bleibt, während die anderen in hrtimer_nanosleep sind, wo ich erwarten würde, dass alle drei sind.

Sehr selten funktionieren alle drei (das Programm druckt also auch Cs sowie As und Bs), was mich glauben lässt, dass es ein Rennzustand beim initialen Setup gibt.

Meine Vermutungen, was falsch sein könnte, sind:

  • irgendetwas damit, dass A ein SIGCHLD sieht, das sich auf B bezieht, ein SIGCHLD das sich auf ein Signal an C bezieht, und wait(2) beide als von B kommend meldet (aber ein hacky Aufruf von PTRACE_CONT zu beiden pids behebt das Problem nicht)?
  • C sollte von B überwacht werden - hat C stattdessen das PTRACE von A geerbt (und der Aufruf von B zu ptrace hat weder einen Fehler erzeugt noch dies überschrieben)?

Kann jemand herausfinden, was ich falsch mache? Vielen Dank.

#include 
#include 
#include 
#include 
#include 
#include 

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;
}

5voto

caf Punkte 224189

Sie sehen tatsächlich eine Wettlaufsituation. Sie können dies wiederholbar verursachen, indem Sie sleep(1); unmittelbar vor dem zweiten fork() Aufruf platzieren.

Die Wettlaufsituation entsteht, weil Prozess A Signale nicht korrekt an Prozess B weitergibt. Das bedeutet, dass Prozess B nie das SIGCHLD Signal erhält, das anzeigt, dass Prozess C gestoppt wurde, wenn Prozess B beginnt, Prozess C nach Prozess A zu verfolgen, nachdem Prozess A Prozess B verfolgt hat. So kann Prozess B nie mit dem Fortfahren fortfahren.

Um das Problem zu beheben, müssen Sie einfach Ihren SIGCHLD Handler reparieren:

static void sigchld_handler(int sig){
    int result, status;
    pid_t child_pid = wait(&status); // herausfinden, wer uns dieses SIGCHLD gesendet hat

    printf("%d hat SIGCHLD von %d erhalten\n", getpid(), child_pid);
    if (WIFSTOPPED(status))
    {
        result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
        if(result) {
            perror("Fortsetzen nach SIGCHLD");
        }
    }
}

0voto

liuyu Punkte 41

Es ist "möglich", einige ptrace-Funktionalitäten auf einen Kindprozess durchzuführen, der ptrace selbst aufruft. Die eigentliche Schwierigkeit besteht darin, dass ein Tracerprozess zum Elternteil des Tracee wird, wenn er am letzteren angebracht ist. Und wenn Ihr Tracerprozess alle Verhaltensweisen aller (direkten und indirekten) Kindprozesse verfolgen möchte (z. B. wie bei einem Debuggerprogramm, das ein Mehrprozessprogramm debuggen muss), wird die ursprüngliche Prozesshierarchie natürlich unterbrochen und alle interprozessuellen/-thread-Kommunikationen (wie Thread-Synchronisation, Signalversand/-empfang, ...) unter allen Kindprozessen müssen vom Tracerprozess emuliert/multiplexiert werden. Es ist immer noch "möglich", aber viel schwieriger und ineffizienter.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X