2017-06-27 3 views
8

Je regardais la fonction _do_fork()() essayant de comprendre comment fork() retourne le PID enfant pour le processus parent et 0 sur le processus de l'enfant.Comment _do_fork() peut renvoyer deux PID différents (un pour le processus parent et un pour le processus enfant)

Je pense que nr contient le PID du processus enfant (qui sera retourné au processus appelant), mais je ne vois pas comment il est capable de retourner 0 au processus enfant.

La réponse How does fork() know when to return 0? dit que la valeur de retour est passé sur la pile créé pour le nouveau processus, mais (en plus pas vraiment comprendre) Je ne peux pas trouver que dans le code.

Alors, où la valeur de retour de 0 est définie pour le processus enfant?

Le code de la fonction _do_fork() est recopié ci-dessous:

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

Vous voulez comprendre le code de Linux, Certaines personnes ont essayé mais ils ont eu des problèmes. – Stargateur

Répondre

9

Vous avez correctement identifié la façon dont le nouveau numéro de processus est retourné au parent, avec return nr. Mais vous ne verrez jamais un return 0 nulle part puisque ce code est exécuté sur le thread parent. Ce code n'est pas pour le nouveau processus qui est créé.

Examinons maintenant la fonction _do_fork.

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

C'est là que toute la magie se produit. Lorsque vous appelez le copy_process , il appelle en interne copy_thread qui est un code spécifique à la cible. Cette fonction est responsable de l'adaptation des structures de données liées au thread. Maintenant, disons que nous avons la cible comme X86_64 avec la convention d'appel que la valeur de retour est retournée dans le registre %rax. Cette fonction copie ensuite 0 dans %rax et copie la valeur de l'adresse return_from_fork à %rip (le pointeur d'instruction). Sur les autres plateformes, l'ABI peut exiger que la valeur de retour soit placée sur la pile. Dans ce cas, 0 est placé sur la pile. copy_thread est spécifique à la cible, mais copy_process ne l'est pas. Est l'implémentation de copy_thread pour X86_64. Vous pouvez voir autour de la ligne 160 les registres sp en cours de définition. Et à la ligne 182, vous pouvez voir %ax (qui est un sous-registre de% rax) étant mis à 0.

J'espère que cela efface une certaine confusion.