Vous êtes sur la page 1sur 90

Programmation système

IAI SR3
objectif: Ce cours a pour objectif de permettre a l’étudiant
d’utiliser efficacement le système d’exploitation.
Objectifs spécifiques:
• Fonctionnement du système d’exploitation : cas des distributions
Linux;
• Concepts clés des OS: processus, fichier, ordonnancement…;
• Utilisation des interfaces systèmes;
• Adoption d’une politique de sécurité.
Prérequis
• Notions de bases en architecture clients/serveurs;
• Notions de bases en sécurité informatique;
• Notions de bases en structure de données;
• Disposer d’un environnement de tests (une distribution Linux);
• Maitrise d’un langage de programmation.
Bibliographie/webographie
• Client/serveur et protocoles applicatifs henry Dubois;
• Introduction aux systèmes d’exploitation : SUPCOM TUNIS ;
• La sécurité dans les systèmes d’exploitation : université de paris-est
Créteil;
• Systèmes d’exploitation et programmation système: martin Quinson;
• Les systèmes d’exploitation, Silberschatz
• www.reseaucerta.org
plan

• Le système de fichier UNIX/Linux


• Ordonnancement des processus
• La gestion de sécurité dans les SE
• Les protocoles clients/serveurs
• Les scripts de sauvegarde
• Le contrôle de charge
Evaluation:
• Contrôle continu;
• Projet;
• Examen.
Chapitre1 : le système de fichier UNIX/Linux
• Un système de fichier est une organisation ou une structure de
données généralement hiérarchique qui permet de spécifier le nom,
l’auteur, la date d'édition, l’extension, le nom du groupe, la taille…d’un
fichier. C’est simplement la table de matières des fichiers dans un DD.
L’entrée du système de ficher se situe a la racine notée /.
Ensuite il existe un certain nombre de répertoires présents par défaut.
Système de fichier UNIX/Linux
• / repertoire; ‘racine ‘ point d’entree du système de fichier
• /boot: repertoire contenant le noyau lunix et l’amorceur
• /bin repertoire contenant les executables de base, comme cp,mv, ls
etc
• /dev: Répertoire contenant des fichiers spéciaux nommés devices qui
permettent le lien avec les périphériques de la machine
• /etc: Répertoire contenant les fichiers de configuration du système
• /home :Répertoire contenant les fichiers personnels des utilisateurs
(un sous-répertoire par utilisateur)
Système de ficher UNIX/Linux
• /lib: Répertoire contenant les librairies et les modules du noyau
(/lib/modules)
• /media: répertoire contenant les points de montages des medias
usuels: CD, DVD, clef USB
• /root: Répertoire personnel de l'administrateur
• /sbin: Répertoire contenant les exécutables destinés à l'administration
du système
• /tmp: Répertoire contenant des fichiers temporaires utilisés par
certains programmes
Système de fichier UNIX/Linux
• /usr: répertoire contenant les exécutables des programmes(/usr/bin
et /usr/sbin) la documentation (/usr/doc), et les prgrammes pour le
serveur graphique (/usr/X11R6)
• /var: répertoire contenant les fichiers qui servent a la maintenance du
système.
Système de fichier UNIX/Linus
Le système de fichier offre a l’utilisateur une vue abstraite sur les
données et permet de les localiser grace au cemins d ’acces. Il exixte
deux types de chemins d’acces:
• Chemin absolu:
liste des repertoires depuis la racine (/) jusqu’au fichier recherché.
• Chemin relatif :
Liste des repertoire depuis le repertoire courant jusqu’au fichier
recherché.
Système de fichier UNIX/Linux
ARBORESCANCE (CHEMIN /PATH):
/ : répertoire racine de l ’arborescence
. : répertoire courant
.. : répertoire parent
~: répertoire principal de l’utilisateur courant
NB :
Le chemin absolu est unique et il existe plusieurs chemins relatifs.
Système de fichier UNIX/Linux
Exemple
Système de fichier UNIX/Iinux
1. Ecrire le chemin absolu pour f1
2. Ecrire deux chemins relatifs pour f2
Chapitre 2: ordonnancement des processus
Un processus est une instance en cours d'exécution d'un programme
exécutable.
L'environnement d'un processus comprend :
• Des variables locales et globales ;
• Un contexte de planification actif, et des ressources système allouées,
telles que des descripteurs de fichier et des ports réseau.
• Un processus (parent) existant réplique son propre espace d'adressage
(fork) pour créer une nouvelle structure de processus (enfant).
• Un identifiant de processus unique (PID) est affecté à chaque nouveau
processus pour le suivi et la sécurité.
ordonnancement des processus
• Le PID et l'identifiant du processus parent (PPID) sont des éléments de
l'environnement du nouveau processus.
• Tout processus peut créer un processus enfant.
• Tous les processus sont des descendants du processus système initial,
qui est systemd.
ordonnancement des processus
La routine fork permet à un processus enfant d'hériter des identités de
sécurité, des descripteurs de fichiers précédents et actifs, des privilèges
relatifs au port et aux ressources, des variables d'environnement et du code
du programme. Un processus enfant peut ensuite exécuter son propre code
de programme. Généralement, un processus parent dort (sleep) pendant
l'exécution du processus enfant, et émet une requête (wait) pour être averti
lorsque le processus enfant est terminé. Lorsque le processus enfant
s'arrête, il a déjà fermé ou éliminé ses ressources et son environnement ; ce
qu'il reste de ce processus est appelé zombie. Le parent, réveillé par un
signal lorsque le processus enfant est terminé, nettoie la structure restante,
puis continue d'exécuter son propre code de programme.
Cycle de vie d’un processus
• Dans un système d'exploitation multitâches, chaque processeur (ou
cœur de processeur) peut travailler sur un processus à un instant
donné.
• Pendant l'exécution d'un processus, ses besoins immédiats en temps
processeur et en ressources évoluent.
• Un état, qui évolue en fonction des circonstances, est affecté à
chaque processus.
• Dans un système multitâche plusieurs processus sont en cours
simultanément. Toutefois, le processeur ne peut, à un moment
donné, exécuter qu’une instruction(d’un programme) à la fois: Le
processeur travaille donc en temps partagé.
• Un processus passe entre les diverses files d’attente pendant sa durée
de vie (file d’attente des processus prêts attendant d’être exécutés,
fille d’attentes des périphériques, ...).Le SE doit sélectionner les
processus à partir de ces files d’attente en suivant une méthode
donnée. Le processus de sélection est mené à bien par le scheduleur
approprié.
l‘ordonnancement c'est la stratégie d’attribution des ressources aux
processus qui en font la demande.
l’ordonancement est exigé lors de:
• La creation et/ou la fin d’un processus;
• Le blocage;
• La prduction d’une interruption.
Les critères d’ordonnancement:
La politique d’ordonnancement doit optimiser les performances du système a
travers:
• Le rendement d’utilisation du CPU: le ratio de temps pendant lequel le CPU
est actif et les processus qui sont traités.
• L’utilisation globale des ressources: assurer une occupation globale maximale
des ressources de la machine et minimiser le temps d’attente pour l’allocation
de ces ressources.
• L’équiter : allouer de façon équitable le CPU a tous les processus de même
priorité.
• Le temps de rotation: la durée moyenne pour traiter tous les processus en
attente .
Algorithme d’ordonnancement
On peut classer les algorithmes d’ordonnancement en deux catégories:
• algorithmes d’ordonnancement préemptifs : l’algorithme sélectionne
un processus puis le laisse s’executer jusqu’à ce qu’il libère
volontairement la ressource ou qu’il se bloque .
• algorithmes d’ordonnancement non-préemptifs: l’algorithme
sélectionne un processus et le laisse s’executer pendant un délai
déterminé.
Algorithmes non-préemptifs:
• Algorithme FIFO (First In First Out):
Les taches sont ordonnancés par ordre d’arrivée, on utilise une
structure de pile. Cette stratégie désavantage les processus courts s’ils
ne sont pas reçus en premiers.
• Algorithme SJF(short Job First): le temps d’execution de chaque
processus est estimé a l’avance. Le processeur est attribué au
processus qui a le plus petit temps d’execution. Cette stratégie
désavantage les processus longs.
Algorithmes préemptifs
• L’algorithme RSJF(Rest Short Job First):
C’est la version préemptive de SJF;
Au début de chaque quantum(intervalle de temps), le processeur est
attribué au processus ayant le plus petit temps d’execution restant.
• L’algorithme de Round-Robin(a tourniquet):
Le contrôle du processeur est attribué a chaque processus pendant un
quantum a tour de rôle. Les performances de cet algorithme
dependent de la valeur du quantum: plus le quantum est petit, plus
l’algorithme est efficace.
Exercice: a l’aide des algorithmes d’ordonnancement
FIFO,SJF,RSJF et ROUND-ROBIN , remplir le tableau
suivant pour un quantum =1.

