web-dev-qa-db-fra.com

Python: comment fonctionne os.fork ()?

J'apprends le multi-traitement en python. J'ai essayé le multiprocessing et après avoir lu le code source du module multiprocessing, je l'ai trouvé utiliser os.fork(), donc j'écris du code pour tester os.fork(), mais je suis bloqué. Mon code est le suivant:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import time

for i in range(2):
    print '**********%d***********' % i
    pid = os.fork()
    print "Pid %d" % pid

Je pense que chaque impression sera exécutée deux fois mais elles s'exécuteront trois fois. Je ne comprends pas comment ça marche? J'ai lu ceci Besoin de savoir comment fonctionne Fork?
D'après ce que dit cet article, il sera également exécuté deux fois, donc je suis tellement coincé ...

18
tudouya

Pour répondre directement à la question, os.fork() fonctionne en appelant la fonction sous-jacente du système d'exploitation fork().

Mais vous êtes sûrement intéressé par ce que cela fait. Eh bien, cela crée un autre processus qui reprendra exactement au même endroit que celui-ci. Ainsi, lors de la première boucle, vous obtenez un fork après lequel vous avez deux processus, le "original" (qui obtient une valeur pid du PID du processus enfant) et le forked (qui obtient un pid valeur de 0).

Ils impriment tous les deux leur valeur pid et poursuivent la 2e boucle, qu'ils impriment tous les deux. Ensuite, ils bifurquent tous les deux, vous laissant avec 4 processus qui affichent tous leurs valeurs pid respectives. Deux d'entre eux devraient être 0, les deux autres doivent être les PID de l'enfant qu'ils viennent de créer.

Changer le code en

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import time

for i in range(2):
    print '**********%d***********' % i
    pid = os.fork()
    if pid == 0:
        # We are in the child process.
        print "%d (child) just was created by %d." % (os.getpid(), os.getppid())
    else:
        # We are in the parent process.
        print "%d (parent) just created %d." % (os.getpid(), pid)

vous verrez mieux ce qui se passe: chaque processus vous indiquera son propre PID et ce qui s'est passé sur le fork.

24
glglgl

Tout d'abord, supprimez cette ligne print '******...'. Cela confond tout le monde. Essayons plutôt ce code ...

import os
import time

for i in range(2):
    print "I'm about to be a dad!"
    time.sleep(5)
    pid = os.fork()
    if pid == 0:
        print "I'm {}, a newborn that knows to write to the terminal!".format(os.getpid())
    else:
        print "I'm the dad of {}, and he knows to use the terminal!".format(pid)
        os.waitpid(pid)

D'accord, tout d'abord, qu'est-ce que "fork"? Fork est une fonctionnalité des systèmes d'exploitation modernes et conformes aux normes (à l'exception de M $ Windows: cette plaisanterie d'un système d'exploitation est tout sauf moderne et conforme aux normes) qui permet un processus (aka: " programme ", et cela inclut l'interpréteur Python!) pour créer littéralement un doublon exact de lui-même, créant effectivement un nouveau processus (une autre instance du" programme "). Une fois cette magie effectuée, les deux processus sont indépendants, tout changement dans l'un d'eux n'affecte pas l'autre.

Le processus responsable de l'énonciation de cette incantation sombre et ancienne est connu comme le processus parent. Le résultat sans âme de cette abomination immorale envers la vie elle-même est connu comme le processus de l'enfant.

Comme cela sera évident pour tous, y compris ceux pour lesquels il ne l'est pas, vous pouvez devenir membre de ce groupe restreint de programmeurs qui ont vendu leur âme au moyen de os.fork(). Cette fonction effectue une opération de fourche, et se traduit ainsi par un deuxième processus créé à partir de l'air mince.

Maintenant, que renvoie cette fonction, ou plus important encore, comment retourne-t-elle même? Si vous ne voulez pas devenir fou, s'il vous plaît n'allez pas lire le fichier /kernel/fork.c Du noyau Linux! Une fois que le noyau fait ce que nous savons qu'il doit faire, mais que nous ne voulons pas l'accepter, os.fork() retourne dans les deux processus! Oui, même la pile d'appels est copiée!

Donc, si ce sont des copies exactes, comment différencier parent et enfant? Facile. Si le résultat de os.fork() est nul, alors vous travaillez dans l'enfant. Sinon, vous travaillez dans le parent et la valeur de retour est le PID (Process IDentifier) ​​de l'enfant. Quoi qu'il en soit, l'enfant peut obtenir son propre PID à partir de os.getpid(), non?

Maintenant, en tenant compte de cela, et le fait que faire fork() à l'intérieur d'une boucle est la recette du désordre, c'est ce qui se passe. Appelons le processus d'origine le processus "maître" ...

  • Maître: i = 0, Se transforme en enfant n ° 1 du maître
    • Enfant n ° 1 maître: i = 1 Se transforme en enfant n ° 1 enfant n ° 1 maître
    • Child- # 1-of-child- # 1-of-master: for boucle sur, quitte
    • Enfant n ° 1 maître: for boucle, quitte
  • Maître: i = 1, Se transforme en enfant n ° 2 du maître
    • Enfant n ° 2 maître: i = 1 Se transforme en enfant n ° 1 enfant n ° 2 maître
    • Child- # 1-of-child- # 2-of-master: for boucle sur, quitte
    • Enfant n ° 2 maître: for boucle, quitte
  • Maître: for boucle sur, quitte

Comme vous pouvez le voir, il y a un total de 6 impressions parent/enfant provenant de 4 processus uniques, résultant en 6 lignes de sortie, quelque chose comme ...

Je suis le père de 12120, et il sait utiliser le terminal!

Je suis 12120, un nouveau-né qui sait écrire sur le terminal!

Je suis le père de 12121, et il sait utiliser le terminal!

Je suis 12121, un nouveau-né qui sait écrire sur le terminal!

Je suis le père de 12122, et il sait utiliser le terminal!

Je suis 12122, un nouveau-né qui sait écrire sur le terminal!

Mais c'est juste arbitraire, cela aurait pu produire cela à la place ...

Je suis 12120, un nouveau-né qui sait écrire sur le terminal!

Je suis le père de 12120, et il sait utiliser le terminal!

Je suis 12121, un nouveau-né qui sait écrire sur le terminal!

Je suis le père de 12121, et il sait utiliser le terminal!

Je suis 12122, un nouveau-né qui sait écrire sur le terminal!

Je suis le père de 12122, et il sait utiliser le terminal!

Ou autre chose que ça. Le système d'exploitation (et les horloges géniales de votre carte mère) est seul responsable de l'ordre dans lequel les processus obtiennent des tranches de temps, alors allez blâmez Torvalds (et n'attendez pas d'auto-pilotage à votre retour) si vous n'aimez pas la façon dont le noyau gère pour organiser vos processus;).

J'espère que cela vous a éclairé!

38
3442