Programmation système en C/C++ - Bienvenue sur le site de Jean

Transcription

Programmation système en C/C++ - Bienvenue sur le site de Jean
gcc
Entrées et sorties
Accès aux fichiers
Processus
Programmation système en C/C++
Jean-Baptiste Vioix
([email protected])
LE2I - ENESAD
http://jb.vioix.free.fr
1-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Programmation en C/C++ sous Unix
Il existe des très nombreux outils de développement sous
Linux (KDevelop, Anjuta, ...).
Ils utilisent tous le compilateur gcc,
http://jb.vioix.free.fr
2-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Programmation en C/C++ sous Unix
gcc s’appelle simplement sur la ligne de commande avec le
nom du(des) fichier(s) source(s) (extension .c) comme
paramètre(s).
Après compilation, on obtient un fichier exécutable nommé,
par défaut, a.out
L’option -o permet de changer le nom du fichier crée :
gcc -o programme programme.c
http://jb.vioix.free.fr
3-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Programmation en C/C++ sous Unix
Pour programmer en C++, le fichier doit avoir une extension
compatible (comme .CC).
De plus il faut indiquer à gcc les bibliothèques C++ avec
l’option -l
En pratique, on utilise g++ qui permet de lier
automatiquement les bonnes librairies.
La syntaxe est la même que gcc
http://jb.vioix.free.fr
4-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Entrée standard
En C, la sortie standard est lue avec la fonction scanf.
En C++, la sortie standard est lue avec le flux cin
#include<iostream.h>
void main(){
char chaine[100];
cin>>chaine;
}
http://jb.vioix.free.fr
5-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Sortie standard et sortie des erreurs en C
En C on accède à la sortie standard via la fonction printf.
Pour accéder à la sortie des erreurs, on doit passer par le
fichier stderr via fprintf
Ces fonctions sont dans la bibliothèque stdio.
#include<stdio.h>
void main(){
printf("Sortie standard \n");
fprintf(stderr,"Sortie des erreurs \n");
}
http://jb.vioix.free.fr
6-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Sortie standard et sortie des erreurs en C++
En C++ les sorties sont gérées par des flux.
Le flux cout gère la sortie standard.
Le flux cerr gère la sortie des erreurs.
Ces fonctions sont dans la bibliothèque iostream.
#include<iostream.h>
void main(){
cout<<"Sortie standard \n";
cerr<<"Sortie des erreurs \n";
}
http://jb.vioix.free.fr
7-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Valeur de retour
Selon la norme UNIX un programme doit renvoyer un entier
compris entre 0 et 255 après son exécution.
Cette valeur vaut 0 si le déroulement du programme est
normal, une autre valeur sinon.
http://jb.vioix.free.fr
8-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Valeur de retour
Pour renvoyer une valeur de retour il faut définir la fonction
main comme renvoyant un int
La valeur de retour du programme est alors définie en utilisant
l’instruction return
#include<iostream.h>
int main(){
cout<<"Sortie standard \n";
cerr<<"Sortie des erreurs \n";
return 0;
}
http://jb.vioix.free.fr
9-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Valeur de retour
On peut aussi quitter le programme de manière plus ”brutale”
en utilisant l’instruction exit(int )
Le paramètre correspond à la valeur de retour.
Dans ce cas, la valeur de retour doit être différente de 0
(sortie anormale du programme).
#include<iostream.h>
int main(){
cout<<"Un texte avant exit \n";
exit(1);
cout<<"Un texte après exit\n";
return 0;
}
http://jb.vioix.free.fr
10-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Passage de paramètres
Le passage de paramètres est possible avec des programmes
écrits en C/C++.
La fonction main doit alors avoir le prototype suivant :
int main (int , char* )
La première variable correspond au nombre de paramètre sur
la ligne de commande.
La seconde est un tableau de chaı̂nes de caractères contenant
les paramètres.
http://jb.vioix.free.fr
11-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Passage de paramètres
Lecture des paramètres :
#include<iostream.h>
int main(int argc, char* argv[]){
cout<<"Nombre de paramètres : "<<argc<<endl;
for(int i=0;i<argc;i++)
{
cout<<i<<" "<<argv[i]<<endl;
}
return 0;
}
http://jb.vioix.free.fr
12-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Accès aux variables d’environnement
Les variables d’environnement permettent d’accéder à certains
paramètres du système (comme la langue, le shell, ...).
La fonction char* getenv(char* ) permet de lire une
variable, passée en paramètre.
#include<iostream.h>
int main(int argc, char* argv[]){
char* var;
var = getenv("SHELL");
cout<<"Le shell est : "<<var<<endl;
return 0;
}
http://jb.vioix.free.fr
13-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Entrée standard
Sortie standard et sortie des erreurs
Valeur de retour
Passage de paramètres
Accès aux variables d’environnement
Accès aux variables d’environnement
On peut créer ou modifier une variable d’environement à
l’aide de int setenv(char* , char*, int)
Le premier paramètre est le nom de la variable, le second sa
valeur, le troisième permet de forcer la mise à jour (s’il vaut
1) ;
Cette fonction renvoie 0 en cas de reussite, -1 sinon.
#include<iostream.h>
int main(int argc, char* argv[]){
int res;
res = setenv ("UTILISATEUR","IQS",1);
return 0;
}
http://jb.vioix.free.fr
14-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Accès bas niveaux
La norme Unix propose différents niveaux d’accès aux fichiers.
Les fonctions de bas niveaux permettent :
d’ouvrir (ou créer) un fichier,
de lire et d’écrire des octects dans un fichier,
de se déplacer dans un fichier,
de fermer un fichier.
Les fichiers sont associés à un descripteur (de type int). Les
descripteurs 0,1,2 sont respectivement l’entrée standard, la
sortie standard, la sortie des erreurs.
http://jb.vioix.free.fr
15-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Ouverture d’un fichier
Pour associer un fichier à un descripteur on utilise la fonction
open.
La fonction renvoie un descripteur en cas de succès, -1 en cas
d’échec.
La fonction utilise deux paramètres : le nom du fichier, les
modes d’ouvertures.
Les modes d’ouvertures sont des entiers mais les bibliothèques
permettent d’utiliser des équivalents
int open(char* , int flags)
http://jb.vioix.free.fr
16-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Ouverture d’un fichier : options d’ouverture
Les modes basiques sont :
O RDONLY : ouverture en lecture seule,
O WRONLY : ouverture en écriture seule,
O RDWR : ouveture en lecture/écriture.
D’autres options sont possibles comme, par exemple :
O APPEND :écriture en fin de fichier ;
O TRUNC : vide le fichier avant d’écrire ;
O CREAT : création du fichier.
On combine les modes avec l’opérateur OU logique.
http://jb.vioix.free.fr
17-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Ouverture d’un fichier : options de création
Lors de la création d’un fichier, il convient d’ajouter les droits
d’accès au fichier.
Dans ce cas, la fonction open admet trois arguments :
int open(char*, int flags, mode t mode)
Les pricipaux droits sont :
S IRUSR, S IRGRP, S IROTH : lecture pour l’utilisateur, le
groupe, les autres ;
S IWUSR, S IWGRP, S IWOTH : écriture pour l’utilisateur, le
groupe, les autres ;
S IXUSR, S IXGRP, S IXOTH : exécution pour l’utilisateur, le
groupe, les autres ;
S IRWXU, S IRWXG, S IRWO : tous les droits pour
l’utilisateur, le groupes, les autres.
Les droits d’accès peuvent aussi être écrits en octal (0600).
http://jb.vioix.free.fr
18-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Fermeture d’un fichier
Un fichier ouvert doit être fermé avant de quitter le
programme.
La fonction de fermeture ne nécessite que le descripteur du
fichier.
int close(int fd)
La fonction renvoie 0 en cas de succès, -1 en cas d’échec.
http://jb.vioix.free.fr
19-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Exemple simple
#include<iostream.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, char* argv[]){
int fd;
fd = open("Fichier.txt", O_RDONLY);
...
close(fd);
return 0;
}
http://jb.vioix.free.fr
20-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Lecture dans un fichier
La fonction int read(int fd, void* buffer, int n)
permet de lire n octects depuis la position actuelle dans le
fichier fd et les place dans buffer.
La valeur de retour correspond au nombre d’octects lus, 0
quand la fin du fichier est atteinte, -1 en cas d’erreur.
http://jb.vioix.free.fr
21-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Ecriture dans un fichier
La fonction int write(int fd, void* buffer, int n)
permet d’écrire n octects provenant de buffer dans le fichier
fd depuis la position actuelle .
La valeur de retour correspond au nombre d’octects écrits, -1
en cas d’erreur.
http://jb.vioix.free.fr
22-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Exemple simple
#include<iostream.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc, char* argv[]){
int fd;
int nb_lus, nb_ecrits;
char buffer[10];
...
nb_lus = read(fd, buffer, 10);
...
nb_ecrits = write(fd, buffer, 10);
...
return 0;
}
http://jb.vioix.free.fr
23-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Redirection des descripteurs
Il est possible de rediriger un descripteur existant vers un
autre descripteur.
La fonction int dup2 (int oldfd, int newfd) permet
d’associer le descripteur newfd au fichier pointé par oldfd.
Après l’éxecution de la fonction, les deux descripteurs
pointeront vers le fichier pointé par olfdfd.
http://jb.vioix.free.fr
24-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Accès bas niveaux
Accès à des données formatées
Accès à des données formatées
En C, la bibliothèque stdio.h fournie de nombreuses
fonctions facilitant l’accès aux données.
En C++ les objets fstream (dans la bibliothèque fstream.h)
permettent de manipuler les données contenues dans des
fichiers.
Les interfaces graphiques (Gnome, KDE, ...) proposent des
fonctions particulières pour accéder aux fichiers (via leurs
librairies associées gtk et Qt entre autres).
Les autres languages font appel aux fonctions bas niveaux vu
précédement pour traiter les fichiers.
http://jb.vioix.free.fr
25-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Définitions (1)
Un programme est fichier exécutable stocké sur une mémoire
de masse.
Un processus est un programme en cours d’exécution.
Chaque processus dispose d’un espace mémoire réservé.
Le système d’exploitation exécute les processus à tour de rôle
jusqu’à ce qu’ils soient terminés.
Un processus peut créer d’autres processus, nommés processus
fils.
http://jb.vioix.free.fr
26-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Définitions (2)
Un processus est reconnu par le système à l’aide de son PID
(Processus Id.).
Il appartient à un propriétaire (UID) et à un groupe (GID).
Les processus peuvent communiquer entre eux à l’aide de
signaux et échanger des données via des tubes (pipe).
Au démarage du système, le programme init est lancé, tous
les processus descendent (directement ou non) de lui.
http://jb.vioix.free.fr
27-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Commandes du shell associées
Lorsque l’on exécute une commande dans le shell, le processus
shell est mis en veille jusqu’à ce que la commande s’achève.
Si on fait suivre le nom de & on crée un nouveau processus
qui s’exécute en arrière plan.
Lorque l’on utilise un pipe | les deux processus sont exécutés
en arrière plan et communiquent via le pipe.
http://jb.vioix.free.fr
28-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Commandes du shell associées
La commande ps permet d’obtenir la liste des processus.
Par défaut, la commande affiche la liste des processus
appartenant à l’utilisateur exécutés dans la fenêtre courante.
L’option -u<nom> permet d’afficher tous les processus
appartenant à l’utilisateur <nom>.
On obtient le numéro de PID, le terminal d’exécution, le
temps d’exécution et la commande d’exécution.
L’option -H permet d’obtenir la hierarchie des processus.
Rappel : la commande id permet d’obtenir le nom de
l’utilisateur et son groupe sous formes litérales et numériques.
http://jb.vioix.free.fr
29-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Indentifiants des processus
La fonction int getuid() renvoie le numéro de UID du
processus en cours (c.-à-d. du programme en cours).
La fonction int getgid renvoie le numéro de GID du
processus en cours.
Les fonctions int getpid et int getppid renvoient le
numéro du processus en cours ainsi que le numéro de
processus du processus père.
http://jb.vioix.free.fr
30-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Création de processus
La fonction int fork() permet de créer un nouveau
processus.
Ce processus hérite de toutes les variables de son père mais les
variables ne sont pas partagées.
La fonction fork renvoie le numéro du processus fils dans le
processus père et renvoie 0 dans le processus fils.
En cas d’échec, le processus fils n’est pas crée et la fonction
renvoie -1.
http://jb.vioix.free.fr
31-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Création de processus : exemple
int main(int argc, char* argv[]){
int n;
...
// Processus père
...
n=fork();
if ( n ==0 ){
//Processus fils ...
}else{
// Suite du processus père ...
}
....
return 0;
}
http://jb.vioix.free.fr
32-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Exécution de processus
La famille des fonctions exec permettent d’exécuter un
programme passé en paramètre dans le processus courant.
Lorsque le programme se termine, le processus est détruit.
La fonction int execlp(char* nom, char* arg0, ...,
char* arg n, ..., NULL) permet d’exécuter le programme
nom avec les paramètres arg0 à arg n (par convention,
l’argument arg0 doit correspondre au nom du fichier, donc à
l’argument nom).
Si la fonction reussit, toutes les instructions qui la suivent ne
seront pas exécutées.
Usuellement, on utilise fork pour créer un processus fils dans
lequel un appel à execlp va exécuter une commande.
http://jb.vioix.free.fr
33-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Mise en pause de processus
Plusieurs fonctions permettent de mettre en pause un
processus.
La fonction sleep (int n) met en pause le processus
pendant n secondes.
Un processus père peut attendre la fin de l’execution de l’un
de ces fils à l’aide de la fonction int wait(int* status).
La fonction renvoie alors le PID du processus qui vient de se
terminer.
La variable status contient alors un code de retour qui décrit
les conditions dans lesquelles le processus s’est terminé. La
variable peut être mise à NULL pour simplifier l’usage.
La fonction int waitpid(int pid, int* status, int
options) permet d’attendre la fin d’un processus précis.
http://jb.vioix.free.fr
34-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (1)
Les processus peuvent se communiquer deux types
d’informations : des données ou des signaux.
Les données peuvent transiter par un fichier temporaire
(eventuellement en utilisant les redirections à l’aide de dup2).
Une approche plus ”unixienne” consiste à utiliser un pipe.
La fonction pipe(int fd[2]) permet de créer un tube de
communication.
Le tableau passé en paramètre contient alors deux descripteurs
de fichier.
On lit dans le descripteur fd[0] et on écrit dans le
descripteur fd[1].
http://jb.vioix.free.fr
35-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (2)
Les processus peuvent se transmettre des signaux. Les signaux
correspondent à des messages simples sans transmission de
données.
Il existe un nombre fini de signaux, la liste est donnée par la
commande kill -l en shell.
La plupart des signaux sont définis par le système, toutefois
quelques signaux sont disponibles pour les utilisateurs
(signaux SIGUSRn).
http://jb.vioix.free.fr
36-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (2)
Nom du signal
SIGINT
SIGFPE
SIGKILL
SIGSEGV
SIGALRM
SIGTERM
SIGSTOP
http://jb.vioix.free.fr
Numéro
2
8
9
11
14
15
19
Description
Signal d’interruption (comme CTRL+C).
Erreur de calcul (par ex. : division par zéro).
Demande d’arrêt du processus.
Ecriture ou lecture à une adresse illégale.
Lié à la fonction alarm()
Signal de fin (émis par défaut par la commande kill).
CTRL+Z (ne peut pas être intercepté).
37-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (3)
En bash la commande kill permet d’envoyer un signal à un
ou des processus précis. Cette commande est souvent utilisée
pour terminer (signal SIGKILL) un processus.
En C, la fonction int kill(int pid, int sig) permet
d’envoyer le signal sig, au processus référencé pid.
Un programme peut avoir trois comportements possibles
lorsqu’il reçoit un signal : soit il l’ignore, soit il utilise le
comportement par défaut, soit il effectue une tâche
particulière.
http://jb.vioix.free.fr
38-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (4)
Si on souhaite modifier le comportement du programme lors
de la réception d’un signal, on utilise la fonction signal(int
sig, sig hdl handler)
La variable sig correspond au numéro du signal (les
équivalences sont reconnues). Les signaux SIGKILL et
SIGSTOP ne peuvent pas être redéfinis.
La variable handler permet de définir le comportement à
adopter :
SIG IGN, le signal est ignoré.
SIG DFL, le signal retrouve son comportement par défaut.
Le nom d’une fonction de prototype void fonction(int ).
Cette fonction sera appelée à chaque réception du signal. Elle
recevra le numéro du signal en paramètre (une même fonction
peut donc traiter des signaux différents).
http://jb.vioix.free.fr
39-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Communication entre processus (4)
...
void signal_recu(int s){
cout<<"Signal recu : "<<s<<endl;
exit(0);
}
...
int main(){
...
signal(SIGINT, signal_recu);
while(1){
}
...
}
http://jb.vioix.free.fr
40-41
gcc
Entrées et sorties
Accès aux fichiers
Processus
Définitions
Commandes du shell associées
Fonctions C associées
Partage de ressources
Les sémaphores sont des mécanismes de synchronisation entre
les processus.
Un sémaphore est une variable entière positive ou nulle.
Les deux opérations principales sont nommées P (proberen) et
V(verhogen).
L’opération P consiste à prendre un jeton. Si la valeur du
semaphore est positive, on la décrémente et le processus
s’exécute ; sinon, le processus est placé en attente jusqu’à ce
que la valeur du semaphore devienne positive.
L’opération V rend un jeton. On peut déposer des jetons à
l’avance sur un sémaphore.
Le système de gestion des sémaphores est très complet (et
complexe) sous Unix. Le TP suivant va permettre de mettre
en place un système simple de sémaphore.
http://jb.vioix.free.fr
41-41

Documents pareils