Id processus Heure d’arrivée Temps Heure début Heure fin Durée de


d’exécution d’exécution d’exécution traitement
estimé
A 1 5
B 2 2
C 3 6
D 4 3
chapitre 3:Gestion de la sécurité dans les SE
Les objectifs de la sécurité:
• intégrité : les données sont bien celles qu’on attend;
• confidentialité : l’acces a chaque ressource est conforme aux
autorisations
• disponibilité : le système reste en fonctionnement, la sécurité
n’empeche pas les utilisateurs légitimes de travailler;
• non répudiation : une transaction ne peut être niée;

Afin de pouvoir sécuriser un système, il faut connaıtre les
menaces :
• exploitation des erreurs de configuration (système ouvert ou
vulnérable) et de programmation (exploits)
dénis de service (mailbombing, flood, exploits, nuke),
potentiellement distribues
• squat (hébergement illégal, attaques par rebond, botnets)

• malware (virus, vers, troyens, espions, backdoors)


• usurpation d’identité (IP/ARP spoofing)
• erreurs humaines ⇒ ne pas être root
• ingénierie sociale : exploitation des erreurs humaines, de la
naïveté, gentillesse, ignorance, etc. Par exemple, le phishing :
faux message de la banque qui redemande le mot de passe
(via une fausse page web) ⇒ identifiée par certains antivirus.
Attaque de Twitter/Yahoo.
Politique de sécurité: Ensemble des droits d’acces aux ressources :

