cours_sys
Short Description
Download cours_sys...
Description
Cours des systèmes d’exploitation
Filière SMI(S4)
INTRODUCTION Un système informatique est constitué du matériel est du logiciel. Son objectif est de permettre le traitement automatique de l’information.
Système informatique Du matériel
Du logiciel Les programmes système
Outils de développement et d’exécution de programmes
compilateurs
Éditeurs
Les programmes d’application
Système d’exploitation
Interpréteur
Les programmes d’application accèdent au matériel à travers différentes couches logicielles. Le système d’exploitation constitue la couche intermédiaire entre le matériel et les programmes d’application.
Le programme « système d'exploitation » est le programme fondamental des programmes systèmes. Il contrôle l’exécution des programmes d’application et agit comme une interface entre l’utilisateur et le matériel. Il vise essentiellement deux objectifs: 1 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Ergonomie: il facilite l’utilisation d’un ordinateur Efficacité: il permet d’utiliser efficacement les ressources d’un ordinateur
Rôles du système d'exploitation Les rôles du système d'exploitation sont divers : Gestion du processeur : le système d'exploitation est chargé de gérer l'allocation du processeur entre les différents programmes grâce à un algorithme d'ordonnancement. Le type d'ordonnanceur est totalement dépendant du système d'exploitation, en fonction de l'objectif visé. Gestion de la mémoire : le système d'exploitation est chargé de gérer l'espace mémoire alloué à chaque application et, le cas échéant, à chaque usager. En cas d'insuffisance de mémoire physique, le système d'exploitation peut créer une zone mémoire sur le disque dur, appelée «mémoire virtuelle». La mémoire virtuelle permet de faire fonctionner des applications nécessitant plus de mémoire qu'il n'y a de mémoire vive disponible sur le système. En contrepartie cette mémoire est beaucoup plus lente. Gestion des entrées/sorties : le système d'exploitation permet d'unifier et de contrôler l'accès des programmes aux ressources matérielles par l'intermédiaire des pilotes (appelés également gestionnaires de périphériques ou gestionnaires d'entrée/sortie). Gestion de l'exécution des applications : le système d'exploitation est chargé de la bonne exécution des applications en leur affectant les ressources nécessaires à leur bon fonctionnement. Il permet à ce titre de «tuer» une application ne répondant plus correctement. Gestion des droits : le système d'exploitation est chargé de la sécurité liée à l'exécution des programmes en garantissant que les ressources ne sont utilisées que par les programmes et utilisateurs possédant les droits adéquats. Gestion des fichiers : le système d'exploitation gère la lecture et l'écriture dans le système de fichiers et les droits d'accès aux fichiers par les utilisateurs et les applications.
Composants du système d'exploitation Le système d'exploitation est composé d'un ensemble de logiciels permettant de gérer les interactions avec le matériel. Parmi cet ensemble de logiciels on distingue généralement les éléments suivants : Le noyau (en anglais kernel) représentant les fonctions fondamentales du système d'exploitation telles que la gestion de la mémoire, des processus, des fichiers, des entrées-sorties principales, et des fonctionnalités de communication. L'interpréteur de commande (en anglais shell, traduisez «coquille» par opposition au noyau) permettant la communication avec le système d'exploitation par l'intermédiaire d'un langage de commandes, afin de permettre à l'utilisateur de piloter les périphériques en ignorant tout des caractéristiques du matériel qu'il utilise, de la gestion des adresses physiques, etc. . II. Les différentes classes de systèmes d'exploitation Les systèmes d’exploitation peuvent être classés en plusieurs classes. Ces classes diffèrent les unes des autres suivant les points suivants : Selon les service rendus mono/multi-taches un système multi-tâches peut exécuter plusieurs processus simultanément. C’est le cas d’UNIX, Windows XP,… mono/multi-utilisateurs un système Multi-utilisateurs peut gérer plusieurs utilisateurs utilisant simultanément les mêmes ressources matérielles. C'est le cas d'UNIX, windows XP
Selon leur architecture • Systèmes centralisés o L'ensemble du système est entièrement présent sur la machine considérée. o Le système ne gère que les ressources de la machine sur laquelle il est présent. 2 Aicha KERFALI Année universitaire 2013/2014
Cours des systèmes d’exploitation •
Filière SMI(S4)
Systèmes répartis (distributed systems) o Les différentes abstractions du système sont réparties sur un ensemble de machines. o Le système d'exploitation réparti apparaît aux yeux de ses utilisateurs comme une machine virtuelle monoprocesseur même lorsque cela n'est pas le cas. o Avec un système réparti, l'utilisateur n'a pas à se soucier de la localisation des ressources. Quand il lance un programme, il n'a pas à connaître le nom de la machine qui l'exécutera.
Selon leur capacité à évoluer • Systèmes fermés o Extensibilité réduite : Quand on veut rajouter des fonctionnalités à un système fermé, il faut remettre en cause sa conception. o Il n'y a aucun ou peu d'échange possible avec d'autres systèmes de type différent, voir même avec des types identiques. • Systèmes ouverts o Extensibilité accrue : Il est possible de rajouter des fonctionnalités sans avoir à repenser le système et même sans avoir à l'arrêter sur une machine. Selon l'architecture matérielle qui les supporte • système monoprocesseur (temps partagé ou multiprogrammation) : o Ressource processeur unique : Il a fallu développer un mécanisme de gestion des processus pour offrir un (pseudo) parallélisme à l’utilisateur. • Systèmes multiprocesseurs o Le multiprocessing est une technique consistant à faire fonctionner plusieurs processeurs en parallèle afin d'obtenir une puissance de calcul plus importante que celle obtenue avec un processeur. o On appelle SMP (Symmetric Multiprocessing ou Symmetric Multiprocessor) une architecture dans laquelle tous les processeurs accèdent à un espace mémoire partagé. o Architecture à mémoire distribue : Ce sont des architectures où chaque processeur possède sa propre mémoire locale ; o Architecture mixte Ce sont des architectures à différents niveaux de mémoire (commune et privée). o Un système multiprocesseur doit donc être capable de gérer le partage de la mémoire et les communications entre les différents processeurs et distribuer la charge de travail.
Cas particuliers Systèmes embarqués Les systèmes embarqués sont des systèmes d'exploitation prévus pour fonctionner sur des machines de petite taille, telles que des PDA (personal digital assistants ou en français assistants numériques personnels) ou des appareils électroniques autonomes (sondes spatiales, robot, ordinateur de bord de véhicule, etc.), possédant une autonomie réduite. Ainsi, une caractéristique essentielle des systèmes embarqués est leur gestion avancée de l'énergie et leur capacité à fonctionner avec des ressources limitées. Les principaux systèmes embarqués «grand public» pour assistants numériques personnels sont : PalmOS Windows CE / Windows Mobile / Window Smartphone Systèmes temps-réel Ce sont des systèmes pour lesquels l'exécution des programmes est soumise à des contraintes temporelles. Les résultats de l'exécution d'un programme n'est plus valide au delà d'un certain temps connu et déterminé à l'avance. Les systèmes à transaction : Gèrent des bases de données de grande taille. La mise à jour de la base est réalisée par des transactions
3 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Exemples de systèmes d'exploitation On distingue plusieurs types de systèmes d'exploitation, selon qu'ils sont capables de gérer simultanément des informations d'une longueur de 16 bits, 32 bits, 64 bits ou plus.
Système DOS Windows95/98/Me WindowsNT/2000 WindowsXP Windows7 Unix / Linux MAC/OS X VMS
Codage Mono-utilisateur Multi-utilisateur Mono-tâche Multitâche 16 bits X X 32 bits X X 32 bits X X 32/64 bits X X 32/64 bits X X 32/64 bits X X 32 bits X X 32 bits X X X
4 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Chapitre1 : Le système d’exploitation UNIX I-INTRODUCTION Le cours des systèmes d’exploitation va s’intéresser à l'étude des systèmes d'exploitation en général et plus précisément aux familles UNIX. Dans ce chapitre nous allons voir comment un utilisateur peut utiliser les services fournis par le système d'exploitation au niveau de ses applications. La mise en œuvre de ses services fait l'objet d'un autre chapitre. Pourquoi UNIX Pourquoi ce choix d’Unix comme sujet d'étude pour le cours ? LE PRIX La disponibilité des sources De grande ressource bibliographique Caractéristiques du système Écrit dans un langage de haut niveau : C; Une interface simple et puissante : les shells, qui fournissent des services de haut niveau ; Des primitives puissantes qui permettent de simplifier l'écriture des programmes ; Un système de fichier hiérarchique qui permet une maintenance simple et une implémentation efficace ; Un format générique pour les fichiers; le flot d’octets qui simplifie l'écriture des programmes ; Il cache complètement l’architecture des machines aux utilisateurs, Il fournit une interface simple aux périphériques . Il permet le parallélisme car il : ◦ est multitâches et multiutilisateurs ◦ est multiprocesseurs : sur les ordinateurs multiprocesseurs un programme peut être exécuté sur plusieurs processeurs. ◦ Permet la communication entre tâches : plusieurs moyen de communication entre les processus utilisateurs : pipe, signaux, socket …. L’architecture du système L’architecture globale d’UNIX est une architecture par couches (coquilles) successives comme le montre la figure 1.1. Les utilisateurs ordinaires communiquent avec la couche la plus évolué celle des applications (en générale aujourd’hui associé avec une interface graphique). Le programmeur lui va en fonction de ses besoins utiliser des couches de plus en plus profondes, plus précises mais plus difficiles à utiliser
shell Utilisateur
Utilisateur
awk
materiel
cat
ls noyau vi Figure 1.1
Chaque couche est construite pour pouvoir être utilisée sans connaître les couches inférieures (ni leur fonctionnement, ni leur interface). 5 Aicha KERFALI Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Cette hiérarchie d’encapsulation permet d’écrire des applications plus portables. En effet si elles sont écrites dans les couches hautes, le travaille de portage est fait par le portage des couches inférieures. Pour des applications où le temps de calcul prime devant la portabilité, les couches basses seront utilisées.
II- Le système Linux Linux est une version libre d’UNIX (le code source du système est disponible gratuitement et redistribuable) qui connait actuellement un grand succès, tant chez les utilisateurs particulier (en tant qu’alternative à Windows) que sur les serveurs Internet/Intranet. Linux est diffusé par différentes sociétés ou organisations, sous formes de distributions qui utilisent le même noyau (ou presque) et organisent de diverses façons le système (packages, mises à jour, etc).
2.1 Les distributions Linux les plus répandues www.redhat.fr société américaine fondée en 1994. célèbre pour avoir introduit le système Red hat Package Manager (RPM), de gestion de paquets logiciels
• • •
fedora.redhat.com version grand publique gratuite de RedHat utilise le système de gestion de paquets RPM
www.mandriva.com LA distribution française par excellence Très répandue dans le milieu éducatif utilise le système de gestion de paquets RPM
www.novell.com/linux/suse société allemande fondée à Nuremberg en 1993 utilise le système de gestion de paquets RPM
• • • • • • •
www.debian.org distribution issue d'un effort communautaire, le « projet Debian », et non d'une entreprise distribution très soignée et ingénieuse austère à installer et à administrer (déconseillée aux débutants ?) Utilise le système de gestion de paquets debian
• • •
www.ubuntu-fr.org Distribution populaire à base débian lancée en 2004. Son nom provient d'un ancien mot bantou (langue d'Afrique), ubuntu, signifiant « Humanité aux autres», ou encore « Je suis ce que je suis grâce à ce que nous sommes tous » 6
Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
2.3 Caractéristiques générales du noyau C’est un système Multitâches / multiutilisateurs. Le fonctionnement multitâche est assuré par un mécanisme préemptif : le système interrompt autoritairement la tâche en cours d’exécution pour passer la main à la suivante. Les tâches sont protégées ; certaines peuvent communiquer, c-à-d échanger ou partager des données, se synchroniser dans leur exécution ou sur le partage de ressources. Certaines tâches peuvent être «temps réel ». Linux comprend un noyau (kernel) et des utilitaires. Irremplaçable par l'utilisateur, le noyau assure la gestion de la mémoire, le partage du processeur entre les différentes tâches à exécuter et les entrées/sorties de bas niveau sans intervention des utilisateurs, il est lancé au démarrage du système (le boot) et s’exécute jusqu’à son arrêt. C’est un programme relativement petit, qui est chargé en mémoire principale. Il s’exécute en mode superviseur; c’est à dire qu’il a accès à toutes les fonctionnalités de la machine. Tous les autres programmes qui s’exécutent sur la machine fonctionnent en mode utilisateur : il leur est interdit d’accéder directement au matériel et d’utiliser certaines instructions. Chaque programme utilisateur n’a ainsi accès qu’à une certaine partie de la mémoire principale, et il lui est impossible de lire ou écrire les zones mémoires attribuées aux autres programmes. Lorsque l’un de ces programmes désire accéder à une ressource gérée par le noyau, par exemple pour effectuer une opération d’entrée/sortie, il exécute un appel système. Le noyau exécute alors la fonction correspondante, après avoir vérifié que le programme appelant est autorisé à la réaliser. 2.4 Interface au noyau Comme le montre la figure 1.2, l'interface entre le noyau Linux et les périphériques est assurée par les gestionnaires de périphériques (devices driver). L'interface entre le noyau et les programmes utilisateurs est assurée par un ensemble d'appels systèmes.
Figure 1.2
Les utilitaires les plus importants sont les suivants • Interpréteurs de commandes (nommés shells), permettant l’accès d’un utilisateur au système. Les shells sont assez sophistiqués et s’apparentent à de véritables langages de programmation interprétés. • Commandes de manipulation de fichiers ; • Commandes de gestion des processus ; • Éditeurs de texte ; • Outils de développement : compilateurs, débugueurs, analyseurs lexicaux et syntaxiques, etc. Connexion Un Compte correspond à un nom de connexion ou nom d'utilisateur + un mot de passe. Le nom est attribué une fois pour toute à un utilisateur par l’administrateur du site. Le mot de passe peut être modifié par l’utilisateur aussi souvent qu’il le désire 7 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Avec la généralisation des interfaces graphiques, l’accès à un système sous UNIX s’est diversifié et (théoriquement) simplifié. La procédure d’entrée d’un utilisateur dans le système se nomme le login. La sortie est donc le logout. Après la vérification du mot de passe, le système lance un interpréteur de commande (shell). « $ » (ou « % », « # ») peuvent être utilisé comme invite de l'interpréteur de commande utilisé. L'interpréteur attend que l'utilisateur tape une commande, exécute cette commande, réaffiche la chaîne d'invite, attend une nouvelle commande.... L'invite du root est en général « # » ; ce compte permet d'accéder à tous les fichiers et d'exécuter toutes les manipulations systèmes (administration). Ce fichier /etc/passwd rassemble des informations sur tous les utilisateurs ayant un compte sur le système. Ce fichier contient une ligne par utilisateur et 7 champs par ligne, séparés par le caractère ':‘. La figure 1.3 illustre les champs composant chaque ligne de ce fichier.
figure 1.3 Le fichier /etc/shells contient les shells que l’utilisateur peut utiliser 2. 5 Système de fichiers Les fichiers permettent de stocker d'une façon permanente les données sur des disques ou d'autres supports physiques. Les fichiers sont gérés par le système d'exploitation. La partie du système qui les gèrent est appelé le système de fichiers ou (file system). Dans ce chapitre nous allons traiter l'interface utilisateur relative à l'accès aux fichiers et aux catalogues. La mise en œuvre des fichiers feront l'objet d'un autre chapitre. 2.5.1 Les type de fichier : Pour le système, les fichiers sont organisés en deux grandes familles : les fichiers ordiniaire que sont par exemple les fichiers texte, les exécutables, etc. C’est-à-dire tout ce qui est manipulé et structuré par les utilisateurs. Les fichiers spéciaux périphériques, mémoire, et autre fichiers ”physiques” ou logique. Ces fichiers ont une structure interne définie (par les développeurs du système) qui doit être respecté ; c’est pourquoi leur manipulation n’est possible que par l’intermédiaire du système. Dans cette famille, on trouve plusieurs types de fichiers : Les catalogues(répertoire) sont des fichiers spéciaux, il faut pour les manipuler physiquement faire appel au système ce qui en protège la structure. On trouve deux types de fichiers physiques dans le répertoire /dev : Character devices (périphériques ou la communication ce fait octet par octet) les terminaux (claviers, crans) les imprimantes la mémoire etc 8 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Block devices (périphériques ou la communication ce fait par groupe d’octet appelés blocs) les disques les bandes magnétiques etc Les fichiers à usages logiques et non physiques liens symboliques sockets tubes nommés Ce dernier type de fichiers spéciaux est utilisé pour servir d’interface entre disques, entre machines et simule des lignes de communication, etc. 2.5.2 Les inodes L’inode est la structure qui contient toutes les informations sur un fichier donné à l’exception de sa référence dans l’arborescence (son nom). Les informations stockées dans une inode disque sont : utilisateur propriétaire groupe propriétaire type de fichier droits d’accès (ugo*rwx) date de dernier accès date de dernière modification date de dernière modification de l’inode taille du fichier adresses des blocs-disque contenant le fichier. 2.5.3 Organisation utilisateur des Disques Comment permettre aux utilisateurs d’identifier les données sur les supports de masse ? Le système le plus rependu aujourd’hui est un système arborescent avec des fichiers utilisés comme des nœuds. Comme il est ulistré sur la figure 1.3, la racine de cet arbre est notée '/’ /
home c1
passw d
c2
projet
.profile fichA
tmp
etc
fich1
Courier toto
fichB
figure 1.4 Chemins absolus et relatifs Un chemin absolu spécifie la suite des répertoires à traverser en partant de la racine, séparés par des caractères / (et non \ comme sous DOS). Exemple /etc/passwd /home/c2/projet/fichA Tout chemin qui ne commence pas par un caractère / (prononcé slash) est interprété comme un chemin relatif au répertoire courant. Exemple répertoire courant chemin relatif /home/c2 ./.profile 9 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
/home/c2/projet ./fichA /tmp ../etc/passwd Répertoire de connexion Après le login, l’interpréteur de commande a pour répertoire courant le répertoire de connexion de l’utilisateur. Le répertoire de connexion contient aussi certains fichiers de configuration permettant à l’utilisateur de personnaliser son environnement de travail. Ces fichiers sont normalement invisibles car leur nom commence par un point. A tout moment, on peut revenir au répertoire de connexion grâce à la commande cd sans argument. On peut aussi spécifier un chemin à partir du répertoire de connexion d’un utilisateur en utilisant ~ 2.5.4 Les commandes L'utilisateur manipule les fichiers via un ensemble de commandes La syntaxe générale d'une commande est commande [option ...] [argument ...] Lorsqu’on lance une commande plusieurs cas d'erreurs peuvent se produire : • La commande n'existe pas. Le shell distingue les lettres majuscules des minuscules. • Vous n'avez pas le droit d'exécuter cette commande. • Les options de la commande sont erronées. En général la commande affiche un message d'utilisation, précisant quels sont les options et arguments. Pour plus de détails taper $ man commande. Les arguments de la commande sont erronés ls [fichier ...] Affiche le contenu des répertoires et les noms des fichiers passés en argument, ou s'il n'y a pas d'argument, tous les fichiers du répertoire courant sauf ceux commençant par un point. Cette commande dispose de diverses options, dont les plus utilisées sont : -a : affiche également les nom des fichiers cachés (fichies dont le nom commence par '.' ; -l : affiche des informations supplémentaires sur les fichiers -i : affiche les numéros d'i-node; -s : affiche la taille en Ko de chaque fichier. cat [ fichier …] Recopie les fichiers spécifiés l’un après l’autre sur la sortie standard (concaténation). Si aucun argument n’est spécifié, lit sur l’entrée standard (jusqu’à rencontrer un caractére fin de fichier CTRLD). cp source dest Si dest est un répertoire, copie le ou les fichier(s) source vers dest. Si dest est un nom de fichier, dulpique source. mv source dest Si dest est un répertoire, déplace le fichier source vers dest. Si dest est un nom de fichier, renomme source. rm fichier ... Supprime le ou les fichiers spécifiés. L’option -r agit de façon récursive, c’est à dire détruit aussi les répertoires (pleins ou vide) et leurs sous-répertoires. pwd Affiche le répertoire courant. mkdir ou md[chemin] Crée un répertoire. Le chemin peut être relatif (par exemple mkdir ../exam) ou absolu (par ex. mkdir /users/emmanuel/cours). cd [chemin] Change le répertoire courant. Sans argument, ramène dans le répertoire de connexion (HOME). Liens entre fichiers Un fichier peut avoir plusieurs noms. Le système identifie un fichier non pas par un nom, mais par son numéro de i-noeud. Les liens "hard" ou physique Création de deux ou plusieurs noms vers un i-noeud unique au moyen de la commande ln. Avantage Une seule copie sur le disque et plusieurs façons d’y accéder Modification immédiatement sur tous les fichiers liés Simplifier l’accès à des fichiers dont le nom est difficile à retenir 10 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Limitation • Impossible de faire un lien sur un répertoire • Cette commande n'est utilisable qu'à l'intérieur d'un même système de fichiers (arborescence). Exemple $ln fichB fichBbis $ls –a donne l’affichage suivant : 423 fichA 666 fichB 666 fichBbis $ls –ai donne 423 -rw-r--r-- 1 c1 cours 342 Oct 18 15:28 fichA 666 -rwxr-xr-x 2 c1 cours 106 Oct 10 10:53 fichB 666 -rwxr-xr-x 2 c1 cours 106 Oct 10 10:53 fichBbis Les liens Symboliques Ces liens, différents des précédents, permettent de lier plusieurs noms à un même fichier sans rattacher ces derniers au i-noeud correspondant. Pour cela on utilise la commande ln avec l'option -s. On peut créer un lien au delà du système de fichiers du fichier d’origine (y compris les système de fichier réseau : NFS). L’information «lien » est plus lisible au ls. Dans ce type de lien il y a la création d’un nouvel i-noeud. Exemple $ ln -s foo bar $ ls -li bar foo 22195 lrwxrwxrwx 1 root root 3 Aug 5 16:51 bar->foo 22192 -rw-r--r-- 1 root root 12 Aug 5 16:50 foo head -n fich affichage les n premières lignes. Par défaut head affiche les 10 premières lignes tail -n fich ; affichage des n dernières lignes ; tail +n fich; affichage à partir de la ligne n tail –nc fich; affichage des n dernier caractères Tail +nc fich; affichage à partir du nème caractères Par défaut tail affiche les 10 dernières lignes wc fich : affichage du nombre de lignes, de mots, de caractères. Les options -l, -w et -c pour les nombres de lignes, de mots et de caractères. grep [option] motif fichier affiche les lignes de fichier qui contiennent le motif motif. grep ’define’ stdio.h: recherche le mot "define" dans le fichier stdio.h. grep ’hello’ *: recherche le mot "hello" dans tous les fichiers du répertoire. cut Il permet de sélectionner certaines parties des lignes d’un fichier ou de l’entrée standard (si aucun fichier n’est précisé). cut [-c] [-f] list [-n] [-d delim][-s] [file] Options: -c list: spécifie les caractères à sélectionner. c2-5 sélectionne les caractères 2 à 5 de chaque ligne. -f list: sélectionne pour chaque ligne les champs spécifiés, les champs étant délimités par un caractère délimiteur. -f 1,5 sélectionne les champs 1 et 5. -d delim: spécifie le caractère délimiteur. -s: supprime les lignes sans caractère délimiteur. -list: Liste de nombres séparés par une virgule, avec - pour indiquer un intervalle. 1,2,3,5 ou 1-3,5 Exemples: $ echo "bonjour" | cut -c 2-6 onjou $ echo "comment ca va" | cut -f2,3 -d’ ’ ca va 11 Aicha KERFALI Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
awk constitue un mini langage de programmation pour le traitement des motifs. Grâce à cette commande, vous pouvez automatiser de nombreux calculs. L’exemple suivant permet de comprendre le fonctionnement de awk : awk -F"\t" ‘{print $2}’ mon_fichier awk lit le contenu du fichier ligne par ligne. Pour chaque ligne, awk recherche des données séparées entre elles par le champ indiqué dans l’option -F et les place dans des arguments $1 , $2 … $n. Dans l’exemple, le séparateur est la tabulation (\t placé entre les doubles côtes " "); awk affiche alors la colonne 2. Concept de redirection À la connexion, le shell dispose de trois flots de communication, l’entrée standard : stdin (numéro 0). La sortie standard et erreur standard : stdout et stderr (numéros 1 et 2). L’association par défaut de ces flots est l’écran pour stdout et stderr, et le clavier pour stdin. Une redirection est une modification de l’une ou de l’autre de ces associations. Elle est valable uniquement le temps de la commande sur laquelle elle porte. Ainsi, la redirection de la sortie standard permet de récupérer le résultat dans un fichier. Redirections vers/depuis des fichiers Pour la sortie, on utilise >: ls > resultat. Un fichier nommé ici resultat est créé (s’il n’existe pas). Le résultat de ls est mis dans resultat. Si resultat existe déjà son contenu est écrasé. Rien n’apparait à l’écran (sauf s’il se produit une erreur). Si l’on désire ne pas écraser l’ancien contenu du fichier, mais écrire à sa fin, on peut utiliser >> : ls >> resultats. Pour rediger l’entrée standard, on utilise < : cat < UnFichier Il est possible de rediriger l’entrée et la sortie en même temps : cat < UnFichier > Resultat il est possible de rediriger la sortie standard d’une commande vers l’entrée d’une autre commande grâce au tube (pipe) noté |. ls | sort -r Droits d’accès un fichier Les utilisateurs d’un fichier donné sont divisés en trois ensembles : • le propriétaire du fichier ; • les utilisateurs du même groupe de travail que le propriétaire ; • les autres utilisateurs ayant accès au système.
La commande ls avec l'option -l (long) affiche de nombreuses informations sur le fichier :
12 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Modification des droits d’accès aux fichiers : Solution symbolique Dans ce cas on utilise les symboles u, g, o ou dont la signification est donnée dans le tableau suivant
chmod permet de modifier les droit d’accès à un fichier. Cet opération n’est autorisé qu’au propriétaire du fichier Exemple chmod go-w fichA; ls -l fichA -rw-r--r-- 1 c1 cours 342 Oct 18 15:28 fichA $chmod u+x fichA; ls -l fichA -rwxr--r-- 1 c1 cours 342 Oct 18 15:28 fichA $chmod g-r fichA; ls -l fichA -rwx---r-- 1 c1 cours 342 Oct 18 15:28 fichA $chmod ug+rw fichA; ls -l fichA -rwxrw-r-- 1 c1 cours 342 Oct 18 15:28 fichA $chmod g-rw fichA; ls -l fichA -rwx---r-- 1 c1 cours 342 Oct 18 15:28 fichA $chmod ug=rw fichA; ls -l fichA -rw-rw-r-- 1 c1 cours 342 Oct 18 15:28 fichA Modification des droits d’accès aux fichiers : Solution symbolique Le code peut également être donné en mode octal.
765 représente 450 représente
rwx rw- r-x r-- r-x --13
Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
$ ls -l toto -rw-r--r-- 1 c1 cours 342 Oct 18 15:28 toto $chmod 760 toto; ls -l toto -rwxrw---- 1 c1 cours 342 Oct 18 15:28 toto chgrp nom_groupe fichier Modifie le groupe propriétaire d'un fichier. chown nom_utilisateur fichier Modifie l'utilisateur propriétaire d'un fichier. chgrp et chown n’est autorisé qu’au superutilisateur $ ls -l f1 -rw-r----- 1 prof cours 11 Jul 13 13:00 f1 $ chown c1 f1 $ ls -l f1 -rw-r----- 1 c1 cours 11 Jul 13 13:01 f1 2.5.5 Méta-caractères pour l’interprétation des noms de fichiers Les caractères dits « Jokers » servent à référencer des noms de fichiers : « * » =) une chaîne de caractères quelconque (même vide) ; « ? » =) UN caractère quelconque ; [...] UN caractère quelconque dans l’ensemble défini entre « [ » et « ] » ; [ !...] UN caractère quelconque hors de l’ensemble. Un ensemble est défini par une liste de caractères ([adefgw]) ou un intervalle ([0-9]) ou toute combinaison des deux ([ab0-9A\ ;]). Par exemple, *.c tous les fichiers dont le nom se termine par un ‘.’ suivi de ‘c’ *.[csp] tous les fichiers dont le nom se termine par un ‘.’ suivi d’un caractère à choisir parmi ‘c’, ‘s’ ou ‘p’ f* Tous les fichiers dont le nom commence par 'f'. f? Tous les fichiers dont le nom commence par 'f', suivi d'un seul caractère quelconque. f[a-z] Tous les fichiers dont le nom commence par 'f' suivi d'une lettre minuscule. ?.c Tous les fichiers dont le nom est formé d'un caractère quelconque, suivi de '.c‘ ?? Tous les fichiers dont le nom est formé de deux caractères. *.[A-Za-z] Tous les fichiers dont le nom se termine par un '.' suivi d'une seule lettre majuscule ou minuscule. *.[ch0-9] Tous les fichiers dont le nom se termine par un '.' suivi d'un seul caractère à choisir parmi 'c', 'h', ou un chiffre entre '0' et '9'. [ !0-9]* =) noms ne commençant pas par un chiffre [!f]* Tous les fichiers dont le nom ne commence pas par 'f' 2.6 La programmation shell Script shell est une suite de commande UNIX réunies dans un fichier Soit le script suivant dans le fichier qui-est-la #!/bin/bash echo Liste des utilisateurs connectés par ordre alphabétique who|sort Pour exécuter le script, il faut rendre le fichier qui-est-la exécutable $chmod 766 qui-est-la $qui-est-la 2.6 .1 Les variables Une variable est une donnée identifiée par un nom. On Définit une variable par L’affectation var=bonjour La commande read var On accède au contenu d’une variable en ajoutant $. $var =‘hello word ’ $ echo $var 14 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
hello world Si ambiguïté utiliser {} $x=shell $echo $xshell donne le message d’erreur variable xshell non définie $echo ${x}shell donne shellshell $echo $x-shell donne shell-shell 2.6 .2 Expressions arithmétiques $expr $var + 1 affiche la valeur de var incrémentée de 1. il faut mettre des espaces dans l’expression. $let var=var+1 b=4*var/ 3 incrémente var de 1 et affecte à b la valeur 4var/3. il ne faut pas d’espace dans les expression $((y=2*var+6)) Affecte à y la valeur de l’expression 2var+6. ((…)) a une valeur, y accéder par $((…)) 2.6 .3 Liste des variables prédéfinies HOME : répertoire de connexion PATH : liste des répetoire de erecherche des commande PWD : répertoire courant REPLAY : paramètre par défaut de read PPID : numéro de processus père ? : code de retour de la dernière commande $ : numéro du processus du script ! :numéro de processus de la dernière tâche lancée en arrière plan Variable de position 0: nom du script 1: 1er paramètres, 2:2ème paramètres, etc. * ou @ : liste de tous les paramètres # : nombre des paramètres Exemple #!/bin/bash # commentaire Echo "information sur le script" echo "Le nombre d’argument est $#" echo "Les arguments sont $*" echo "Le premier est $1" echo "Le numero du process est $$" 2.6.4 Exécution conditionnelle Il est possible d’exécuter une suite d’instruction sous une certaine condition. if [ test ] then commande-si-vrai else commande-si-faux fi Le teste peut être une condition sur un fichier, ou une comparaison de chaînes ou de valeur. Il ne faut pas oublier les espaces dans if [ test ] 2.6.4.1 Conditions sur les fichiers Cette liste permet de réaliser des testes sur les fichiers. [ -s file ]: vrai si le fichier existe et est non vide. [ -e file ]: vrai si le fichier existe. [ -f file ]: vrai si c’est un fichier ordinaire. [ -d file ]: vrai si c’est un répertoire. [ -r file ]: vrai si le fichier est libre en lecture. 15 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
[ -w file ]: vrai si le fichier est libre en écriture. [ -x file ]: vrai si le fichier est exécutable. [ -L fich ] : vrai si le fichier est un lien symbolique [ -O fich ] : vrai si le fichier appartient au propriétaire du processus [ fich1 –nt fich2 ]: vrai si fich1 est plus récent que fich2 [ fich1 –ot fich2 ]: vrai si fich1 est plus vieux que fich2 [ fich1 –et fich2 ]: vrai si fich1 est un lien vers fich2 2.6.4.2 Comparaison de valeurs et chaînes La comparaison des entier utilise les testes suivant : [ exp1 -eq exp2 ]: vrai si exp1 = exp2. [ exp1 -ne exp2 ]: vrai si exp1 exp2. [ exp1 -lt exp2 ]: vrai si exp1 < exp2. [ exp1 -le exp2 ]: vrai si exp1 exp2. [ exp1 -ge exp2 ]: vrai si exp1 >= exp2 Les testes concernant les chaînes de caractère utilisent les comparaison suivantes : [ chaine = motif ]: vrai si chaine est de forme motif [ chaine!= motif ]: vrai si chaine n’est pas de la forme motif [ chaine1chaine2 ]: vrai si chaine1 est supérieur à chaine2 [ -z chaine ]: vrai si chaine est de longueur nulle [ -n chaine ]: vrai si chaine est de longueur non nulle Exemple Afficher le contenu d’un fichier s’il existe. #/bin/bash if [ -f $1 ] then cat $1 fi Créer un répertoire toto s’il n’existe pas déjà. #/bin/bash if [ ! -d toto ] then mkdir toto fi 2.6.4.3 Combinaison de tests [ E -a F ] est vrai si les expressions E et F sont vraies. [ E -o F ] est vrai si E ou F est vraie. On peut utiliser && au lieu de –a et on peut utiliser || au lieu de -o 2.6.5 Choix multiple Case … .in ... ... esac case cvaleur in val11|val12|…|val1x liste_commandes1 ;; val21|val22|…|val1y liste_commandes2;; ... valn1|valn2|…|val1z 1liste_commandesn;; esac liste_commandesi est exécutée si valeur est de la forme de l'une des valij Le caractère * recouvre toute valeur
16 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
2.6.6 Structures de contrôle itérations for variable vin liste_valeurs do liste_commandes done Exécuter liste_commandes pour chaque valeur de variable dans liste_valeurs. Le manque de in liste_valeurs est équivalent à in $* (variable prend tour à tour les valeurs des variables de position) Mots clés do et done précédés d'un ';' ou d'un retour à la ligne. On peut aussi utiliser la structure de contrôle while condition do liste_commandes done
17 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Chapitre 2 Les processus I- caractéristique Un processus est un programme qui s'exécute et il possède un compteur ordinal, un ensemble de registres et une mémoire (données, code et pile ).
Le code correspond aux instructions, en langage machine, du programme à exécuter. La zone de données contient les variables globales ou statiques du programme ainsi que les allocations dynamiques de mémoire. Enfin, les appels de fonctions, avec leurs paramètres et leurs variables locales, viennent s'empiler sur la pile. Conceptuellement, chaque processus a son processeur propre virtuel. En réalité, le vrai processeur commute entre plusieurs processus sous la direction d’un ordonnanceur.
II- Les différents états d'un processus 2.1 Au niveau système Au niveau système un processus peut être dans l'un des états suivants : • Elu s’il est en cours d'exécution sur un processeur. il y a toujours autant de processus élu que de processeurs. • Prêt s’il est suspendu provisoirement pour permettre l'exécution d'un autre processus. Un processus est prêt s’il ne lui manque que la ressource processeur pour s’exécuter. • Bloqué s’il est en attente d’un événement extérieur (bloc disque, frappe clavier pour pouvoir continuer) La transition entre ces trois états est matérialisée par le schéma suivant: Sélection Création nn
Prêt
Elu Préemption
Fin attente
Destruction
Attente Bloqué
2.2 Vision utilisateur des états d’un processus Pour un utilisateur, un processus peut se trouver dans trois états : en exécution (exécution de la commande), suspendu (CTRL-Z) ou terminé. Le schéma suivant récapitule les transitions permettant de passer d’un état à un autre. 18 Aicha KERFALI Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Par défaut, une commande s’exécute en avant-plan (en anglais foreground). Dans ce cas pour exécuter une commande, le shell crée un processus enfant et attend qu’il se termine. Le processus enfant exécute la commande. Les processus parent et enfant s’exécutent séquentiellement (l’un après l’autre). Une seule commande est donc exécutée à la fois. Une commande peut aussi s’exécuter en arrière-plan (en anglais background). Par exemple, l’utilisateur saisit date &. Dans ce cas le shell crée un processus enfant et n’attend pas qu’il se termine. Le processus enfant exécute la commande date. Les deux processus, parent et enfant, s’exécutent alors « simultanément ». Sous Unix, il est possible de suspendre le processus en avant-plan en tapant CTRL-Z. Le processus suspendu pourra reprendre ultérieurement. Il existe deux façons de reprendre un processus suspendu En avant-plan par la commande fg (foreground), En arrière-plan par la commande bg (background). Par exemple : $gedit lancement de gedit en avant-plan CTRL-Z édition suspendue) $ bg entraine la reprise de l’édition en arrière-plan Un « job » est défini comme un processus en arrière-plan ou suspendu. La commande jobs permet de lister ces processus III- Relation entre les processus Les processus des utilisateurs sont lancés par un interprète de commande (shell). Ils peuvent eux-même lancer ensuite d'autres processus. On appelle le processus créateur, le père, et les processus créés, les fils. Les processus peuvent donc se structurer sous la forme d'une arborescence. Au lancement du système, il n'existe qu'un seul processus, appelé processus « init » Le processus "init" crée 2 sortes de processus : • Des démons, des processus qui ne sont rattachés à aucun terminal, qui sont endormis la plupart du temps, mais qui se réveillent de temps en temps pour effectuer une tâche précise. • Des processus interactifs, vous permettant de vous connecter.
19 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
init daemons
getty
login
shell 3.1 Création d’un processus Un processus est créer au moyen d’un appel système, c’est-à-dire par un autre processus ayant réalisé cet appel, on voit apparaître naturellement la notion d’arborescence des processus, à partir d’un ancêtre commun à tous, crée au démarrage du système (processus init sous Unix). Un processus et identifié par un numéro (PID) . La primitive getpid() renvoie le numéro (PID) du processus qui l’exécute. Chaque processus a un père, celui qui l’a lancé. La primitive getppid() renvoie le numéro du père et getuid() permet d’obtenir le numéro d’utilisateur (auquel appartient le processus) Le PID du processus init est 1 Au niveau des appels système un processus est créé par une instruction fork() Format de création des processus sous Unix Fork : pid_t fork() L’appel de cette primitive permet la création d'un processus fils. Elle retourne le PID du fils à l'intérieur du processus père et 0 à l'intérieur du processus fils. Il en résulte que les branches père et fils se distinguent dans le programme par un if sur la valeur du pid (pid = 0, on est dans le fils). Si le fils n'a pu être créé, fork retourne -1 #include #include //pour faire appel aux primitives getpid() et getppid() int main(void) { fork(); printf("je suis le processus %d\n",getpid()); } $ gcc processus.c L'execution de ce programme donne le résultat suivant : $ ./a.out je suis le processus 2005 //affichage du père je suis le processus 2006 //affichage du fils Après sa création le fils et son père exécutent la même instruction. Pour distinguer le code du père de ce celui du fils il faut tester la valeur retournée par fork() if (fork() != 0) { /* Exécution du code du père */ printf("je suis le père, mon PID est %d\n", getpid()); 20 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
} else { /* Exécution du code du fils */ printf("je suis le fils, mon PID est %d et le PID de mon pere est %d\n", getpid(), getppid()); } Pendant l'exécution du fork le système: o alloue une entrée dans la table des processus au nouveau processus, o alloue un pid unique au nouveau processus, o duplique le contexte du processus père (code, données, compteur ordinal, compteur de pile, etc...), o retourne au père le pid du fils créé et 0 au fils. o Le fils hérite de tous les attributs du père sauf : son pid (hors du contexte du père, fourni par le système opératoire), les statistiques de temps d'exécution (remises à zéro). Exemple avec fork() #include #include int main() { int pid=fork(); if(pid!=0) { printf("Je suis le perre."); printf(" Mon PID est : %d et le PID de mon fils est %d \n",getpid(),pid); } else{ printf("Je suis le fils."); printf(" Mon pid est:%d\n",getpid()); } } Résultat: Je suis le pere. Mon PID est : 1320 et le PID de mon fils:1321 Je suis le fils. Mon PID est:1321 $ Peut aussi apparaître dans l’ordre inverse. Le fils et le père exécutent le même programme, mais chacun a sa propre mémoire virtuelles ce qui donne naissance à deux 2 jeux de données différents int i ; if (fork() != 0) { printf("Je suis le père, mon PID est %d\n", getpid()); i = 3; } else { printf("Je suis le fils, mon PID est %d\n", getpid()); i = 5; } printf("Pour %d, i = %d\n", getpid(), i); } Résultat Je suis le fils, mon PID est 1271 Pour 1271, i = 5 Je suis le père, mon PID est 1270 Pour 1270, i = 3 3.2 Terminer et faire attendre un processus Un processus se termine après l’exécution de la dernière instruction. La primitive void exit(int status) termine brutalement un processus. La valeur status est envoyée au processus parent. La librairie à utiliser est stdlib.h. 21 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
int sleep(int seconds) fait attendre un processus un nombre donné de secondes. Le processus qui appelle sleep est bloqué pendant le nombre de secondes spécifié. Exemple1 int main() { if (fork() != 0) { printf("je suis le père, mon PID est %d\n", getpid()); sleep(10) /* blocage pendant 10 secondes */ exit(0); } else { printf("je suis le fils, mon PID est %d\n", getpid()); sleep(10) /* blocage pendant 10 secondes */ exit(0); } Exemple 2: main() { if (fork() == 0) { // fils printf("je suis le fils, mon PID est %d\n", getpid()); exit(0);//terminaison du fils /* la partie suivante est non exécutable par le fils */ printf("je suis le fils, le PID de mon père est %d\n", getppid()); } else { printf("je suis le pere, mon PID est %d\n", getpid()); } } Exemple 3: main() { if (fork() == 0) { /* fils */ printf("je suis le fils, mon PID est %d\n", getpid()); exit(0); } else { /* pere */ sleep(30); } } L’exemple 3 est équivalent à: main() { if (fork() == 0) { // fils printf("je suis le fils, mon PID est %d\n", getpid()); exit(0); } sleep(30);} 3.3 Les processus zombi Quand un processus se termine, il délivre un code de retour (paramètre de la primitive exit()). Par exemple exit(0) donne un code de retour 0. Tout processus qui se termine passe dans l’état zombi tant que son père n’a pas pris connaissance de sa terminaison. Voir Exemple 3 exit zombi fils disparu père
fork
sleep
bloqué
terminé
bash 22 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Le temps pendant lequel le processus fils est en état zombi, il n’a plus son code ni ses données en mémoire; seules les informations utiles pour le père sont conservées. Il faut éviter de garder des processus en état zombi. Pour cela il faut synchroniser le père et le fils à l’aide de la primitive wait. 3.4 Synchronisation entre un processus père et ses fils #include #include pid_t wait(int *ptretat) ; Si le processus appelant n’a aucun fils, la primitive wait renvoie -1. Si le processus appelant possède des fils qui ne sont pas à l’état « zombie », il est mis en attente jusqu'à ce que l’un des fils passe dans cet état. Si le processus appelant possède des fils à l’état « zombie », la primitive renvoie le PID d’un des fils et écrit à l’adresse ptretat un entier qui renseigne sur la façon dont s’est terminé ce fils (code de retour) Si le processus fils est mort par un appel à exit(code), alors l’octet de poids faible de ce code est recopié dans l’octet de poids fort de l’entier pointé par ptretat (l’autre octet est nul). Exemple4 #include int main() { if (fork() != 0) { printf("je suis le père, mon PID est %d\n", getpid()); while (1) ; /* boucle sans fin sans attendre le fils */ wait(0); } else { printf("je suis le fils, mon PID est %d\n", getpid()); Sleep(2) /* blocage pendant 2 secondes */ printf("fin du fils\n"); exit(0); } } exit zombi père
fork
Exemple5 #include …… int pidfils=fork(); if(pidfils!=0) { printf("Je suis le père \n"); wait(0); } else { printf("arrêt du fils \n"); sleep(30); exit(0); } père fork
exit
wait
bloqué
Exemple 6 #include …… int pidfils=fork(); if(pidfils!=0) { 23 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
printf("Je suis le père \n"); wait(0); printf("mon fils a terminé \n"); // ne sera exécutée qu'après terminaison du fils } else { printf("je suis le fils \n"); sleep(2); printf("arrêt du fils \n"); exit(0); } Résultats d’exécution Je suis le père je suis le fils arrêt du fils mon fils a terminé $ Si le processus fils est mort par un appel à exit(code), alors l’octet de poids faible de ce code est recopié dans l’octet de poids fort de l’entier pointé par ptretat (l’autre octet est nul). Pour que le père puisse utiliser la même valeur que son fils, il fait appel à WEXITSTATUS(*ptretat) Exemple 7 #include #include #include #include #include #include main(void) { pid_t pid,pidfils ; int status ; printf("Processus père de PID %d\n ",getpid()) ; switch(pid = fork()) { case -1 : perror("naufrage... ") ; exit(2) ; case 0 : printf(" Début du processus fils PID=%d \n ",getpid()) ; sleep(30) ; exit(33) ; default : pidfils = wait(&status) ; /* attention adresse ! ! ! */ printf(" Mort du processus fils PID=%d\n ",pidfils) ; printf(" code de retour :%d\n ",WEXITSTATUS(status)) ; } } Résultat d'exécution Processus père de PID 3232 Début du processus fils PID=3233 Mort du processus fils PID=3233 code de retour :33 3.5 Exécution d’un programme spécifié sous UNIX #include 24 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
int execl(const char *ref, const char *arg0,...,(char *)0); Les primitives de la famille exec* permettent à un processus de charger en mémoire, en vue de son exécution, un nouveau programme binaire. En cas de succès de l’opération, il y a écrasement du code, des données et par conséquent aucune marche arrière n’est possible. Le processus garde son PID et donc la plupart de ses caractéristiques. Il existe plusieurs variante de exec. Pour en savoir plus executez : $man exec Exemple : main(){ if (fork()==0) { printf("fils\n"); execl("/bin/ls","ls","-al","/bin",NULL); //le fils exécute ls -al /bin } exit(0) ; }
25 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Chapitre 3 :
Filière SMI(S4)
Communication entres processus
Introduction Il existe différents moyens pour faire communiquer les processus sous Unix Les tubes permettent de transmettre un flux de caractères en mode continu entre processus appartenant ou non à la même famille Les files de massages permettent de transmettre des données structurées entre processus appartenant ou non à la même famille Les sockets permettent une communication entre processus s’exécutant sur des machines différentes dans un réseau d’ordinateurs. Les signaux permettent d’interrompre un processus pour exécuter une autre tâche I- Communication par tubes (pipe) Un tube est un canal de communication inter-processus unidirectionnel (communication dans un seul sens). Un processus écrit (producteur) des données dans le tube et l'autre les lit (consommateur) dans le tube
écriture
lecture
Caractéristiques Un tube est géré comme une file FIFO (First In First Out: premier entré premier sorti). Les données sont lues dans l’ordre dans lequel elles ont été écrites dans le tube. Les données écrites dans le tube sont destinées à un seul processus et ne sont donc lus qu’une seule fois. la lecture dans un tube est destructive, une fois une donnée est lue elle sera supprimée. On peut avoir plusieurs processus lisant dans le même tube mais ils ne liront pas les mêmes données, car la lecture est destructive. Il existe deux types • tubes anonymes: ne permettent que la communication entre processus dans la même application: entre processus descendant du processus qui a crée le tube. tubes nommés: permettent des communications entre processus quelconques (processus créés dans des application différente mais pas nécessairement par le même utilisateur).
1.1 - Les tubes anonymes 1.1.1 Création et fermeture d’un tube La création d'un tube correspond à celle de deux descripteurs de fichiers, l'un permettant d'écrire dans le tube par l’opération classique de lecture dans un fichier (write) et l'autre de lire dans le tube l’opération classiques d’écriture dans un fichier (read). Pour la création d’un type on utilise l'appel système de la primitive pipe(); #include int pipe (int p[2]); // tableau p des descripteurs En cas d'échec, cette fonction retourne -1 En cas de succès cette fonction retourne 0. Dans ce cas le tableau p contient les descripteurs des 2 extrémités du tube qui seront utilisés pour accéder (en lecture/écriture) au tube . Le descripteur d'indice 0 (p[0]) désigne la sortie du tube, ouvert en lecture seule. Le descripteur d'indice 1 (p[1]) désigne l'entrée du tube, ouvert en écriture seule. Exemple: main () { int p[2]; pipe(p); /* création de tube */ } La fermeture d'un descripteur se fait grâce à la primitive close() close (int fd)avec fd est le descripteur du flux 26 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Exemple: close (p [1]); // descripteur en écriture non utilisé close (p [0]); // descripteur en lecture non utilisé 1.1.2 Écriture et lecture dans un tube L’écriture dans un tube se fait grâce à la primitive write() dont la syntaxe est la suivante: int write (int p[1], void *buf, int Bufsize); P[1] : descripteur du flux buf : un pointeur sur la zone mémoire contenant les données à écrire dans le tube. bufsize: le nombre d'octets que l’on souhaite écrire dans le tube. En cas de succès cette fonction retourne le nombre d'octets effectivement écrits. En cas d’erreur cette fonction retourne -1 Remarque: La valeur de Bufsize doit être inférieure à la capacité maximale d’un tube PIPE_BUF définie dans le fichier (cette valeur à calculer, elle dépend du système). Exemple : Le processus écrit dans un tube une chaîne de N caractères #include #include #define N 6 main () { char *c; int p[2]; int nb_ecrits; c=(char *)malloc(N*sizeof(char)); c="ABCDEF"; pipe(p); /* création de tube */ if ((nb_ecrits=write(p[1],c,6))==-1) { printf("Erreur d'ecriture dans le tube \n"); exit(0); } printf("nb_ecrits = %d \n ", nb_ecrits); } Soit N le nombre d’octets libres dans le tube, autrement dit le nombre maximal d’octets que l’on peut écrire encore dans le tube. Si la valeur de Bufsize est supérieure à N, le processus sera bloqué jusqu'à ce que Bufsize octets soient libérés dans le tube. Algorithme d’écriture Si nombre de lecteurs dans le tube =0 alors le signale SIGPIPE est envoyé au processus Sinon le processus sera bloqué jusqu'à ce que Bufsize octets soient libérés dans le tube. fsi Un lecteur dans le tube est un processus qui détient le descripteur de lecture associé à ce tube. SIGPIPE arrête l’exécution du processus. Ecriture dans un tube fermé en lecture Lorsqu’un processus tente d’écrire dans un tube sans lecteur (descripteur en lecture est fermé), alors il reçoit le signal SIGPIPE et il sera interrompu (s'il ne traite pas ce signal). Exemple main () { int p[2]; 27 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
pipe (p); // création de tube close (p [0]); // descripteur en lecture est fermé printf(“ Debut d ecritue dans le tube ”); if ( write(p[1],"abc" ,3)==-1) printf(“ erreur ecriture dans le tube”); else printf(“Pas d erreur”); printf(“ processus termine ”); } Résultat d’exécution: Ce programme affiche le message « Debut d’ecritue dans le tube » et s'arrête. La lecture dans un tube se fait grâce à la primitive read dont la syntaxe est la suivante: int read (int p[0], void *buf, int bufsize); P[0] : descripteur du flux buf : un pointeur sur une zone mémoire dans laquelle elle seront rangées les données après lecture. bufsize: le nombre d'octets que l’on souhaite lire à partir du tube. En cas de succès cette fonction retourne le nombre d'octets effectivement lus. En cas d’erreur cette fonction retourne -1 Exemple d’un processus qui lit et écrit dans le même canal de communication : #include #include #define N 6 main () { char *c, *s; int p[2]; int nb_lus, nb_ecrits; c=(char *)malloc(N*sizeof(char)); s=(char *)malloc(N*sizeof(char)); c="ABCDEF"; pipe(p); /* création de tube */ if ((nb_ecrits=write(p[1],c,N))==-1) { printf("Erreur d'ecriture dans le tube \n"); exit(0); } printf("nb_ecrits = %d \n ", nb_ecrits); if ((nb_lus=read(p[0],s,N))==-1) { printf("Erreur de lecture dans le tube \n"); exit(0); } else if (nb_lus==0) { printf("pas de caractère lu \n"); exit(0); } printf(" la chaîne lue est : %s \n", s); } Un lecteur qui tente de lire dans un tube vide trouve les problèmes suivants : 28 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation • •
Filière SMI(S4)
Si le tube n’a pas de rédacteur (le descripteur en écriture est fermé) alors la fonction read retourne 0 caractère lus. Si le tube a un rédacteur alors il reste bloqué jusqu’à ce que le tube ne soit pas vide.
Algorithme de lecture Si tube non vide et contient N octets alors La primitive extrait nb_lu égale min(N, bufsize) caractères et les place à l’adrsse buf Sinon Si nombre de rédacteurs =0 alors la primitive renvoie la valeur 0 Sinon le processus est bloqué jusqu’à tube non vide Fsi Fsi Un rédacteur est un processus qui détient le descripteur d’écriture associé au tube Lecture dans un tube vide fermé en écriture (pas de rédacteur) Exemple: L’exemple suivant illustre le cas de la lecture dans un tube vide sans producteur (pas de processus qui écrit dans le tube). void main () { char c; int p[2]; int nb_lus; pipe (p); // création de tube close (p [1]); // descripteur en écriture non utilisé if ((nb_lus= read(p[0],&c,1))== -1) printf(“ erreur lecture dans le tube”); else if (nb_lus==0) printf(“Pas de caracteres lus”); printf(“processus termine \n”); } Résultat d’exécution: Ce programme affiche “Pas de caracteres lus”, car comme il n’y pas de producteur, la fonction read retourne 0. Lecture dans un tube vide avec rédacteur Exemple: Lecture dans un tube vide mais qui a un rédacteur. L’exemple suivant illustre le cas de la lecture dans un tube vide avec producteur. void main () { char c; int p[2]; int nb_lus; pipe (p); // création de tube if ((nb_lus==read(p[0],&c,1))==-1) printf(“ erreur lecture dans le tube”); else if (nb_lus==0) printf(“Pas de caractère lu”); printf(“processus termine \n”); } Résultat d’exécution: Ce programme reste bloqué. 1.1.3 Communication entre 2 processus Les exemples précédents ont traités le cas où c’est le même processus qui lit et écrit dans le tube. On va utiliser l'appel système fork() pour créer un processus fils 29 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Si la communication va du père vers le fils, le père ferme son descripteur de sortie de tube et le fils ferme son descripteur d'entrée de tube.
père fils Dans cet exemple le père envoie un tableau d'entier au fils. Ce dernier reçoit le tableau et calcule la somme de ses éléments et l'affiche #include #include # define N 10 # define NN N*sizeof(int) main () { int *c, *s; /* on peut utiliser une seule variable */ int p[2]; int i, nb_lus, nb_ecrits, somme=0; c=(int *)malloc(N*sizeof(int)); s=(int *)malloc(N*sizeof(int)); pipe(p); /* création de tube */ if (fork()!=0) { // le père for (i=0; i NSIG valeur incorrecte ; • 0 pas de signal envoyé mais peut être utilisé pour tester l’existence d’un processus. • sinon signal de numéro sig. Les processus émetteur et récepteur doivent avoir le même propriétaire ! ! ! La primitive renvoie 0 ou -1 selon qu’elle s’est déroulée normalement (0) ou pas (-1). La fonction int raise (int sig) envoie le signal de numéro sig donné au processus courant. raise(sig) est équivalent à kill(getpid(),sig). unsigned int alarm(unsigned int seconds) permet de programmer l'envoi d'un signal SIGALRM après un délai donné. Exemple Le processus père teste l’existence de son fils avant de lui envoyer le signal SIGUSR1. #include #include #include #include #include #include int main(void) { pid_t pid,pidBis; int status; pid = fork(); if(pid==0) while(1) sleep(1); else { sleep(10); if(kill(pid,0) == -1) {printf("processus fils %d inexistant\n",pid); exit(1); } else {printf("envoi de SIGUSR1 au fils %d\n",pid); kill(pid,SIGUSR1); } pidBis=wait(&status); printf("Mort de fils %d avec status=%d\n",pidBis,status); exit(0); } } Résultat de l'exécution $ ./a.out 34 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
envoi de SIGUSR1 au fils 2898 Mort de fils 2898 avec status=10 Dans le paramètre status de la fonction wait on a récupéré le numéro du signal qui a causé la terminaison du fils. 2.4 Le captage des signaux 2.4.1 traitement des signaux à partir d'un programme C Les handlers Un handler est une fonction qui sera exécutée au moment où le processus va recevoir un certain signal. A la réception d'un signal, le processus peut le traiter de deux façons différentes : • Si on a déclaré un handler, la fonction est exécutée et l’action par défaut n’est pas appliquée. La primitive signal(numéro_signal, fonction_a_appeler) permet de déclarer un handler. Mais il existe une autre fonction plus générale que signal mais plus compliquée à utiliser : la primitive sigaction. • Si le processus n’a pas de comportement spécifique prévu, le comportement par défaut est appliqué La fonction signal permet de spécifier ou de connaître le comportement du processus à la réception d’un signal donné, il faut donner en paramètre à la fonction le numéro du signal sig que l’on veut détourner et la fonction de traitement fonction_a_appeler à réaliser à la réception du signal. Trois possibilités pour ce paramètre fonction_a_appeler : SIG_DFL Comportement par défaut, plusieurs possibilités exit Le processus se termine ignore Le processus ignore le signal pause Suspension du processus continue Reprise du processus si il était suspendu. SIG_IGN le signal est ignoré. les signaux SIGKILL, SIGSTOP ne peuvent pas être ignorés. HANDLER Une fonction de votre choix. Exemple #include #include void fonction_handler() { printf("Interruption du processus !\n"); } int main() { // Association du handler au signal SIGUSR1 signal(SIGUSR1, fonction_handler) ; // Boucle infinie, pour faire travailler le processus while (1) { sleep(1); printf(".\n"); } } On lance le programme, qui boucle infiniment puis on lui envoie le signal SIGUSR1, grâce à la commande kill du shell. Pour cela il faut connaître le PID du processus. $ ps -a PID TTY TIME CMD 2891 pts/0 00:00:00 a.out 2892 pts/1 00:00:00 ps $ kill -USR1 2891 … ce qui nous donne ce résultat : . . . . 35 Aicha KERFALI Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
. .Interruption du processus ! . . . . . On voit bien que la boucle infinie a été interrompue et que le handler a été exécuté. Une fois la fonction terminée, le processus a continué son exécution normale. Il est possible d’ignorer un signal en mettant comme handler la valeur SIG_IGN. Exemple #include #include void fonction_handler() { printf("Interruption du processus !\n"); } int main() { // Association du handler au signal SIGUSR1 signal(SIGUSR1, SIG_IGN); // Boucle infinie, pour faire travailler le processus while (1) { sleep(1); printf(".\n"); } } Si on lance l'exécution de ce programme ça va boucler d'une façon infinie sans interruption même si on envoie au processus le signal à partir de la ligne de commande. La primitive int pause(void) bloque le processus appelant jusqu'à l'arrivé d'un signal quelconque Exemple : envoi et traitement d'un signal à partir d'un programme C. #include #include #include #include void fonction_handler(int num) { printf("%d %d jai recu un signal\n",getpid(),getppid()); } int main() { int pid; signal(SIGUSR1, fonction_handler); switch(pid = fork()) { case 0: pause(); //le fils attend l'arrivé d'un signal break; default: sleep(1); kill(pid, SIGUSR1); //le père envoit le signal SIGUSR1 au fils sleep(5); } } $./a.out 3173 3172 jai recu un signal 36 Aicha KERFALI
Année universitaire 2013/2014
Cours des systèmes d’exploitation
Filière SMI(S4)
Exemple Dans cet exemple, on va compter le nombre d'exemplaires reçus de chacun des signaux pour lesquels on peut redéfinir le comportement. #include #include #include void handler(int); int nsig[NSIG]; int main(void) { int s; printf("le nombre de signaux est %d\n",NSIG); for (s = 1; s < NSIG; s++) { if (signal(s, handler) == SIG_ERR) fprintf(stdout, "Je ne peux pas attraper signal no %d\n", s); nsig[s] = 0; } while (1); } void handler(int s) { printf("Signal %d recu %d fois\n", s, ++nsig[s]); } Résultat d'une exécution $./a.out le nombre de signaux est 65 Je ne peux pas attraper signal no 9 Je ne peux pas attraper signal no 19 Je ne peux pas attraper signal no 32 Je ne peux pas attraper signal no 33 Signal 2 recu 1 fois
View more...
Comments