Pour exécuter un programme on peut
Le programme s'exécute… oui mais comment ?
Proposé par John Von Neumann en 1945
Un processeur
De la mémoire
Des périphériques d'entrée/sortie
Un processeur est un circuit programmable. Il sait exécuter un certain nombre d'opérations (arithmétiques, logiques, lecture/écriture en mémoire, …).
L'ensemble de ces instructions s'appelle le jeu d'instruction
Chaque instruction possède une représentation en binaire (suite de 0 et de 1)
Par exemple, sur les processeur Intel, 01101001 représente la multiplication
Un fichier exécutable est un fichier contenant des
instructions machines (comme 01101001).
Lorsqu'on l'exécute :
Le modèle de Von Neumann seul ne permet pas d'exécuter « plusieurs programmes à la fois »
Les processeurs modernes possèdent aussi des alarmes configurables
Le système d'exploitation configure une alarme pour interrompre le processeur toutes les X㎲
Lors de cette interruption une portion de code spéciale est exécutée : le gestionnaire de processus
C'est le ordonanceur de processus qui décide quel
programme a la main et pour combien de temps (priorité aux
tâches critiques par exemple)
Le système d'exploitation stocke pour chaque processus un
ensemble d'informations, le PCB (Process Control Block).
Le PCB contient:
Un processus change d'état au cours de son exécution
Nouveau | le processus est en cours de création |
Exécution | le processus s'exécute |
En attente | le processus attend un évènement particulier (saisie au clavier, écriture sur le disque, …) |
Prêt | le processus est prêt à reprendre son exécution et attend que l'OS lui rende la main |
terminé | le processus a fini son exécution |
L'OS détermine et modifie l'état d'un processus:
Permet d'avoir des informations sur les processus en cours
d'exécution (voir « man ps » pour les
options):
$ ps -o user,pid,state,cmd x
USER PID S CMD
…
kim 27030 Z [chrome] <defunct>
kim 27072 S /opt/google/chrome/chrome --type=renderer
kim 29146 S bash
kim 29834 S evince
kim 29858 S emacs cours.xhtml
kim 29869 R ps -o user,pid,state,cmd x
R | Running (en cours d'exécution) |
S | Interruptible sleep (en attente, interruptible) |
D | Uninterruptible sleep (en attente, non-interruptible) |
T | Stopped (interrompu) |
Z | Zombie (terminé mais toujours listé par le système) |
L'OS peut envoyer des signaux à un processus. Sur réception d'un signal, un processus peut interrompre son comportement normal et exécuter son gestionnaire de signal. Quelques signaux:
Nom | Code | Description |
---|---|---|
INT/TERM | 2,15 | demande au processus de se terminer |
QUIT | 3 | interrompt le processus et produit un dump |
KILL | 9 | interrompt le processus immédiatement |
SEGV | 11 | signale au processus une erreur mémoire |
STOP | 24 | suspend l'exécution du processus |
CONT | 28 | reprend l'exécution d'un processus suspendu |
Un processus est lié au terminal dans lequel il est
lancé. Si on exécute un programme dans un terminal et que le
processus ne rend pas la main, le terminal est bloqué
$ gedit
On peut envoyer au processus le signal STOP en
tapant ctrl-Z
dans le terminal:
$ gedit
^Z
[1]+ Stopped gedit
Le processus est suspendu, la fenêtre est gelée (ne répond plus).
On peut reprendre l'exécution du programme de deux
manières:
$ fg
Reprend l'exécution du processus et le remet en avant plan (terminal
bloqué)
$ bg
Reprend l'exécution du processus et le remet en arrière plan (terminal
libre)
On peut lancer un programme
directement en arrière plan en faisant:
$ gedit &
On peut envoyer un signal à un
processus avec la commande « kill [-signal] pid »
$ kill -9 2345
Le terminal et le processus sont liés par trois fichiers spéciaux:
Dans le shell, on peut utiliser les
opérateurs <, >
et 2> pour récupérer le contenu
de stdin, stdout
et stderr:
$ sort < toto.txt
$ ls -l > liste_fichiers.txt
$ ls -l * 2> erreurs.txt
Dans le shell, l'opérateur | permet
d'enchaîner la sortie d'un programme avec l'entrée d'un
autre:
$ ls -l *.txt | sort -n -r -k 5 | head -n 1
-rw-rw-r 1 kim kim 471 Sep 14 16:25 bd.txt
-rw-rw-r 1 kim kim 234 Sep 15 17:46 foo.txt
-rw-rw-r 1 kim kim 1048576 Sep 24 09:20 large.txt
-rw-rw-r 1 kim kim 1048576 Sep 24 09:20 large.txt
-rw-rw-r 1 kim kim 471 Sep 14 16:25 bd.txt
-rw-rw-r 1 kim kim 234 Sep 15 17:46 foo.txt
-rw-rw-r 1 kim kim 1048576 Sep 24 09:20 large.txt
Quelques exemples de commandes problématiques :
$ sort fichier.txt > fichier.txt
fichier.txt devient vide !
Il est ouvert en écriture et tronqué avant l'exécution de la commande.
$ sort < fichier.txt > fichier.txt
fichier.txt devient vide !
Il est ouvert en écriture et tronqué avant l'exécution de la commande.
$ sort < fichier.txt >> fichier.txt
fichier.txt contient son contenu original, suivi de son contenu trié !
$ cat < fichier.txt >> fichier.txt
fichier.txt est rempli jusqu'à
saturation de l'espace disque !
La commande sort doit trier son entrée
standard. Elle doit donc la lire intégralement avant de
produire la moindre sortie. Pour
$ sort < fichier.txt >> fichier.txt
on a donc :
La commande cat ré-affiche son entrée
standard sur sa sortie standard. Elle peut donc lire le
fichier morceaux par morceaux et les afficher au fur et à
mesure. Supposons que fichier.txt
contient AB :
$ cat < fichier.txt >> fichier.txt
On évitera toujours de manipuler le même fichier en entrée et en sortie. Il vaut mieux rediriger vers un fichier temporaire, puis renommer ce dernier (avec la commande mv).
Sous Unix, chaque commande renvoie un code de
sortie (un entier entre 0 et 255).
Par convention, un code de 0 signifie terminaison normale, un code différent de 0 une erreur. On peut enchaîner des commandes de plusieurs façons :
La commande test permet de tester des conditions sur les fichiers passés en arguments.
Si le test est vrai, la code de sortie est 0, sinon c'est 1
Les options permettent de spécifier les tests.
L'option -f permet de tester si un fichier
existe :
$ test -f toto.txt
La commande sort avec le code de sortie 0 si le fichier
toto.txt existe et 1 sinon.
Comment s'en servir ?
$ test -f toto.txt && echo "Ok" || echo "Pas ok"
La commande affiche "Ok" si le fichier existe et "Pas ok" sinon.
On a vu qu'un fichier exécutable est censé contenir des instructions machines.
Un humain ne peut pas écrire directement des instructions machines en binaire.
Est-il possible d'écrire des programmes en shell ou en Python et d'en faire des fichiers exécutables ?
Oui !
On procède de la façon suivante
#!/bin/bash
Cette ligne commence par #! est suivie du
chemin vers le programme qui interprète le langage
(bash, python3, …)
$ chmod 755 test.sh
$ ./test.sh
Lorsque l'on essaye d'exécuter une commande :
#!/chemin/vers/un/programme
...
texte
...
Le texte du fichier est copié sur l'entrée standard du
programme dont le chemin est donné (« comme si un
utilisateur avait saisi les lignes au clavier »)On peut utiliser cette fonctionalité pour écrire des scripts shell.
On peut définir des variables au moyen de la notation
VARIABLE=contenu
et on peut utiliser la variable
avec la notation $VARIABLE
i=123
j="Ma super chaine"
TOTO=titi
echo $TOTO
exemple d'utilisation: echo $j $i $TOTO
affiche « Ma super chaine 123 titi »
Les variables $1, $2, … contiennent les arguments passés au script sur la ligne de commande.
La variable $0 contient le chemin vers le script en cours d'exécution
Attention, il est recommandé de toujours encadrer l'utilisation d'une variable par des guillemets "
La syntaxe est :
if commande
then
...
else
...
fi
commande est évaluée. Si elle se termine avec succès,
la branche then est prise. Si elle se termine avec un code
d'erreur, la branche else est prise. On peut utiliser la
commande test qui permet de tester plusieurs conditions
(existance d'un fichier, égalités de deux nombres, ...) et se termine
par un succès si le teste est vrai et par un code d'erreur dans le
cas contraire
On est dans le fichier backup.sh :
#!/bin/bash
INPUT="$1"
if test -f "$INPUT"
then
cp "$INPUT" "$INPUT".bak
echo "Sauvegarde du fichier $INPUT réussie"
else
echo "Erreur, le fichier $INPUT n'existe pas"
fi
On suppose que le script possède les bonnes permissions, qu'il se trouve dans le répertoire courant, et qu'un unique fichier doc.txt se trouve dans ce même répertoire
$ ./backup.sh doc.txt
Sauvegarde du fichier doc.txt réussie
$ ls
backup.sh doc.txt doc.txt.bak
$ ./backup.sh toto.txt
Erreur, le fichier toto.txt n'existe pas