===============================================================
INFO 1 - Systèmes d’Exploitations - TP2 - Gestion des Processus
===============================================================


Dans le Shell
-------------

Nous allons tester l’apparition de processus, et leur manipulation via un
programme graphique : `xeyes`.

- Lancer `xeyes` depuis un terminal, et déplacer la souri. Le terminal est-il
  toujours utilisable ?
- Faire ^C dans le terminal, que s’est-il passé ? Quel est l’état du terminal ?
- Relancer `xeyes` depuis un terminal, faire ^Z cette fois-ci. Quel est le
  comportement du terminal ? Celui de `xeyes` ?
- Sans toucher le terminal utilisé précédemment, ouvrir un nouveau terminal, et
  lister les processus actifs sur la machine via `ps aux`, quel est l’état du
  processus `xeyes` ?
- Dans le terminal d’où la commande `xeyes` a été lancée, saisir `bg`.
  Qu’observe-t-on sur le terminal et sur `xeyes` ?
- Dans l’autre terminal, relancer `ps aux` pour voir l’état d’`xeyes`.
- Lister plusieurs manières d’arrêter `xeyes`, les tester (en refaisant la
  même manip’ avec `xeyes` entre chaque teste évidemment). Vérifier
  régulièrement l’état d’`xeyes` dans un autre terminal.

Le contrôle de processus peut s’effectuer dans n’importe quel terminal via les
signaux.

- La commande `kill -l` liste les signaux disponibles sur le système. Saisir
  cette commande dans un terminal, combien de signaux sont listés ?
- Lancer `xeyes`, et lui envoyer `SIGSTOP` via un autre terminal. Observer.
- À l’aide d’un manuel, envoyer un signal pour que le processus reprenne son
  exécution.
- Finalement, envoyer le signal 9 à `xeyes`… qu’avez-vous fait ?


Appels système
--------------

```c
#include <unistd.h>

pid_t getpid(void);
pid_t getppid(void);
pid_t fork(void);

int execl(const char *path, const char *argv, ...);
int execv(const char *path, const char *argv[]);
int execle(const char *path, const char *argv, const char *envp[]);
int execlp(const char *file, const char *argv, ...);
int execvp(const char *file, const char *argv[]);

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *pointeur_status);
```
Dans la famille des `exec`,
- `l` vaut pour *list* : on donne la liste des arguments séparés par des
  virgules, la fonction est d’arité variable,
- `v` vaut pour *vector*, on donne un pointeur vers la liste des arguments,
- `e` vaut pour *environment*,
- `p` vaut pour `PATH`, l’appel cherchera dans le `PATH` l’exécutable si ce
  n’est pas un nom donné par chemin absolu.

Exemple :

```c
char *arguments[4];
arguments[0] = "/bin/ls";
arguments[1] = "-l";
arguments[2] = "/etc";
arguments[3] = "NULL";
execv("/bin/ls", arguments);
```

1. Écrire un programme qui engendre *n* processus, *n* étant donné par
   l’utilisateur en ligne de commande.

2. Écrire un programme qui crée deux enfants, l’un affiche les entiers de 1 à
   50, l’autre de 51 à 100. Modifier le programme précédent pour que l’affichage
   soit dans l’ordre.

3. Écrire un programme qui engendre trois enfants. L’un rentrera dans l’état
   zombie puis deviendra orphelin. Un deuxième deviendra orphelin avant de
   devenir zombie. Le troisième deviendra zombie et il ne sera jamais orphelin.

4. (Chasse aux zombies) L’appel `waitpid(-1, NULL, WNOHANG)` renvoie −1 en cas
   d’erreur, s’il n’y a pas d’enfant zombie il renvoie immédiatement 0 (appel
   non-bloquant) et sinon un enfant zombie est choisi, toutes ses ressources
   sont libérées et son PID est renvoié. En déduire (et expérimenter) une
   manière pour un processus de se débarasser de tous les zombies qu’il a créé.

5. Compléter le programme suivant en respectant les commentaires comme
   spécifications :

```c
#include <unistd.h>
#include <stdlib.h>
#include<stdio.h>
#include <sys/wait.h>

void jouer(int NumJoueur);

#define MaxJoueurs 4

int main()
{
	/*
	 * Créer MaxJoueurs processus enfant du processus principal
	 * Chaque jouer exécute la fonction `jouer` puis se termine
	 */
	while (1) {
		/*
		 * Attendre la fin d’un joueur
		 * lorsqu’un joueur se termine, un autre joueur de même numéro est créé
		 */
	}
	return 0;
}
```

6. Écrire un programme qui lance, l’un à la suite de l’autre, les fichiers
   exécutables passés en paramètre.
   Un fichier exécutable est lancé uniquement si tous ceux qui le précèdent sont
   terminés. Avant de se terminer, le programme affiche, dans le terminal, le
   nombre de lancements de fichiers exécutables qui ont échoué (parce que le
   fichier n’existe pas ou n’est pas exécutable).

7. Écrire un programme `multiexec` tel que `$ ./multiexec n cmd arg1 … argk`
   lance *n* copies (par `fork`) de la commande `cmd arg1 … argk`.
   Faire en sorte que le programme s’arreêt et donne un message d’erreur lorsque
   le nombre d’arguments n’est pas suffisant.
   Faire en sort que le statut de sortie de la commande soit celui de la
   dernière commande à avoir terminé si elle s’est terminée normalement ou bien
   128 + SIGNUM si celle-ci a été tuée par un signal de numéro SIGNUM (voir `man
   2 wait`).

8. Écrire un shell minimal, capable d’exécuter des commandes simples sans
   argument entrées par l’utilisateur.
   On pourra utiliser la fonction `fgets()` pour lire une ligne d’entrée
   standard et remplacer le caractère `\n` par `\0` puis utiliser `execlp()`.
   Ajouter les fonctionnalités suivantes à votre `minishell` :
   - Gérer les arguments des commandes (disons, séparés par des espaces). En
     utilisant par exemple `strtok()` de `<string.h>` et `execvp()`
   - Faire en sorte que si le dernier caractère de la ligne est `&`, la commande
     est lancée en arrière-plan : le parent n’attend pas qu’elle se termine
     avant de demander une nouvelle commande à l’utilisateur.
   - Ajouter les commandes internes (c’est-à-dire directement prises en charge
     par votre shell) `pwd`, et `cd` pour afficher le répertoire courant et
     changer de répertoire courant (voir `man 3 getwd` et `man 2 chdir`).
   - Ajouter autant de fonctionnalités que vou voulez, par exemple la gestion
     des variables (`var=val`), leur utilisation (`echo $var`), l’utilisation
     d’autres commandes internes (`echo`, …) à la place des commande externes…
   - Ajouter la gestion des redirections, notamment du `|`.
