This is an old revision of the document!
Processus
, Chapitre 26 Surveillance des processus enfants
Gestion des processus
Un processus est un programme en cours d'exécution. Les processus sont l'unité primitive à travers laquelle le système d'exploitation alloue des ressources aux utilisateurs. Chaque processus a un espace d'adressage et un ou plusieurs threads. Plusieurs processus peuvent exécuter le même programme, mais deux processus sont complètement indépendants.
Les informations de processus sont conservées dans une structure appelée “Bloc de contrôle de processus” (PCB), une pour chaque processus du système. Parmi les informations les plus importantes contenues par PCB, on trouve:
PC
(programme de comptage), SP
(indicateur de pile)Lors du lancement d'un programme, un processus sera créé dans le système d'exploitation pour allouer les ressources nécessaires à l'exécution du programme. Chaque système d'exploitation fournit des appels système permettant de travailler avec des processus: création, fin, attente de fin. En même temps, il y a des appels à la duplication de descripteurs de ressources entre processus, ou à la fermeture de ces descripteurs.
Les processus peuvent avoir une organisation:
init
(pid = 1).Généralement, un processus s'exécute dans un environnement spécifié par un ensemble de variables d'environnement . Une variable d'environnement est une paire 'NOM = valeur'. Un processus peut vérifier ou définir la valeur d'une variable d'environnement via une série d'appels de bibliothèque ( Linux, Windows).
Les canaux (canaux de communication) sont des mécanismes primitifs de communication entre processus. Un canal peut contenir une quantité limitée de données. L'accès à ces données est de type FIFO (les données sont écrites à une extrémité du canal pour être lues à l'autre extrémité). Le système d'exploitation garantit la synchronisation entre les opérations de lecture et d'écriture aux deux extrémités ( Linux, Windows).
Il existe deux types de tuyaux:
Le lancement d'un programme implique les étapes suivantes:
exec*
.Sous UNIX, un processus est créé à l’aide de l’appel système fork:
os.fork();
L'effet est la création d'un nouveau processus (le processus enfant), une copie du “fork” (le processus parent). Le processus enfant reçoit un nouvel identificateur de processus ( PID
) du système d'exploitation.
Pour connaître le «PID» du processus actuel et du processus parent, appelez les fonctions ci-dessous.
La fonction getpid renvoie le PID
du processus appelant:
os.getpid()
La fonction getppid renvoie le “PID” du processus parent du processus appelant:
os.getppid()
La famille de fonctions exec exécutera un nouveau programme, remplaçant l'image du processus en cours par celle d'un fichier (exécutable). Cela signifie:
PID
et les descripteurs de fichier qui n'ont pas activé l'indicateur CLOSE_ON_EXEC
restent inchangés (par défaut, CLOSE_ON_EXEC
n'est pas défini).os.execl(path, arg1, arg2, ...); os.execv(path, args); os.execlp(file, arg1, arg2, ...);
Exemple d'utilisation des fonctions ci-dessus:
os.execl("/bin/ls", "ls", "-la") args = ["ls", "-la"] os.execv("/bin/ls", args) os.execlp("ls", "ls", "-la")
execl
et execv
ne recherchent pas le programme donné en tant que paramètre dans PATH
, il doit donc être accompagné du chemin complet. Les versions execlp et execvp recherchent le programme dans PATH
.
Toutes les fonctions exec *
sont implémentées par l'appel système execve.
La famille de fonctions wait suspend l'exécution du processus appelant jusqu'à ce que le ou les processus spécifiés dans les arguments se soient terminés ou aient été arrêtés (“SIGSTOP”).
os.waitpid(pid, options)
L’état du processus peut être trouvé en examinant status
avec des macrodefines telles que WEXITSTATUS, qui renvoie le code d'erreur avec lequel le processus attendu a été achevé, en évaluant le plus insignifiant 8 bits.
Il existe une version simplifiée qui attend la fin d’un processus enfant. Les séquences de code suivantes sont équivalentes:
(pid, status, info) = wait3(0) | status = waitpid(-1,0)
Pour terminer le processus en cours, Linux fournit l'appel système sys.exit.
import sys sys.exit(status)
Un processus dont le parent est terminé s'appelle processus orphelin . Ce processus est automatiquement adopté par le processus init
, mais il porte toujours le nom orphelin car le processus qui l'a créé à l'origine n'existe plus.
Un processus terminé dont le parent n'a pas encore lu le statut de la terminaison est appelé processus zombie . Le processus entre dans un état final et les informations continuent d'exister dans la table des processus afin de donner au parent la possibilité de vérifier le code avec lequel le processus s'est terminé. Lorsque le parent appelle l'attente, les informations sur le processus disparaissent. Tout processus enfant passera par le statut de zombie à la fin.
Pour terminer un autre processus dans le système, un signal sera envoyé à ce processus via l'appel système kill. Plus de détails sur 'tuer' et les signaux dans signal lab.
import os def my_system(command): try: pid = os.fork() if pid == -1: # error forking return os.EXIT_FAILURE elif pid == 0: # child process os.execvp(command, args) # only if exec failed */ os._exit(os.EXIT_FAILURE) else: # parent process pass #only parent process gets here status = os.waitpid(pid, 0) if (os.WIFEXITED(status)): print("Child {} terminated normally, with code {}".format(pid, os.WEXITSTATUS(status))) return status except Exception as e: print ("Error: {}".format (e)) my_system("ls")
dup duplique le descripteur de fichier oldfd
et renvoie le nouveau descripteur de fichier, ou -1
en cas d'erreur:
os.dup(oldfd)
dup2 duplique le descripteur de fichier oldfd
dans le descripteur de fichier 'newfd'; si newfd
existe, il sera fermé en premier. Retourne le nouveau descripteur de fichier, ou -1
en cas d'erreur:
os.dup2(oldfd, newfd)
Les descripteurs de fichiers sont en réalité des indices dans le tableau de fichiers ouverts. Le tableau est rempli par des structures avec des informations sur les fichiers. La duplication d'un descripteur de fichier signifie la duplication de l'entrée dans la table de fichiers ouverte (c'est-à-dire que 2 pointeurs situés à des positions différentes de la table pointeront sur la même structure de fichiers associée au fichier). Pour cette raison, toutes les informations associées à un fichier (verrou, curseur, drapeau) sont partagées par les deux descripteurs de fichier. Cela signifie que les opérations qui modifient ces informations sur l'un des descripteurs de fichier (par exemple, “lseek”) sont également visibles pour l'autre fichier de descripteur (dupliqué).
os.CLOSE_ON_EXEC
n'est pas partagé (cet indicateur n'est pas conservé dans la structure ci-dessus).
Les descripteurs de fichier du processus parent sont hérités du processus enfant après l'appel 'fork'. Après un appel “exec”, les descripteurs de fichier sont conservés, à l'exception de ceux pui possedentl'indicateur “CLOSE_ON_EXEC ”.
Dans un programme, il est possible d'accéder aux variables d'environnement en accedant la structure os.environ qui contient toutes les variables sous la forme <clé, valeur>, ou la clé représente le nom de la variable.
getenv renvoie la valeur de la variable d'environnement nommée name
ou une valeur défaut s'il n'y a pas de variable d'environnement avec ce nom. Si la valeur prédéfinie n'est pas précisée, la fonction va retoruner None:
os.getenv(key, defaultValue)
Pour attribuer une valeur a une variable d'environnement, on utilise la structure environ:
os.environ['API_USER'] = 'username'
unsetenv supprime la variable nommée name
de l'environnement:
os.unsetenv(name)
Pour résoudre le labo, veuillez cloner repository. Si vous l'avez déjà clonné, veuillez lancer git pull
.
utils.h
avec des fonctionnalités utiles dans le répertoire utils
de l'archive.
Allez dans le répertoire 1-system
.
Le programme my_system.py
exécute une commande envoyée en tant que paramètre à l'aide de la fonction de bibliothèque system. Le fonctionnement de system est le suivant:
sh
avec les arguments -c commande
, pendant que le processus parent attend la fin de l'enfant.Lancez le programme en donnant une commande comme paramètre.
python3 my_system.py pwd
Comment pouvez-vous envoyer plus de paramètres à une commande? (par exemple: ls -la
)
Pour voir le nombre d'appels de système execve sont réalisés, exécutez:
strace -e execve,clone -ff -o output ./my_system ls
execve,clone
-ff
accompagné de -o output
génère un fichier de sortie pour chaque processus.Consultez la section Remplacement d’une image de processus sous Linux et du execve.
Remplacez le système
par execlp
. La fonction reçoit comme parametre une commande et une liste d'arguments (séparés par virgule). Suivez la ligne avec TODO 1.
Allez dans le répertoire 2-parameters
.
Exécutez le programme parameters.py en utilisant la commande 'python3 parameters.py'. Qu'est-ce que le programme parameters.py fait ?
Résolvez l'exercice dans le fichier program.c.
Utilisez la fonction system
pour exécuter le programme paramètres avec certains paramètres.
Remplacez la fonction system par execlp . Pourquoi le texte n'est plus affiché par print après execl ?
Suivez les lignes avec TODO 2.
Allez dans le répertoire 3-run
.
Utilisez les fonctions fork et execl pour exécuter la commande ls -l
dans run.py.
Suivez les lignes avec TODO 1.
Assurez-vous que le texte “ls a été exécuté” apparaît après la fin du programme ls . (Indice: waitpid)
Suivez les lignes avec TODO 2.
Déplacez et modifiez la ligne avec TODO 3 afin que le code de sortie de ls soit affiché. (Indice: WEXITSTATUS)
Exécutez le programme exitcode.py et affichez le code de sortie. Modifiez le programme exitcode.py afin qu'il renvoie un autre code de sortie.
Suivez les lignes avec TODO 4.
Allez dans le répertoire 4-orphan
et inspectez la source orphan.py
.
Donnez des dorits d'exécution sur le fichier ( chmod u+x
) et exécutez le programme a l'aide de la commande:
./orphan.py
which python3
et remplacez la chemin de la premiere ligne du fichier par celle affichée dans le terminal.
watch -d '(ps -al | grep -e orphan -e PID)'
Notez que pour le processus indiqué par l'exécutable 'orphelin' (colonne CMD), le pid du processus parent (colonne 'PPID') devient 1 car le processus est adopté par init
à la fin du processus. son parent. Pourquoi y a-t-il deux processus orphelins?
Allez dans le répertoire 5-zombie
et inspectez la source zombie.py
.
Donnez des droits d'exécution sur le fichier ( chmod u+x
) puis exécutez-le en utilisant la commande suivante:
./zombie.py
which python3
et remplacez la chemin de la premiere ligne du fichier par celle affichée dans le terminal.
Ouvrez un autre terminal et lancez la commande:
watch -d '(ps -al | grep -e orphan -e PID)'
Notez que pour le processus indiqué par l'exécutable zombie, la colonne 'CMD' devient 'zombie' '<obsolète>. Qu'est-ce qui se passe vraiment?
Modifiez le fichier zombie.py afin que le processus ne devienne pas un zombie (indice: waitpid).
Suivez les lignes avec TODO 1.
Allez dans le répertoire 6-tiny
.
Les points suivants sont ont comme but l'implémentation d'un shell minimal prenant en charge l'exécution d'une seule commande externe avec plusieurs arguments et redirections. Le shell doit fournir du support pour l'utilisation et la définition des variables environnementales.
Note: Pour quitter le petit shell, utilisez exit
ou CTRL + D
.
Créez un nouveau processus pour exécuter une commande simple.
La fonction simple_cmd
reçoit comme argument un vecteur de chaînes contenant la commande et ses paramètres.
Voir l'exemple mon_système et suivez les commentaires avec TODO 1
dans le code.
Pour tester, vous pouvez utiliser les commandes:
./tiny > pwd > ls -al > exit
Vous devez remplir les fonctions 'set_var' et 'expand'; ils sont déjà appelées lorsque la ligne de commande est analysée. Les erreurs doivent être vérifiées dans ces fonctions.
TODO 2
dans le code../tiny > echo $HOME > name=Makefile > echo $name
Remplissez le champ do_redirect
pour que tiny-shell prenne en charge la redirection de la sortie d'une commande (stdout) dans un fichier.
Si le fichier spécifié par filename
n'existe pas, il sera créé. S'il existe, il doit être tronqué.
Lisez la section Copie de descripteurs de fichiers et suivez les commentaires avec le mot “TODO 3” dans le code. Pour tester, vous pouvez utiliser les commandes:
./tiny > ls -al > out > cat out > pwd > out > cat out