2017-06-27 78 views
8

Patrzyłem na funkcję _do_fork()() próbując zrozumieć, jak fork() zwraca PID dziecka dla procesu macierzystego i 0 na proces potomny.W jaki sposób _do_fork() zwraca dwa różne PID (jeden dla procesu nadrzędnego i jeden dla procesu potomnego)

Myślę, że nr zawiera PID procesu potomnego (który zostanie zwrócony do procesu wywołującego), ale nie widzę, jak jest on w stanie zwrócić 0 do procesu potomnego.

Odpowiedź How does fork() know when to return 0? mówi, że zwracana wartość jest przekazywana na stos utworzony dla nowego procesu, ale (poza tym niezupełnie go rozumiemy) Nie mogę znaleźć tego w kodzie.

Tak, gdzie wartość zwracana 0 jest ustawiona dla procesu potomnego?

Kod funkcji _do_fork() są kopiowane poniżej:

long _do_fork(unsigned long clone_flags, 
      unsigned long stack_start, 
      unsigned long stack_size, 
      int __user *parent_tidptr, 
      int __user *child_tidptr, 
      unsigned long tls) 
{ 
    struct task_struct *p; 
    int trace = 0; 
    long nr; 

    /* 
    * Determine whether and which event to report to ptracer. When 
    * called from kernel_thread or CLONE_UNTRACED is explicitly 
    * requested, no event is reported; otherwise, report if the event 
    * for the type of forking is enabled. 
    */ 
    if (!(clone_flags & CLONE_UNTRACED)) { 
     if (clone_flags & CLONE_VFORK) 
      trace = PTRACE_EVENT_VFORK; 
     else if ((clone_flags & CSIGNAL) != SIGCHLD) 
      trace = PTRACE_EVENT_CLONE; 
     else 
      trace = PTRACE_EVENT_FORK; 

     if (likely(!ptrace_event_enabled(current, trace))) 
      trace = 0; 
    } 

    p = copy_process(clone_flags, stack_start, stack_size, 
      child_tidptr, NULL, trace, tls, NUMA_NO_NODE); 
    add_latent_entropy(); 
    /* 
    * Do this prior waking up the new thread - the thread pointer 
    * might get invalid after that point, if the thread exits quickly. 
    */ 
    if (!IS_ERR(p)) { 
     struct completion vfork; 
     struct pid *pid; 

     trace_sched_process_fork(current, p); 

     pid = get_task_pid(p, PIDTYPE_PID); 
     nr = pid_vnr(pid); 

     if (clone_flags & CLONE_PARENT_SETTID) 
      put_user(nr, parent_tidptr); 

     if (clone_flags & CLONE_VFORK) { 
      p->vfork_done = &vfork; 
      init_completion(&vfork); 
      get_task_struct(p); 
     } 

     wake_up_new_task(p); 

     /* forking complete and child started to run, tell ptracer */ 
     if (unlikely(trace)) 
      ptrace_event_pid(trace, pid); 

     if (clone_flags & CLONE_VFORK) { 
      if (!wait_for_vfork_done(p, &vfork)) 
       ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); 
     } 

     put_pid(pid); 
    } else { 
     nr = PTR_ERR(p); 
    } 
    return nr; 
} 
+3

Chcesz zrozumieć kod Linuksa, Niektórzy próbowali, ale mieli problemy. – Stargateur

Odpowiedz

9

Masz poprawnie zidentyfikowane jak nowy identyfikator procesu wraca do rodziców, z return nr. Ale nigdy nie zobaczysz, gdzie jest return 0, ponieważ ten kod jest wykonywany w wątku rodzica. Ten kod nie dotyczy nowego tworzonego procesu.

Teraz sprawdźmy funkcję _do_fork.

... 
} 
p = copy_process(clone_flags, stack_start, stack_size, 
     child_tidptr, NULL, trace, tls, NUMA_NO_NODE); 
add_latent_entropy(); 
... 

To tutaj dzieje się cała magia. Po wywołaniu copy_process , wywołuje wewnętrznie copy_thread, który jest kodem docelowym. Ta funkcja jest odpowiedzialna za radzenie sobie z strukturami danych związanych z wątkami.

Teraz mówimy, że mamy cel jako X86_64 z konwencją wywołującą, że zwracana wartość jest zwracana w rejestrze %rax. Ta funkcja kopiuje następnie 0 do %rax i kopiuje wartość adresu return_from_fork do %rip (wskaźnik instrukcji).

Na innych platformach ABI może wymagać zwracanej wartości, aby przejść na stos. W takim przypadku 0 jest umieszczany na stosie. copy_thread jest specyficzne dla celu, ale nie jest to copy_process.

This jest implementacją copy_thread dla X86_64. Możesz zobaczyć wokół linii numer 160 ustawione rejestry sp. I w linii 182 widać %ax (który jest subregisterem% rax) ustawionym na 0.

Mam nadzieję, że to wyjaśni pewne zamieszanie.