• globale `a une organisation


• définie par la hiérarchie
• mise en œuvre par les informaticiens en ce qui concerne les
ressources informatiques
• prend en compte la formation (utilisation correcte des
ressources, ingénierie sociale)
• le maillon le plus faible définit la force de la chaıne de
sécurité, c’est souvent l’utilisateur lambda
• défense en profondeur (`a plusieurs niveaux) ⇒ une barrière
peut tomber, il en reste d’autres
• simplicité ⇒ efficacité (KISS : keep it simple, stupid)

• conception ouverte : la protection ne doit pas dépendre de


l’ignorance de l’attaquant ⇒ audit (par des experts)

• médiation complète : toute tentative d’acces a une ressource


doit être vérifié

• sureté des comportements par défaut (refus) : tout le monde a


connu l’amer expérience d’un Windows XP qui fraıchement
installé n’a pas le firewall activé ⇒ a peine lancé, il est
victime d’un vers.
• séparations des privilèges : l’acces `a une ressource doit
dépendre de plus d’une condition

• acceptabilité psychologique et simplicité d’utilisation

• nécessaire intégration de la marche a suivre en cas d’attaque


réussie, de compromission des clefs, etc.
Sécurité de la machine:
Chaque accès physique possible représente un risque:
• vol de la machine, de ses disques
• installation de logiciels (malware)
• vol ou consultation de données
• usurpation d’identité
• modification de données
• modification de configuration (mots de passe)
Sécurité physique:
• Rendre la machine fixe
• empêcher son ouverture : Vol des éléments (en particulier les
disques durs) ou retirer la pile du BIOS pour supprimer le mot
de passe.
• empêcher le redémarrage
Sécurité du BIOS:
• mettre un mot de passe
• attention aux mots de passe par défaut
• supprimer le boot sur support amovible
• sur un serveur ⇒ mot de passe au boot
Comptes utilisateurs:
• donner les privilèges minimaux pour l’usage demandé
• interdire la connexion `a certains moments, de certains endroits
• interdire les salles serveurs ou l’installation de logiciel maisons
en binaires dessus
• désactiver les comptes inactifs
• tous les comptes doivent etre individuels
Comptes utilisateurs
• faire des audits réguliers des comptes (Un utilisateur ne
devrait pas avoir de dossier ou fichiers accessibles en ´ecriture `a
tous. Il faut régulièrement chercher les malwares sur son
compte. Il faut contrôler les binaires qu’il installe.) ⇒ la
politique de sécurité
• maintenir des log des activités
• interdire les mots de passe faibles ou par défaut .
Protection des mots de passe
• idéalement, on ne les stocke pas. Mot de passe dans
/etc/shadow (double protection : fichier protégé, mots de
passe hachés)
• sinon, les stocker chiffrés et protégés par les droits d’acces
(ssh ne se lance pas si les droits sont incorrects)
• ne pas autoriser les mots de passe trop simples
• ne jamais faire transiter en clair un mot de passe sur le réseau
(voir capture mot de passe POP sur la feuille)
• ne jamais écrire ni divulguer son mot de passe
garder une clef le moins longtemps possible en mémoire
Outils de protection
• antivirus : protège des virus, vers, etc.
• pare-feu : contrôle les connexions réseaux
• anti-espion : cherche les spywares, backdoors, rootkits, etc. ⇒
comme antivirus
• contrôle d’intégrité : vérifie si le système a été altéré
• détecteur d’intrusions : cherche la présence d’un intrus
Un système d'exploitation doit comporter des moyens et des
procédures de protection des objets qu'il permet de manipuler. Les
objets à protéger appartiennent à deux grandes catégories :
• les objets persistants tels que les fichiers, les périphériques, ..etc.

• les objets temporaires, comme les processus, les espaces de mémoire


crée créés, ..etc.

La protection consiste à empêcher qu'un utilisateur puisse altérer un


fichier qui ne lui appartient pas et dont le propriétaire ne lui en a pas
donné l'autorisation, ou encore à empêcher qu'un processus en cours
d'exécution ne modifie une zone mémoire attribuée à un autre
processus sans l'autorisation de celui-ci, par exemple.
Sommairement, on peut dire que la protection d'un objet informatique
se pose dans les termes suivants, inspirés des concepts mis en œuvre
par certains systèmes d’exploitation, comme Multics et
Linux :
• Un objet (processus, fichier, segment mémoire) a un propriétaire
identifié, généralement l'utilisateur qui l'a créé.
• Le propriétaire d'un objet peut avoir conféré à lui-même et à d'autres
utilisateurs des droits d'accès à cet objet.
• À chaque objet est donc associée une liste de contrôle d'accès (access
control list) qui énumère les utilisateurs autorisés et leurs droits.
• Avant toute tentative d'accès à un objet par un utilisateur, l'identité
de cet utilisateur doit être authentifiée
• Pour qu'un utilisateur ait le droit d'exécuter une action sur un objet,
et dans un système informatique cette action est perpétrée par
l'intermédiaire d'un processus, il faut en outre que le processus en
question possède le pouvoir voulu. Le pouvoir est un attribut d'un
processus, il peut prendre des valeurs qui confèrent à ce processus
des privilèges plus ou moins étendus (mode user, mode supersuer)
• La valeur du pouvoir d'un processus peut changer au cours de son
exécution. Ainsi un processus qui se déroule dans un mode utilisateur
peut faire une demande d'entrée-sortie, ce qui nécessite le mode
superviseur. Ceci sera résolu, sous Unix par exemple, par le
mécanisme de l'appel système, qui transfère le contrôle, pour le
compte du processus utilisateur, à une procédure du noyau qui va
travailler en mode superviseur.
• O définit a notion de domaine de protection dans lequel s'exécute un
processus comme l'ensemble des objets auxquels ce processus a
accès et des opérations qu'il a le droit d'effectuer sur ces objets.
Lorsqu'un processus change de valeur de pouvoir, il change par là
même de domaine de protection.
Les domaines de protection
On définit un droit d’accès comme une paire : (objet / ensemble de
droits). Exemple (01, Read)
On définit un domaine de protection comme un ensemble de droits
d’accès {DA1, DA2, … DAn} .
La Liaison « processus / domaine » peut être statique ou dynamique:
• statique : l’ensemble de ressources disponibles fixe. Le principe de
nécessité d’accès requiert
un mécanisme de modification des contenus de domaines.
Les domaines de protection
• Dynamique : requiert un mécanisme de « commutation de domaine »
(pas nécessairement de
modification).
Un domaine peut être soit : un utilisateur, un processus ou une procédure:
• Domaine = utilisateur
les objets auxquels on peut accéder dépendent de l’utilisateur qui y
accède. La commutation
de domaine est liée au changement d’utilisateur.
Les domaines de protection
• Domaine = processus
les objets auxquels on peut accéder dépendent du processus qui y
accède. La commutation de domaine est liée à la commutation de
contexte.
• Domaine = utilisateur
les objets auxquels on peut accéder correspondent aux variables
utilisées par la procédure. La commutation de domaine est liée à
chaque appel de procédure.
Matrices des droits d’acces
Les matrices des droits d’accès permettent de formaliser le domaine de
protection de chaque
processus.
Les lignes de la matrice représentent les domaines, et les colonnes
représentent les objets.
Exemple:
O1 O2 O3
D1 Lecture exécution
D2 lecture
D3 Lecture/ecriture Lecture/ecriture
Matrices des droits d’acces
L’implémentation des matrices de droits d’accès, peut se faire selon plusieurs méthodes :
La table globale : consiste à représenter la matrice par une table constituée d’un ensemble de
triplets
« domaine, objet, ensemble de droits » . Ainsi , à chaque fois que l’on exécute une opération A sur
un
objet, il faut rechercher dans la table des triplets si A y figure bien . Dans l’affirmative, l’opération
est
autorisée, sinon elle est refusée
• Liste d’accès aux objets : On implémente chaque colonne de la matrice par une liste chainée.
• Liste des domaines : On implémente chaque ligne de la matrice par une liste chainée.
Les problèmes techniques actuels de sécurité informatique découlent
directement ou indirectement de l'essor des réseaux, qui multiplie la
quantité et la gravité des menaces potentielles. Ces menaces peuvent
être : atteinte à la disponibilité des systèmes et des données,
destruction de données, corruption ou falsification de données, vol ou
espionnage de données, usage illicite d'un système ou d'un réseau,
usage d'un système compromis pour attaquer d'autres cibles.
Les personnes qui accèdent à une ressource non publique doivent être
identifiées ; leur identité doit être authentifiée ; leurs droits d'accès
doivent être vérifiés.
La sécurité des accès par le réseau à une ressource protégée n'est pas
suffisamment garantie par la seule identification de leurs auteurs. Sur
un réseau local de type Ethernet ou sur Internet, il est possible à un
tiers de capter la transmission de données. Les données doivent donc
être protégées, grâce aux techniques de chiffrement ou cryptage.
authentification
L'authentification est la procédure qui consiste, pour un système informatique, à
vérifier l'identité d'une entité (personne, ordinateur...), afin d'autoriser l'accès de
cette entité à des ressources (systèmes, réseaux, applications...).
Le protocole d’authentification peut appartenir à l’une des familles suivantes :
• L'authentification simple : l'authentification ne repose que sur un seul élément
ou « facteur »
• L'authentification forte : l'authentification repose sur deux facteurs ou plus.
authentification
• L'Authentification unique : (ou identification unique ; en anglais Single Sign-On ou
SSO) est une méthode permettant à un utilisateur de ne procéder qu'à une seule
authentification pour accéder à plusieurs applications informatiques (ou sites
internet sécurisés).
L’authentification peut se baser sur :
• La vérification d’un « mot de passe » préalablement établi.
• Une « vérification biométrique » (reconnaissance de la voix, empreintes digitales,
l’iris, …etc).
Chiffrement
Le chiffrement, ou cryptage est le procédé grâce auquel on souhaite rendre
la compréhension d'un document impossible à toute personne qui n'a pas
la clé de déchiffrement.
La sécurité d'un système de chiffrement repose plus sur le secret de la clé
de chiffrement que sur
l'algorithme lui-même.
Un système de chiffrement est dit :
• symétrique quand il utilise la même clé pour chiffrer et déchiffrer.
Chiffrement
• asymétrique quand il utilise des clés différentes : une paire composée d'une
clé publique , servant au chiffrement, et d'une clé privée, servant à déchiffrer.
Le point fondamental soutenant cette décomposition publique/privée est
l'impossibilité calculatoire de déduire la clé privée de la clé publique.
L'utilisation d'un système symétrique ou asymétrique dépend des tâches à
accomplir. La cryptographie asymétrique présente deux intérêts majeurs : elle
supprime le problème de transmission sécurisée de la clé, et elle permet la
signature électronique. Elle ne remplace cependant pas les systèmes
symétriques car ses temps de calcul sont nettement plus longs.
Chapitre4: Les protocoles clients/serveurs
Caractéristiques du modèle client/serveur:
Notion de service:
• réalisé par un serveur
• demandé par un client
• définie par une interface (API) entre client et serveur
Communication par messages :
• Requête : paramètre d'appel, spécification du service requis
• Réponse : résultat, indicateur d'exécution/d'erreur
• Synchrone : Client et serveurs sont bloqués
Modèle client/serveur: le partage
• Vu du client
Requête

Client serveur

Réponse
Modèle client/serveur: le partage
• Vu du serveur
Gestion des requêtes (priorité)
Exécution des services
Mémorisation ou non de l’état du client

Requête Traitement Réponse

File des requêtes


Modèle client/serveur: gestion des processus
Client et serveur exécutent des processus distincts
 Mise en œuvre par les primitives TCP/IP
 le client est suspendu (appel synchrone)
 éventuellement plusieurs requêtes peuvent être traitées
concurremment par le serveur
• parallélisme réel (multiprocesseur)
• pseudo-parallélisme
 La concurrence peut prendre plusieurs formes
• plusieurs processus
• plusieurs processus légers dans le même espace virtuel
Modèle client/serveur: protocoles sans état
• Chaque requête est indépendante de la suivante
– http, echo, X11 (à détailler), jdbc/sqlNet, …
• Pas de causalité entre les requêtes
• Pas d'ordre d'arrivé
• Le modèle est résistant aux pannes du client et du
serveur
Modèle client/serveur: protocoles avec état
Certaines requêtes dépendent des autres pour un même
client
– Identification, Echange de clés de cryptage, Sélection de Menus…
• Le serveur doit mémoriser la connexion du client
– Mode connecté : la connexion est identifiée par la socket
– Mode déconnecté : le protocole doit véhiculer l'information
d'identification du client
• Problèmes
– Panne
– Sécurité
Modèle client/serveur: serveurs à états:
pannes
• Panne du client :
– Le serveur peut repérer qu'il s'agit du même client
• Comment ?
– Le client peut "rejouer" toute la session.
• Panne du serveur
– Le serveur doit enregistrer les états du clients
• Notion de journalisation ou de persistance
• Mais il doit pouvoir ré-identifier le client
– Le client peut "rejouer" toute la session
Modèle client/serveur: serveurs avec données
rémanentes
Les requêtes manipulent des données persistantes
accessibles par plusieurs clients
– Exemple base de données
• Banque, Bourse
– L'ordre des requêtes est important :
• Délais,
• Inconsistance des données
– Exemple :
» Crédit --> Débit --> ok
» Débit --> Agios --> Crédit --> ok – agios
Modèle client/serveur: serveurs
interdépendants
Pour fournir une réponse un serveur dépend
d'autre serveurs.
– Nécessité de synchronisation
– Panne des serveurs esclaves
– Transactions, 2PC
Atelier 1: remplir le tableau suivant
Service serveur Numéro de port Applications clientes Application serveur
Transfert d’une page Serveur web
HTML correspondante a
une URL
Envoie et réception de Serveur de
mail messagerie
Transfert de fichier Serveur FTP
Administration distante Serveur SSH
sécurisée d’un serveur
Administration distante Serveur Telnet
non sécurisée d’un
serveur

Fourniture d’une adresse Serveur DNS Resolver client Bind


IP correspondant a un
nom de domaine
Attribution d’une Serveur DHCP Processus client DHCP DHCPD
configuration réseau
Atelier 2: mettre en place les services suivants
DNS
Telnet
Chapitre 5: les scripts de sauvgarde
Le système d’exploitation maintient une arborescence des processus
(pouvant être affichée par pstree) : chaque processus a un processus
parent (sauf le processus de pid 1, processus init qui est le premier
démarré après initialisation du noyau).
Un nouveau processus est créé par fork.
L’appel à fork duplique le processus courant en créant un processus
fils identique au processus courant. Ces deux processus continuent
alors à s’exécuter en parallèle après la ligne de code contenant
l’appel à fork. La valeur retournée par fork permet plus
particulièrement au processus père de connaître l’identité de son fils
(son pid), et de différencier le fils et le père (pour pouvoir faire des
traitements différents dans l’un et dans l’autre).
Création d’un processus:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int variable; pid_t pid;
setbuf(stdout, NULL);
variable=1;
pid = fork();
if (pid != 0) {
variable = 2;
printf("[Père]␣PID␣%d␣-␣Fils␣%d\n", getpid(), pid);
printf("[Père]␣variable:␣%d\n", variable);
sleep(5); }
else {
sleep(1);
printf("[Fils]␣PID␣%d␣-␣Père␣%d\n", getpid(), getppid());
printf("[Fils]␣variable:␣%d\n", variable); }
return 0;
}
L’appel à fork duplique le processus courant : L’espace d’adressage
mémoire est dupliqué, père et fils ont donc une copie des variables
avant l’appel à fork, ces copies peuvent évoluer différemment.
Les descripteurs de fichiers (ouverts avant le fork) sont accessibles
dans le père comme dans le fils, mais repèrent le même fichier
Fin d’exécution d’un processus
Quand un processus se termine, il retourne une valeur à son
processus père. C’est la valeur retournée à la fin du main (ou par
exit), habituellement 0 pour signaler une terminaison normale, ou
une valeur différente de 0 pour signaler une erreur.
Cette valeur peut être lue par le père, le système d’exploitation doit
donc conserver le processus qui s’est terminé (et sa valeur de retour)
tant que la fin du processus et la lecture de la valeur n’ont pas été
prises en compte par le processus père. Le processus terminé est
alors un processus zombie
Fin d’exécution d’un processus
Pour attendre la fin d’exécution d’un processus fils, on peut utiliser :
pid_t wait(int *wstatus); ou
pid_t waitpid(pid_t pid, int *wstatus, int options);
wait est bloquant et attend qu’un quelconque processus fils se
termine, retourne le pid de ce processus et stocke à l’adresse passée
la valeur de retour. WIFEXITED, WEXITSTATUS…permettent de
connaître la façon dont le fils s’est terminé.
waitpid permet d’attendre un processus fils particulier (repéré par
son pid, ou -1 pour attendre n’importe quel fils). Si option vaut 0,
attente, si WNOHANG se termine immédiatement, même si aucun fils ne
s’est terminé.
Remplacement d’un processus
Les fonctions execl, execlp, etc. permettent d’appeler un programme
exécutable en remplaçant le processus courant par un processus
correspondant à l’exécutable. Ces fonctions diffèrent sur la façon de
passer les paramètres à la commande et sur la recherche (ou non) de
la commande dans le PATH :
▶ S’il y a un p, la fonction cherche dans le PATH (on doit donc lui
passer le nom de l’exécutable, par exemple ls). Sinon, pas de
recherche, on doit passer le chemin complet de l’exécutable, par
exemple /usr/bin/ls.
▶ S’il y a un l, on passe les arguments comme à une fonction à
nombre d’arguments variable (puis NULL), s’il y a un v, on passe
un tableau d’arguments (terminé par une valeur NULL).
▶ S’il y a un e, un dernier argument contient les variables
d’environnement à passer. Sinon, le même environnement est
conservé.
Remplacement d’un processus
int main() {
char saisie[200];
setbuf(stdout, NULL);
int cpt;
cpt=0;
do {
printf("MonShell%d>␣", ++cpt);
fgets(saisie,200,stdin);
saisie[strlen(saisie)-1] = 0;
if (strcmp(saisie, "exit") == 0)
break;
else
lancer(saisie);
} while(cpt<100);
return 0;
}
1
Remplacement d’un processus
void lancer(char * cmd) {
char* args[2];
pid_t pid = fork();
if (pid == 0) {
args[0] = cmd;
args[1] = NULL;
execvp(cmd, args);
printf("Commande␣introuvable\n");
exit(1);
}
else
wait(NULL);
}
1
Communication interprocessus
Un signal est un mécanisme asynchrone de communication
interprocessus.
C’est une « interruption logicielle » que peut recevoir un processus. À
la réception d’un signal, l’exécution du programme s’interrompt pour
traiter le signal : S’il n’y a pas de gestionnaire pour ce signal, le
processus se termine. Sinon, le gestionnaire de signal est exécuté et le
programme reprend éventuellement son exécution.
Certains signaux sont envoyés par le système (Segmentation
fault) ou par l’utilisateur (commande kill)
Il est possible d’envoyer des signaux à partir d’un processus. Pour
cela, on peut utiliser la fonction kill (section 2 du man).
Le premier paramètre à passer à kill est le numéro de processus
(pid) à qui le signal doit être envoyé et le second paramètre est le
type de signal à envoyer. Il existe plusieurs types de signaux, chaque
type est identifié par un numéro. Par exemple le signal 2 (SIGINT) est
celui qui est reçu quand l’utilisateur fait un control-C.
Habituellement, on utilise le nom de chaque signal plutôt que son
numéro (voir signal dans la section 7 du man).
envoie du signal
void fils() {
int compteur = 1;
while(1) {
printf("%d\n", compteur++);
sleep(1);
}
}
int main(int argc, char* argv[]) {
pid_t p = fork();
if (p == 0)
fils();
else {
sleep(5);
kill(p, SIGTERM);
wait(NULL);
}
return 0;
}
Configuration du signal
void fils() {
int compteur = 1;
while(1) {
printf("%d\n", compteur++);
sleep(1);
}
}
int main(int argc, char* argv[]) {
pid_t p = fork();
if (p == 0)
fils();
else {
sleep(5);
kill(p, SIGTERM);
wait(NULL);
}
return 0;
}
Gestion d’un ensemble de signaux
int sigemptyset(sigset_t *ens); // Initialiser (vide)
int sigfillset(sigset_t *ens); // Remplir
int sigaddset(sigset_t *ens, int sig); // Ajouter
int sigdelset(sigset_t *ens, int sig); // Supprimer
int sigismember(sigset_t *ens, int sig); // Tester
Bloquer un signal
Dans une section critique du code, on veut parfois interdire la
réception de signaux.
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
▶ how=SIG_BLOCK Ajoute les signaux de set aux signaux bloqués.
▶ how=SIG_UNBLOCK Débloque les signaux de set.
▶ how=SIG_SETMASK Bloque les signaux de set uniquement.
▶ oldset si non NULL contient après l’appel les signaux bloqués
avant l’appel
Installer un gestionnaire de signaux
int sigaction(int signum, const struct sigaction *act, struct
sigaction *oldact);
▶ Déclarer une struct sigaction.
▶ Initialiser à vide le champ sigset_t sa_mask : signaux bloqués
pendant l’exécution du gestionnaire.
▶ Fixer à 0 le champ int sa_flags.
▶ Initialiser le champ sa_handler pour le faire pointer sur la
fonction gestionnaire de signal
gestionnaire
void gestionnaire(int s) {
printf("Réception␣de␣%d\n",s);
}
int main() {
struct sigaction sa;
sa.sa_flags=0;
sa.sa_handler=&gestionnaire;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT,&sa,NULL);
int compteur=0;
while (1) {
sleep(1);
printf("%d\n", compteur++);
};
return 0;
}
1
Le TUBE :
Mécanisme de communication unidirectionnel entre processus
permettant d’échanger un flux d’octets.
Ce qui est lu est « consommé », ce n’est pas un fichier.
Un tube est représenté par deux descripteurs de fichiers : un en
lecture (la sortie du tube), un en écriture (l’entrée du tube).
La lecture et l’écriture dans un tube se font de la façon habituelle :
read et write.
Création d’un tube
int pipe(int pipefd[2]); Après appel, pipefd[1] est un
descripteur de fichier en écriture et pipefd[0] en lecture.
Pour que deux processus puissent communiquer à travers un tube, ils
doivent accéder à ce tube : le tube doit être créé avant le fork, les
descripteurs sont alors hérités dans le père et le fils.
Attention en utilisant
ssize_t read(int fd, void *buf, size_t count); :
▶ buf doit pointer sur une zone effectivement réservée (de taille
count octets).
▶ Retourne le nombre d’octets lus, pas forcément count.
▶ Si plus rien à lire (parce que tous les descripteurs d’écriture du
tube ont été fermés), retourne 0.
Attention en utilisant write : L’écriture dans un tube qui n’a pas de
lecteur provoque un signal SIGPIPE
tube
void producteur(int fd) {
int i, r;
for (i=0; i<100; ++i) {
r = rand() % 100 + 1;
write(fd, &r, sizeof(r));
sleep(1);
}
}
void consommateur(int fd) {
int i, v;
for (i=0; i<100; ++i) {
read(fd, &v, sizeof(int));
printf("%d\n", v);
}
}
tube
int main() {
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if (pid == 0) {
close(fd[0]);
producteur(fd[1]);
}
else {
pid = fork();
if (pid == 0) {
close(fd[1]);
consommateur(fd[0]);
}
}
return 0;
}
La lecture dans un tube (read) est par défaut bloquante : Si rien n’est
disponible, read attend que des octets soient disponibles pour se
terminer.
Pour rendre une lecture dans un descripteur de fichier non bloquante,
il faut utiliser :
int fcntl(int fd, int cmd, ... /* arg */ );
▶ si cmd=F_SETFL, l’argument suivant doit être un ensemble de
flags qui s’appliquent au descripteur fd. Le flag O_NONBLOCK fait
passer le descripteur en mode non-bloquant.
▶ si cmd=F_GETFL, fcntl retourne l’état actuel des flags.
int flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
Tube nommé
n tube permet la communication entre processus issus du même
exécutable.
Le même type de communication peut être utilisé entre des processus
issus d’exécutables différents en utilisant les tubes nommés.
Un tube nommé est identifié dans le système de gestion de fichiers
par un nom, il est créé par la commande mkfifo (section 1 du man) ou
l’appel système mkfifo (section 3 du man).
L’écriture ou la lecture dans un tube nommé se fait comme dans un
tube, sur un descripteur de fichier. Ce descripteur est obtenu par
open, qui est bloquante jusqu’à ce que l’autre extrémité soit ouverte.
Tube nommé
int main() {
char * nomfifo = "/tmp/fifo_nombres";
mkfifo(nomfifo, S_IRUSR | S_IWUSR);
int fd = open(nomfifo, O_WRONLY);
for (;;) {
long int r = random();
printf("Tirage␣:␣%ld\n", r);
write(fd, &r, sizeof(r));
sleep(1);
}
return 0;
}
Tube nommé
int main() {
char * nomfifo = "/tmp/fifo_nombres";
int fd = open(nomfifo, O_RDONLY);
for (;;) {
long int r;
if (read(fd, &r, sizeof(r)) == 0)
break;
printf("Lecture␣:␣%ld\n", r);
sleep(1);
}
return 0;
}

Vous aimerez peut-être aussi