This shows you the differences between two versions of the page.
sde:laboratoare:04 [2019/03/12 19:31] iuliana.marin |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laboratoire 4 - Processus ====== | ||
- | ===== Documents d'aide ===== | ||
- | * [[https://www.slideshare.net/alexandruradovici/sde-tp-4-processus-135693677 | Diapositives]] | ||
- | * [[http://elf.cs.pub.ro/so/res/laboratoare/lab03-refcard.pdf | lab03-refcard.pdf]] | ||
- | * [[http://elf.cs.pub.ro/so/res/tutorial/lab-03-procese/| Processes]] | ||
- | ==== Agréable à lire ==== | ||
- | * TLPI - Chapitre 6, '' Processus '', Chapitre 26 '' Surveillance des processus enfants '' | ||
- | * WSP4 - Chapitre 6, '' Gestion des processus '' | ||
- | |||
- | ===== Liens vers des sections utiles ===== | ||
- | * [[# # Créer un processus Linux | Créer un processus]] | ||
- | * [[# Remplacement d'une image de processus sous Linux | Remplacement d'une image de processus]] | ||
- | * [[# En attente de la fin d'un processus sous Linux | En attente de la fin d'un processus] | ||
- | * [[#Terminer un processus sous Linux | Terminer un processus]] | ||
- | * [[# # Exemple (mon_système) | Exemple (mon_système)]] | ||
- | * [[# Descripteurs de fichiers de copie | Copie de descripteurs de fichiers]] | ||
- | * [[# Héritage des descripteurs de fichier après les opérations fork / exec | Héritage des descripteurs de fichier après les opérations fork / exec]] | ||
- | * [[# Variables de version sous Linux | Variables d'environnement Linux]] | ||
- | |||
- | ===== Vue d'ensemble des concepts ===== | ||
- | |||
- | 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" ([[http://en.wikipedia.org/wiki/Process_control_block|PCB]]), une pour chaque processus du système. Parmi les informations les plus importantes contenues par PCB, on trouve: | ||
- | * PID - identifiant du processus | ||
- | * Espace d'adressage | ||
- | * registres généraux, '' PC '' (programme de comptage), '' SP '' (indicateur de pile) | ||
- | * ouvrir la table | ||
- | * [[so:laboratoare-2013:laborator-06| informations sur les signaux]] | ||
- | * liste des bloqués, ignorés ou en attente d'envoi au processus | ||
- | * Gestionnaires de signaux | ||
- | * informations sur les systèmes de fichiers (répertoire racine, répertoire actuel) | ||
- | |||
- | |||
- | 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: | ||
- | // hierarchical // - par exemple sous Linux - il existe une arborescence dans laquelle la racine est le processus '' init '' (pid = 1). | ||
- | * // non hiérarchique // - par exemple sous Windows. | ||
- | |||
- | 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 ([[# variables d'environnement dans Linux | Linux]], [[#variables d'environnement dans Windows | 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 | ||
- | ([[# tubes dans Linux | Linux]], [[# tubes dans Windows | Windows]]). | ||
- | |||
- | Il existe deux types de tuyaux: | ||
- | * pipes **anonymous **: ne peuvent être utilisés que par des processus **associés** (un processus parent et un ou deux enfants) car ils ne sont accessibles que par héritage. Ces canaux n'existent plus une fois que les processus ont terminé leur exécution. | ||
- | * **Name** pipes: ont un support physique - ils existent sous forme de fichiers de droits d'accès. Par conséquent, ils existeront indépendamment du processus qui les crée et peuvent être utilisés par des processus indépendants. | ||
- | |||
- | ===== Processus sous Linux ===== | ||
- | |||
- | Le lancement d'un programme implique les étapes suivantes: | ||
- | * Un nouveau processus "fork" est créé - le processus enfant dispose d'une copie des ressources du processus parent. | ||
- | * Si vous souhaitez remplacer l'image du processus enfant, vous pouvez la modifier en appelant une fonction de la famille '' exec* ''. | ||
- | ==== Créer un processus Linux ==== | ||
- | |||
- | Sous UNIX, un processus est créé à l’aide de l’appel système [[http://linux.die.net/man/2/fork|fork]]: | ||
- | <code c> | ||
- | pid_t fork(void); | ||
- | </code> | ||
- | L'effet crée 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. | ||
- | |||
- | |||
- | <note important> Cette fonction est appelée une fois et retourne (en cas de succès) deux fois: | ||
- | * Le parent retournera le pid du processus nouvellement créé (enfant). | ||
- | * Dans le processus enfant, l'appel retournera 0. | ||
- | </note> | ||
- | {{ so:laboratoare:fork.png?300|}} | ||
- | Pour connaître le «PID» du processus actuel et du processus parent, appelez les fonctions ci-dessous. | ||
- | |||
- | La fonction [[http://linux.die.net/man/3/getpid|getpid]] renvoie le '' PID '' du processus appelant: | ||
- | <code c> | ||
- | pid_t getpid(void); | ||
- | </code> | ||
- | La fonction [[http://linux.die.net/man/3/getppid|getppid]] renvoie le "PID" du processus parent du processus appelant: | ||
- | <code c> | ||
- | pid_t getppid(void); | ||
- | </code> | ||
- | ==== Remplacement d'une image de processus sous Linux ==== | ||
- | |||
- | |||
- | La famille de fonctions [[http://linux.die.net/man/3/exec|exec]] exécutera un nouveau programme, remplaçant l'image du processus en cours par celle d'un fichier (exécutable). Cela signifie: | ||
- | * L'espace adresse de processus sera remplacé par un nouveau créé spécifiquement pour l'exécution de fichier. | ||
- | * Le 'PC' (le compteur de programme), le 'SP' (l'indicateur de pile) et les registres généraux seront réinitialisés. | ||
- | * Les blocs de signaux ignorés et verrouillés sont réglés sur les valeurs par défaut, de même que les gestionnaires de signaux. | ||
- | * '' 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). | ||
- | <code c> | ||
- | |||
- | int execl(const char *path, const char *arg, ...); | ||
- | int execv(const char *path, char *const argv[]); | ||
- | int execlp(const char *file, const char *arg, ...); | ||
- | |||
- | </code> | ||
- | |||
- | Exemple d'utilisation des fonctions ci-dessus: | ||
- | <code c> | ||
- | execl("/bin/ls", "ls", "-la", NULL); | ||
- | |||
- | char *const argvec[] = {"ls", "-la", NULL}; | ||
- | execv("/bin/ls", argvec); | ||
- | |||
- | execlp("ls", "ls", "-la", NULL); | ||
- | </code> | ||
- | |||
- | <note important> Le premier argument est le nom du programme. Le dernier argument de la liste de paramètres doit être NULL, que la liste soit sous la forme d'un vecteur (execv *) ou en tant qu'argument variable (execl *). | ||
- | </note> | ||
- | |||
- | '' 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 [[http://linux.die.net/man/2/execve|execve]]. | ||
- | ==== En attente de terminer un processus sous Linux ==== | ||
- | |||
- | La famille de fonctions [[http://linux.die.net/man/3/waitpid|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"). | ||
- | <code c> | ||
- | pid_t waitpid(pid_t pid, int *status, int options); | ||
- | </code> | ||
- | L’état du processus peut être trouvé en examinant '' status '' avec des macrodefines telles que [[http://linux.die.net/man/3/waitpid|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: | ||
- | <code c> | ||
- | wait(&status); | waitpid(-1, &status, 0); | ||
- | </code> | ||
- | |||
- | Si vous souhaitez uniquement attendre la fin du processus enfant sans examiner l'état, vous pouvez utiliser: | ||
- | <code c> | ||
- | wait(NULL); | ||
- | </code> | ||
- | |||
- | ==== Terminer un processus Linux ==== | ||
- | Pour terminer le processus en cours, Linux fournit l'appel système ''exit''. | ||
- | <code c> | ||
- | void exit(int status); | ||
- | </code> | ||
- | |||
- | <spoiler Apeluri exit> | ||
- | A partir d'un programme '' C '', il existe trois manières d'appeler cet appel système: | ||
- | |||
- | 1. Appelez [[http://linux.die.net/man/2/exit|_exit]] [[[http://linux.die.net/man/7/standards|POSIX.1-2001]] ): | ||
- | <code c> | ||
- | void _exit(int status); | ||
- | </code> | ||
- | 2. Appelez [[http://linux.die.net/man/2/exit|_Exit]] depuis la bibliothèque standard '' C '' (selon [[http: //en.wikipedia.org/wiki/C99 | C99]]): | ||
- | <code c> | ||
- | void _Exit(int status); | ||
- | </code> | ||
- | 3. L'appel [[http://linux.die.net/man/3/exit|exit]] de la bibliothèque standard ''C'' (selon ''C89'', ''C99''), celle décrite ci-dessus ci-dessus. | ||
- | |||
- | |||
- | ''_exit (2)'' et''_Exit(2)'' sont fonctionnellement équivalents (uniquement s'ils sont définis par des normes différentes): | ||
- | * le processus d'appel se termine immédiatement | ||
- | * tous les descripteurs de processus de fichiers sont fermés | ||
- | * les enfants du procès sont "nés" par "init" | ||
- | * Un signal 'SIGCHLD' sera envoyé au processus parent. Il sera également rétabli à «statut» à la suite d'une fonction «wait» ou «waitpid». | ||
- | |||
- | De plus, ''exit(3)'': | ||
- | * supprimera tous les fichiers créés avec ''tmpfile ()'' | ||
- | * écrira les tampons ouverts et les fermera | ||
- | </spoiler> | ||
- | <note> Selon ''ISO C'', un programme se terminant par '' return x '' dans ''main()'' aura le même comportement que ''exit (x) ''. </note> | ||
- | |||
- | 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 [[http://linux.die.net/man/2/kill|kill]]. Plus de détails sur 'tuer' et les signaux dans [[so:laboratoare-2013:laborator-06 | signal lab]]. | ||
- | ==== Exemple (mon_système) ==== | ||
- | <code c my_system.c> | ||
- | #include <stdlib.h> | ||
- | #include <stdio.h> | ||
- | |||
- | #include <sys/types.h> | ||
- | #include <sys/wait.h> | ||
- | #include <unistd.h> | ||
- | |||
- | int my_system(const char *command) | ||
- | { | ||
- | pid_t pid; | ||
- | int status; | ||
- | const char *argv[] = {command, NULL}; | ||
- | |||
- | pid = fork(); | ||
- | switch (pid) { | ||
- | case -1: | ||
- | /* error forking */ | ||
- | return EXIT_FAILURE; | ||
- | case 0: | ||
- | /* child process */ | ||
- | execvp(command, (char *const *) argv); | ||
- | |||
- | /* only if exec failed */ | ||
- | exit(EXIT_FAILURE); | ||
- | default: | ||
- | /* parent process */ | ||
- | break; | ||
- | } | ||
- | |||
- | /* only parent process gets here */ | ||
- | waitpid(pid, &status, 0); | ||
- | if (WIFEXITED(status)) | ||
- | printf("Child %d terminated normally, with code %d\n", | ||
- | pid, WEXITSTATUS(status)); | ||
- | |||
- | return status; | ||
- | } | ||
- | |||
- | int main(void) { | ||
- | my_system("ls"); | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | ==== Copier les descripteurs de fichier ==== | ||
- | [[http://linux.die.net/man/2/dup|dup]] duplique le descripteur de fichier '' oldfd '' et renvoie le nouveau descripteur de fichier, ou '' -1 '' en cas d'erreur: | ||
- | <code c> | ||
- | int dup(int oldfd); | ||
- | </code> | ||
- | [[http://linux.die.net/man/2/dup|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: | ||
- | <code c> | ||
- | int dup2(int oldfd, int newfd); | ||
- | </code> | ||
- | |||
- | Les descripteurs de fichiers sont en réalité indexés dans la table de fichiers ouverte. La table est remplie avec les structures d'informations de fichier. 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é). | ||
- | |||
- | <note important> L'indicateur '' CLOSE_ON_EXEC '' n'est pas partagé (cet indicateur n'est pas conservé dans la structure ci-dessus). </note> | ||
- | |||
- | === Héritage des descripteurs de fichier après les opérations fork / exec === | ||
- | |||
- | Les descripteurs de fichier du processus parent ** héritent ** du processus enfant après l'appel 'fork'. Après un appel à "exec", les descripteurs de fichier sont conservés, à l'exception de ceux pour lesquels l'indicateur "CLOSE_ON_EXEC " est activé. | ||
- | <spoiler Détail du drapeau CLOSE_ON_EXEC> | ||
- | **fcntl** | ||
- | Pour définir l'indicateur '' CLOSE_ON_EXEC '', la fonction [[http://linux.die.net/man/3/fcntl|fcntl]] est utilisée, avec un appel de formulaire: | ||
- | <code c> | ||
- | fcntl(file_descriptor, F_SETFD, FD_CLOEXEC); | ||
- | </code> | ||
- | |||
- | **O_CLOEXEC** | ||
- | '' fcntl '' peut uniquement activer l'indicateur '' FD_CLOEXEC '' pour les descripteurs de fichiers existants. Dans les applications multithreads, entre la création d'un descripteur de fichier et un appel "fcntl", un appel "exec" peut être placé sur un autre thread. | ||
- | <code C> | ||
- | / * THREAD 1 */ |/ * THREAD 2 */ | ||
- | fd = op_creare_fd() | | ||
- | | exec(...) | ||
- | fcntl(fd, F_SETFD, FD_CLOEXEC); | | ||
- | </code> | ||
- | |||
- | Comment, par défaut, les descripteurs de fichier sont hérités après un appel "exec", bien que le programmeur ait voulu être incapable d'y accéder après "exec", ne peut empêcher un appel "exec" entre créer et fcntl. | ||
- | |||
- | Pour résoudre cette situation critique, de nouvelles versions d'appels système ont été introduites dans Linux 2.6.27 (2008): | ||
- | <code C> | ||
- | int dup3(int oldfd, int newfd, int flags); | ||
- | int pipe2(int pipefd[2], int flags); | ||
- | int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); | ||
- | </code> | ||
- | Ces variations d'appels système ajoutent le champ '' flags '', qui peut spécifier '' O_CLOEXEC '' pour créer et activer '' CLOSE_ON_EXEC '' en // mode atomique //. Le numéro du nom de l'appel système spécifie le nombre de paramètres d'appel. | ||
- | |||
- | Les appels système qui créent des descripteurs de fichier ayant déjà reçu un paramètre 'flags' (par exemple, '' open '') ont uniquement été étendus pour accepter ''O_CLOEXEC''. | ||
- | </spoiler> | ||
- | Variables d'environnement ===== sous Linux ===== | ||
- | |||
- | Dans un programme, il est possible d'accéder aux variables d'environnement en mettant en surbrillance le troisième paramètre (facultatif) de la fonction ''main'', comme dans l'exemple suivant: | ||
- | <code c> | ||
- | int main(int argc, char **argv, char **environ) | ||
- | </code> | ||
- | |||
- | Il désigne un vecteur de pointeurs sur des chaînes contenant les variables d'environnement et leurs valeurs. Les caractères sont sous la forme ''VARIABLE = VALUE''. Le vecteur est fini avec '' NULL ''. | ||
- | |||
- | [[http://linux.die.net/man/3/getenv|getenv]] renvoie la valeur de la variable d'environnement nommée ''nom'' ou ''NULL'' s'il n'y a pas de variable d'environnement nommée: | ||
- | <code c> | ||
- | char* getenv(const char *name); | ||
- | </code> | ||
- | |||
- | [[http://linux.die.net/man/3/setenv|setenv]] ajoute la variable nommée '' nom '' (si elle n'existe pas déjà) et définit sa valeur sur '' valeur ''. Si la variable existe et '' remplace '' e '' 0 '', l'action de définition de la valeur de la variable est ignorée; si '' remplacer '' est différent de '' 0 '', la valeur de la variable devient '' valeur '': | ||
- | <code c> | ||
- | int setenv(const char *name, const char *value, int replace); | ||
- | </code> | ||
- | |||
- | [[http://linux.die.net/man/3/unsetenv|unsetenv]] supprime la variable nommée ''name'' de l'environnement: | ||
- | <code c> | ||
- | int unsetenv(const char *name); | ||
- | </code> | ||
- | |||
- | ===== Dépannage d'un processus ===== | ||
- | |||
- | Des informations supplémentaires sur le dépannage d'un processus peuvent être trouvées | ||
- | [[so:laboratoare:resurse:gdb#depanarea unui proces | ici]] | ||
- | ===== Exercices ===== | ||
- | |||
- | Pour résoudre le labo, veuillez cloner [[https://www.github.com/upb-fils/sde|repository]]. si vous en avez déjà un, lancez svp '' git pull ''. | ||
- | |||
- | <note tip> Pour vous aider à mettre en œuvre les exercices de laboratoire, il existe un fichier '' utils.h '' avec des fonctionnalités utiles dans le répertoire '' utils '' de l'archive. </ note> | ||
- | |||
- | |||
- | ==== Exercice 1 - Système (1p) ==== | ||
- | |||
- | Allez dans le répertoire '' 1-system ''. | ||
- | Le programme '' mon_système.c '' exécute une commande envoyée en tant que paramètre à l'aide de la fonction de bibliothèque [[http://linux.die.net/man/3/system|system]]. Le fonctionnement de [[http://linux.die.net/man/3/system|system]] est le suivant: | ||
- | * créer un nouveau processus avec [[http://linux.die.net/man/2/fork|fork]] | ||
- | * Le processus enfant s'exécute en utilisant [[http://linux.die.net/man/2/execve | execve]], le programme ** ** 'sh' ** avec les arguments -c 'commande', pendant que le processus parent attend la fin de l'enfant. | ||
- | |||
- | Compilez (en utilisant ''make'') et lancez le programme en donnant une commande en paramètre. | ||
- | * Exemple: <code bash> ./my_system pwd </code> | ||
- | Que faites-vous pour envoyer plus de paramètres à une commande? (par exemple: '' ls -la '') | ||
- | |||
- | Pour voir combien d'appels système [[http://linux.die.net/man/2/execve | execve]] est terminé, exécutez: | ||
- | <code bash> strace -e execve,clone -ff -o output ./my_system ls </code> | ||
- | * ** Attention! ** Il n'y a pas de virgule dans l'argument '' execve, clone '' | ||
- | * L'argument '' -ff '' accompagné de '' -o output '' génère un fichier de sortie pour chaque processus. | ||
- | * Voir la page de manuel [[http://linux.die.net/man/1/strace|strace]] | ||
- | Consultez la section [[# # Remplacement d’une image de processus sous Linux | Remplacement d’une image de processus sous Linux]] et du [[http://linux.die.net/man/2/execve | execve]]. | ||
- | |||
- | Remplacez le '' système '' par '' execlp ''. La fonction reçoit une commande, une liste d'arguments (séparés par des virgules), puis NULL (le dernier paramètre correspond à la fin de la liste). | ||
- | |||
- | Suivez la ligne avec TODO 1. | ||
- | |||
- | ==== Exercice 2 - Paramètres (2p) ==== | ||
- | |||
- | Allez dans le répertoire '' 2-parameters ''. | ||
- | |||
- | Compilez le programme //parameters.c// en utilisant la commande 'make'. Que fait //parameters.c//? | ||
- | |||
- | Résolvez l'exercice dans le fichier //program.c//. | ||
- | |||
- | === 2a. système (1p) === | ||
- | |||
- | Utilisez la fonction '' système '' pour exécuter // paramètres // avec certains paramètres. | ||
- | |||
- | <note> Pour exécuter un programme à partir du répertoire en cours, préfixez l'exécutable avec ./ (ex: ./parameters) </ note> | ||
- | |||
- | === 2b. execl (1p) === | ||
- | |||
- | Remplacez la fonction // system // par // execl //. Pourquoi le texte affiché par // printf // après // execl //? | ||
- | |||
- | Suivez les lignes avec TODO 2. | ||
- | |||
- | ==== Exercice 3 - Exécuter (4p) ==== | ||
- | |||
- | Allez dans le répertoire '' 3-run ''. | ||
- | |||
- | === 3a - fork, exec (1p) === | ||
- | |||
- | Utilisez les fonctions // fork // et // execl // pour exécuter la commande ''ls'' dans //run.c//. | ||
- | |||
- | Suivez les lignes avec TODO 1. | ||
- | |||
- | === 3b - waitpid (1p) === | ||
- | |||
- | Assurez-vous que // // "ls a été exécuté" // apparaît après la fermeture du programme // ls //. (Indice: waitpid) | ||
- | |||
- | Suivez les lignes avec TODO 2. | ||
- | |||
- | === 3c - statut de sortie (1p) === | ||
- | Déplacez et modifiez la ligne avec TODO 3 afin que le code de sortie de // ls // soit affiché. (Indice: WEXITSTATUS) | ||
- | |||
- | |||
- | === 3d - waitpid (1p) === | ||
- | Compilez le programme //exitcode.c// en utilisant la commande 'make'. Exécutez le programme et affichez le code de sortie. Modifiez le programme //exitcode.c// afin qu'il renvoie un autre code de sortie. | ||
- | |||
- | Suivez les lignes avec TODO 4. | ||
- | |||
- | ==== Exercice 4 - orphelin (0.5p) ==== | ||
- | |||
- | Allez dans le répertoire '' 4-orphan '' et inspectez la source '' orphan.c ''. | ||
- | |||
- | Compilez le programme ('' make '') puis exécutez-le en utilisant la commande suivante: | ||
- | <code bash> ./orphan </code> | ||
- | Ouvrez un autre terminal et lancez la commande: | ||
- | <code bash> watch -d '(ps -al | grep -e orphan -e PID)' </code> | ||
- | 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 deux processus orphelins? | ||
- | |||
- | ==== Exercice 5 - zombie (0.5p) ==== | ||
- | |||
- | Allez dans le répertoire '' 5-zombie '' et inspectez la source '' zombie.c ''. | ||
- | |||
- | Compilez le programme ('' make '') puis exécutez-le en utilisant la commande suivante: | ||
- | <code bash> ./zombie </code> | ||
- | |||
- | Ouvrez un autre terminal et lancez la commande: | ||
- | <code bash> watch -d '(ps -al | grep -e orphan -e PID)' </code> | ||
- | |||
- | 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.c// afin que le processus ne devienne pas un zombie (indice: waitpid). | ||
- | |||
- | Suivez les lignes avec TODO 1. | ||
- | |||
- | ==== Exercice 6 - Tiny-Shell (3p) ==== | ||
- | |||
- | Allez dans le répertoire '' 3-minuscule ''. | ||
- | |||
- | Les sous-programmes suivants sont destinés à implémenter un shell minimal prenant en charge l'exécution d'une commande // single // external avec plusieurs arguments et redirections. Shell doit fournir un soutien pour l'utilisation et la définition de variables environnementales. | ||
- | |||
- | // Note: // Pour quitter le petit shell, utilisez '' exit '' ou '' CTRL + D ''. | ||
- | |||
- | === 6a. Exécution d'une commande simple (1p) === | ||
- | |||
- | Créez un nouveau processus pour exécuter une commande simple. | ||
- | |||
- | La fonction '' simple_cmd '' reçoit en argument un vecteur chaîne contenant la commande et ses paramètres. | ||
- | |||
- | Voir l'exemple [[#example (my_system) | mon_système]] et suivez les commentaires avec '' TODO 1 '' dans le code. | ||
- | Pour tester, vous pouvez utiliser les commandes: <code bash> ./tiny | ||
- | > pwd | ||
- | > ls -al | ||
- | > exit </code> | ||
- | === 6b. Ajout du support pour la définition et le développement de variables d’environnement (1p) === | ||
- | |||
- | Vous devez remplir les fonctions 'set_var' et 'expand'; ils sont déjà appelés lorsque la ligne de commande est analysée. Les erreurs doivent être vérifiées dans ces fonctions. | ||
- | |||
- | * Suivre les commentaires avec '' TODO 2 '' dans le code. | ||
- | * Lisez la variable d'environnement [[#variabile de mediu in linux | Variables d'environnement Linux]]. | ||
- | * Pour tester, vous pouvez utiliser les commandes suivantes: <code bash> ./tiny | ||
- | > echo $HOME | ||
- | > name=Makefile | ||
- | > echo $name </code> | ||
- | === 6c. Rediriger la sortie standard (1p) === | ||
- | |||
- | 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 '' nomfichier '' n'existe pas, il sera créé. S'il existe, il doit être tronqué. | ||
- | |||
- | Lisez la section [[# Descripteurs de fichiers de copie | Copie de descripteurs de fichiers]] et suivez les commentaires avec le mot "TODO 3" dans le code. | ||
- | Pour tester, vous pouvez utiliser les commandes: | ||
- | <code bash> ./tiny | ||
- | > ls -al > out | ||
- | > cat out | ||
- | > pwd > out | ||
- | > cat out | ||
- | </code> | ||
- | |||
- | <hidden> | ||
- | ===== Soluții ===== | ||
- | |||
- | [[http://elf.cs.pub.ro/so/res/laboratoare/lab03-sol.zip | lab03-sol.zip]] | ||
- | </hidden> | ||
- | ===== Ressources utiles ===== | ||
- | - [[http://en.wikipedia.org/wiki/Fork_(operating_system)|Fork - Wikipedia]] | ||
- | - [[http://www-h.eng.cam.ac.uk/help/tpl/unix/fork.html|About Fork and Exec]] | ||
- | - [[http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html|Fork, Exec and Process Control - YoLinux Tutorial]] | ||
- | |||