TNSI : TD Processus en Python : Le module os
⚓︎
Danger
Ce TD est spécifique à Unix/Linux
- Référence Ultime de ce TD : Le module
os
de la Librairie Standard. - Les traductions automatiques en Français
sont quelquefois douteuses (malheureusement).. ne pas hésiter à lire la source (en Anglais
)
- Les programmes Python doivent être exécutés en ligne de commande, dans un Terminal
Lire des Informations sur les Processus⚓︎
Le code suivant, à sauvegarder, modifier et tester dans un fichier identifier.py
:
# identifier.py (A sauvegarder sous ce nom-là)
import os
os.getpid() # PID du processus courant
os.getppid() # PPID : PID du Père
os.getuid() # UID : User ID = ID du propriétaire réel (celui qui l'exécute)
os.geteuid() # Effecive UID = UID du propriétaire effectif (celui qui l'a créé)
os.getgid() # groupe réel (Group ID)
os.getegid() # groupe effectif (groupe de celui qui l'a créé)
os.setuid(uid) # change le propriétaire du processus
- Copier-coller ce code, sauvegardez-le (en tant que
identifier.py
), modifier-le, exécutez-le. - Afficher en détaillant tous les PIDs obtenus dans un Terminal (en ajoutant des
print
) - Placer ces méthodes dans une fonction
identifierProcessus.py
, et configurez le moduleidentifier.py
avec unif __name__ == main
Créer un processus Fils avec os.fork()
sur Linux⚓︎
newpid = os.fork()
crée un processus fils du processus courant, et renvoie un nombre entier (ici stocké dans la varaible newpid
) qui prend des valeurs différentes selon que l'on se trouve dans le processus fils ou dans le père :
Valeur denewpid = os.fork() (un entier) |
Signification Quel Processus est en cours d'exécution ? |
---|---|
<0 |
Problème de Création |
0 |
Processus Fils |
>0 en fait = au PID du fils, lorsqu'on se trouve à l'intérieur du Père |
Processus Père |
- Création d’un processus fils dans le fichier
creation.py
:
a. Analysez le programme suivant, et commentez-le (càd ajouter des commentaires), en particulier dans lesif elif else
:b. Modifier le programme pour qu'il affiche 3 fois « dans le père » et 5 fois « dans le fils », dans le Terminal, en utilisant une boucle# creation.py import os import time def pereFils(): print("je suis le père") newpid = os.fork() if newpid == -1: print("Erreur de création") elif newpid == 0: print("Dans le fils") time.sleep(2) else: print("Dans le père") time.sleep(2) if __name__=="__main__": pereFils()
for
dans chacun des cas.
c. Modifier le programme pour qu'il affiche 3 fois « dans le père » et 5 fois « dans le fils », dans le Terminal, en mettant une seule bouclefor
à la fin de la fonction. - Création d’un processus zombie. Exécuter le programme suivant, nommé
zombie.py
:# zombie.py import os import time def pereFils(): #Variables du père, qui seront dupliquées dans le fils #Création du fils newpid = os.fork() if newpid == -1: print("Erreur de création") elif newpid == 0: # dans le fils #Variables modifiées par le fils donc dans son propre espace mémoire a=1 print('{0} {1}'.format('PID du fils ',os.getpid())) time.sleep(0.2) else: #newpid>0 -> dans le père print('{0} {1}'.format('PID du père ',os.getpid())) time.sleep(30) if __name__=="__main__": pereFils()
- Dans une autre console, taper
$ ps -eo pid,ppid,stat,command
. Quel est l’état du processus fils. Pourquoi ?
Le fils est dans l'état Zombie Z+
Le +
signifie qu'il fait partie des processus du premier plan.
44078 2052 Sl xfce4-terminal
197090 44078 Ss bash
197099 197090 S+ man ps
228454 2 I [kworker/18:1-events]
228525 2 I [kworker/13:2]
228534 2 I [kworker/u40:2-events_unbound]
235612 31479 S+ python zombie.py
235613 235612 Z+ [python] <defunct>
235681 2280 S /bin/bash /usr/lib/code/out/vs/base/node/cpuUsage.sh 2331 42789
235685 235681 S sleep 1
235686 159336 R+ ps -eo pid,ppid,stat,command
Le module signal
⚓︎
Le module signal
de la librairie Standard (cf Référence module signal) permet de travailler avec les différents Signaux Linux (liste ici):
Exp
import signal
monSignal = signal.SIGTERM # représente le Signal SIGTERM
# stocké dans la variable monSignal
Interruption d’un Processus avec os.kill()
⚓︎
La méthode os.kill(pid, sig)
envoie le signal sig
au processus identifié de manière unique par son pid
.
On se donne le programme suivant, à copier-coller et sauvegarder sous tuerProcessus.py
.
# tuerProcessus.py
import os
import time
import signal
def pereFils():
#Variables du père, qui seront dupliquées dans le fils
#Création du fils
newpid = os.fork()
if newpid == -1:
print("Erreur de création")
elif newpid == 0: # dans le fils
#Variables modifiées par le fils donc dans son propre espace mémoire
a=1
print('{0} {1}'.format('PID du fils ',os.getpid()))
time.sleep(0.2)
else: #newpid>0 -> dans le père
print('{0} {1}'.format('PID du père ',os.getpid()))
time.sleep(30)
if __name__=="__main__":
pereFils()
- Lancer
tuerProcessus.py
, observer la liste des processus avec la commandeps
en affichant les pid et ppid. - Que se passe t-il pour le père ?
- Que se passe t-il pour le fils ?
Signaux avec os.wait()
et os.waitid()
⚓︎
La méthode wait()
⚓︎
Référence : Librairie Standard + https://linux.die.net/man/2/wait ($ man 2 wait
)
La méthode wait()
du module os
de Python est bloquante et attend qu'un processus fils soit terminé, elle ne reçoit aucun argument en entrée, puis renvoie en sortie un tuple contenant :
- le PID du fils
- son état/statut de sortie : Il s'agit d'un nombre entier de 16 bits tel que :
- L'octet de poids faible est le numéro de signal qui a tué le processus,
- L'octet de poids fort est le statut de sortie, si le numéro de signal vaut 0.
- Le bit de poids fort de l'octet de poids faible est mis à 1 si un (fichier système) core file a été produit.
-
ou bien, renvoie -1 si le père n'a pas de fils
-
Exécuter le programme suivant, sous le nom de fichier
attend.py
(attention : PASwait.py
sinon conflit avec la librairie standard):# attend.py (Sauvegarder sous ce nom) import os import time def pereFils(): newpid = os.fork() if newpid == -1: print("Erreur de création") elif newpid == 0: # dans le fils for i in range(0, 5): print("J'écris %d"%(i)) time.sleep(2) else: #newpid>0 -> dans le père childProcExitInfo = os.wait() if __name__=="__main__": pereFils()
Compléter le programme avec les instructions suivantes :
print("Le processus fils %d a terminé"%(childProcExitInfo[0])) print("Le père %d se termine après que le fils ait terminé"%(os.getpid())) print("Le processus fils de PID %d termine" %os.getpid()) print("Dans le fils") print("je suis le père") print("Le père attend que le fils ait terminé")
de sorte obtenir l’affichage en sortie suivant :
je suis le père Le père attend que le fils ait terminé Dans le fils Le fils écrit 0 Le fils écrit 1 Le fils écrit 2 Le fils écrit 3 Le fils écrit 4 Le processus fils de PID 956 termine Le processus fils 956 a terminé Le père 955 se termine après que le file ait terminé
La méthode waitid()
⚓︎
Référence : Librairie Standard + https://linux.die.net/man/3/waitid ($ man 3 waitid
)
La méthode waitid(idtype, id, options)
du module os
est bloquante et attend la fin d'un ou plusieurs processus fils. Cette méthode os.waitid()
-
accepte en entrée 4 arguments :
-
idtype
peut prendre 3 valeurs constantesP_PID
,P_PGID
,P_ALL
, ouP_PIDFD
sur Linux, qui indiquent comment l'argumentid
doit être interprété, càd quel(s) sont le/les processus à attendre :Signal signification P_PID
N'attendre que le processus fils indiqué P_PGID
Attendre Tout le groupe Unix du processus fils P_ALL
Attendre Tous les processus fils
l'argumentid
sera alors ignoréP_PIDFD
id
désigne ici un Descripteur de Fichier
qui fait référence au processus -
id
est le PID du processus qui doit être attendu. infop
le nom de la variable contenant le statut de retouroptions
précise une ou plusieurs constantes suivantes, séparées par un OR (|
en Unix), et qui servent à indiquer quels sont les types de signaux à attendre pour mettre fin au processus fils :
Type de Signal
Plus d'infos sur$ man 3 waitid
Signification : Quel signal attendre du fils? os.WEXITED
Attend les processus fils qui ont terminé via un exit os.WSTOPPED
Attend chaque processus fils ayant été stoppé après réception d'un signal
et dont le statut :- ou bien n'a pas été rapporté depuis sa fin
- ou bien n'a été rapporté que par des appels à
waitid()
avec un drapeauWNOWAIT
os.WCONTINUED
Attend chaque processus fils ayant repris (continué) et dont le statut : - ou bien n'a pas été rapporté depuis sa reprise
après un stop du job control - ou bien n'a été rapporté que par des appels à
waitid()
avec un drapeauWNOWAIT
Options
Facultatives\(\,\) os.WNOHANG
ne pas attendre si pas de statut.
Renvoyer/Revenir instantanémentos.WNOWAIT
Empêche le processus renvoyé dans infop
d'être en attente
L'état du processus ne devrait pas être modifié
Le processus pourra être de nouveau attendu
après la fin de cet appeloption = os.WSTOPPED | os.WEXITED
-
-
Renvoie en sortie un objet modélisant une structure de données POSIX (
posix.waitid_result
), une sorte wrapper de tuple de plusieurs entiers, décrivant le statut du processus sous forme des plusieurs attributs suivants, dans cet ordre (accessibles via la syntaxe POO en Python) :si_pid
,si_uid
,si_signo
,sig_status
,si_code
ou bienNone
si WNOHANG a été spécifiée et qu'aucun processus enfant n'est dans un état d'attente :Valeur
du Statut
RenvoyéDans quelle situation? -1
En cas d'erreur
eterrno
contient alors le numéro de l'erreur0
Au cas où waitid()
soit en retour lié à
un changement d'état de l'un de ses enfants
/>ou bien, pour tout processus identifié
paridtype
etid
dans le cas oùWNOHANG
a été spécifié
et que le statut n'est pas disponibleExp
idtype = os.P_ALL id = pid option = os.WSTOPPED | os.WEXITED statut = os.waitid(idtype, id, option) print(statut.sig_pid) # print(statut[0])
Signal | Signification |
---|---|
os.WIFEXITED(statut) |
Vrai si le processus fils identifié par son statut de sortie s'est terminé normalement càd par l'utilisation de sys.exit() ou os._exit() , par retour de main() (par défaut) |
os.WEXITSTATUS(statut) |
Fournit le statut de retour du fils s'il s'est terminé correctement A n'employer que si WIFEXITED() est Vrai |
os.WIFSIGNALED |
Vrai si le fils s'est terminé à cause d'un signal |
os.WTERMSIG |
Fournit le n° du Signal ayant provoqué la fin du processus |
os.WIFSTOPPED |
Vrai si le processus est Stoppé (si waitid avec WUNTRACED) |
os.WSTOPSIG() |
Fournit le n° du signal ayant stoppé le processus A n'utiliser que si WIFSTOPPED est Vrai |
-
Exécuter le programme suivant sous le nom de fichier
attendId.py
(attention : PASwaitid.py
sinon conflit avec la librairie standard), puis modifier-le de sorte que: -
un nouveau fils soit créé après
if pid>0 :
et -
de sorte que le père attende la fin d’exécution des 2 fils avant de se terminer
# attendId.py (Sauvegarder sous ce nom) import os newpid = os.fork() if newpid>0 : # dans le père # Spécifier idtype idtype = os.P_PID # Spécifier id id = newpid # Spécifier option option = os.WEXITED status = os.waitid(idtype, id, option) print("\n\nDans le parent--") print("Statut du fils:") print(status) else : print("Dans le premier enfant-") print("Process ID:", os.getpid()) print("Hello ! Geeks") print("Exiting..")