374-programmationsysteme

81
Programmation Système (en C sous linux) Rémy Malgouyres LIMOS UMR 6158, IUT département info Université Clermont 1, B.P. 86 63172 AUBI

Upload: bestcourse4ucom

Post on 22-Nov-2015

62 views

Category:

Documents


2 download

TRANSCRIPT

  • Programmation Systme (en C sous linux)

    Rmy MalgouyresLIMOS UMR 6158, IUT dpartement info Universit Clermont 1, B.P. 86 63172 AUBIERE cedex. http://www.malgouyres.fr/

  • Une version PDF de ce document est tlchargeable sur mon siteweb, ansi que la version html.

    2

  • Table des matires

    1 Arguments dun programme et variables denvironnement 51.1 atoi, sprinf et sscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.2 Arguments du main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.3 Variables denvironnement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

    2 Processus 102.1 Processus, PID, UID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2 La fonction fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.3 Terminaison dun processus ls . . . . . . . . . . . . . . . . . . . . . . . . . . 122.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

    3 Lancement dun programme : exec 153.1 Rappels : Arguments en ligne de commande . . . . . . . . . . . . . . . . . . . 153.2 Lappel systme exec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.3 La fonction system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.4 Applications suid et problmes des scurit lis system, execlp ou exevp . . . 193.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

    4 Communication entre processus 214.1 Tubes et fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.2 Transmettre des donnes binaires . . . . . . . . . . . . . . . . . . . . . . . . . 234.3 Rediriger les ots dentres-sorties vers des tubes . . . . . . . . . . . . . . . . 244.4 Tubes nomms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

    5 Threads Posix 275.1 Pointeurs de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.2 Thread Posix (sous linux) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305.3 Donne partages et exclusion mutuelle . . . . . . . . . . . . . . . . . . . . . . 325.4 Smaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

    6 Gestion du disque dr et des chiers 426.1 Organisation du disque dur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426.2 Obtenir les informations sur un chier en C . . . . . . . . . . . . . . . . . . . 476.3 Parcourir les rpertoires en C . . . . . . . . . . . . . . . . . . . . . . . . . . . 486.4 Descripteurs de chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

    1

  • TABLE DES MATIRES

    7 Signaux 517.1 Prliminaire : Pointeurs de fonctions . . . . . . . . . . . . . . . . . . . . . . . 517.2 Les principaux signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537.3 Envoyer un signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537.4 Capturer un signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

    8 Programmation rseaux 608.1 Adresses IP et MAC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608.2 Protocoles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618.3 Services et ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628.4 Sockets TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638.5 Crer une connection client . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698.6 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

    A Compilation spare 74A.1 Variables globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74A.2 Mettre du code dans plusieurs chiers . . . . . . . . . . . . . . . . . . . . . . . 75A.3 Compiler un projet multichiers . . . . . . . . . . . . . . . . . . . . . . . . . . 77

    2

  • Introduction

    Ce cours porte sur lutilisation des appels systme des systmes de la famille Unix : Linux,MacOS X, AIX, LynxOS, BeOS, QNX, OpenBSD, FreeBSD,NetBSD.

    Le rle dun systme est de (vois la gure 1) :

    Grer le matriel : Masquer le matriel en permettant aux programmes dintragir avec le matriel travers des pilotes ;

    Partager les ressources entre les dirents programmes en cours dexcution (pro-cessus).

    Fournir une interfaces pour les programmes (un ensemble dappels systme) Fournir une interfaces bas niveau pour lutilisateur (un ensemble de commande shell) Eventuellement une interface utilisateur haut niveau par un environnement graphique(kde, gnome)...

    Figure 1: Schma dun systme dexploitation de type Unix

    La norme POSIX (Portable Operating System Interface uniX) est un ensemble de standardsde lIEEE (Institute of Electrical and Electronics Engineers). POSIX dnit notamment :

    Les commandes shell de base (ksh, ls, man, ...)

    3

  • TABLE DES MATIRES

    LAPI (Application Programming Interface) des appels systme. LAPI des threads

    4

  • Chapitre 1Arguments dun programme etvariables denvironnement

    Nous allons voir dans ce chapitre les passages darguments et variables denvironnement quipermettent un shell de transmettre des informations un programme quil lance. Plus g-nralement, ces techniques permettent un programme de transmettre des informations auxprogrammes quil lance (processus ls ou descendants).

    1.1 atoi, sprinf et sscanfParfois, un nombre nous est donn sous forme de chane de caractre dont les caractres sontdes chires. Dans ce cas, la fonction atoi permet de raliser la conversion dune chane versun int.

    #include

    int main(){

    int a;char s[50];

    printf("Saisissez des chiffres : ");scanf("%s", s); \com{saisie d'une chane de caractres}a = atoi(s); \com{conversion en entier}printf("Vous avez saisi : %d\n", a);return 0;

    }

    Plus gnralement, la fonction sscanf permet de lire des donnes formates dans une chanede caractre (de mme que scanf permet de lire des donnes formates au clavier ou fscanfdans un chier texte).

    #include

    int main()

    5

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    {float x;char s[50];

    printf("Saisissez des chiffres (avec un point au milieu) : ");scanf("%s", s); /* saisie d'une chane de caractres */sscanf(s, "%f", &x); /* lecture dans la chane */printf("Vous avez saisi : %f\n", x);return 0;

    }

    Inversement, la fonction sprintf permet dcrire des donnes formates dans une chanede caractres (de mme que printf permet dcrire dans la console ou fprintf dans un chiertexte).

    #include

    void AfficheMessage(char *message){

    puts(message);}

    int main(){

    float x;int a;

    printf("Saisissez un entier et un rel : ");scanf("%d %f", &a, &x);sprintf(s, "Vous avez tap : a = %d x = %f", a, x);AfficheMessage(s);return 0;

    }

    1.2 Arguments du mainLa fonction main dun programme peut prendre des arguments en ligne de commande. Parexemple, si un chier monprog.c a permis de gnrer un excutable monprog la compilation,

    gcc monprog.c -o monprog

    on peut invoquer le programme monprog avec des arguments

    ./monprog argument1 argment2 argument3

    Exemple. La commande cp du bash prend deux arguments :$ cp nomfichier1 nomfichier2

    6

  • Chapitre 1 : Arguments dun programme et variables denvironnement

    Pour rcuprer les arguments dans le programme C, on utilise les paramtres argc et argvdu main. Lentier argc donne le nombre darguments rentrs dans la ligne de commande plus1, et le paramtre argv est un tableau de chanes de caractres qui contient comme lments :

    Le premier lment argv[0] est une chane qui contient le nom du chier executable duprogramme ;

    Les lments suivants argv[1], argv[2], etc... sont des chanes de caractres qui contiennentles arguments passs en ligne de commande.

    Le prototype de la fonction main est donc :

    int main(int argc, char**argv);

    Exemple. Voici un programme longeurs, qui prend en argument des mots, et ache la longueurde ces mots.

    #include #include

    int main(int argc, char**argv){

    int i;printf("Vous avez entr %d mots\n", argc-1);puts("Leurs longueurs sont :");for (i=1 ; i

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    qui sont disponibles pour tous les processus du systme, y compris les shells. Dans un shell, onpeut avoir la liste des varialbles denvironnement par la commande env. Par exemple, pour lavariable denvironnement PATH qui contient la liste des rpertoires o le shell va chercher lescommandes excutables :

    $ echo PATH/usr/local/bin:/usr/bin:/bin:/usr/bin/X11$ PATH=PATH:.$ export PATH$ env | grep PATHPATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:.

    la commande export permet de transmettre la valeur dune variable denvironnement auxdescendants dun shell (programmes lancs partir du shell).

    1.3.2 Accder aux variables denvironnement en CDans un programme C, on peut accder la liste des variables denvironnement dans la variableenviron, qui est un tableau de chanes de caractres (termin par un pointeur NULL pourmarquer la n de la liste).

    #include

    extern char **environ;

    int main(void){

    int i;for (i=0 ; environ[i]!=NULL ; i++)

    puts(environ[i]);return 0;

    }

    Pour accder une variable denvironnement particulire partir de son nom, on utilise lafonction getenv, qui prend en paramtre le nom de la variable et qui retourne sa valeur sousforme de chane de caractre.

    #include #include /* pour utiliser getenv */

    int main(void){

    char *valeur;valeur = getenv("PATH");if (valeur != NULL)

    printf("Le PATH vaut : %s\(\backslash\)n", valeur);valeur = getenv("HOME");if (valeur != NULL)

    8

  • Chapitre 1 : Arguments dun programme et variables denvironnement

    printf("Le home directory est dans %s\(\backslash\)n", valeur);return 0;

    }

    Pour assigner une variable denvironnement, on utilise la fonction putenv, qui prend enparamtre une chane de caractre. Notons que la modication de la variable ne vaut que pourle programme lui-mme et ses descendants (autres programmes lancs par le programme), etne se transmet pas au shell (ou autre) qui a lanc le programme en cours.

    #include #include /* pour utiliser getenv */

    int main(void){

    char *path, *home, *nouveaupath;char assignation[150];path = getenv("PATH");home = getenv("HOME");printf("ancien PATH : %s\net HOME : %s\n",

    path, home);sprintf(assignation, "PATH=%s:%s/bin", path, home);putenv(assignation);nouveaupath = getenv("PATH");printf("nouveau PATH : \n%s\n", nouveaupath);return 0;

    }

    Exemple de trace :

    $ gcc putenv.c -o putenvecho PATH$ /usr/local/bin:/usr/bin:/bin:/usr/bin/X11$ ./putenvancien PATH : /usr/local/bin:/usr/bin:/bin:/usr/bin/X11et HOME : /home/remynouveau PATH :/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/home/remy/binecho PATH/usr/local/bin:/usr/bin:/bin:/usr/bin/X11

    9

  • Chapitre 2

    Processus

    2.1 Processus, PID, UID2.1.1 Processus et PIDChaque programme (chier excutable ou script shell,Perl) en cours dexcution dans le systmecoorespond un (ou parfois plusieurs) processus du systme. Chaque processus possde unnumro de processus (PID).

    Sous unix, on peut voir la liste des processus en cours dexcution, ainsi que leur PID, parla commande ps, qui comporte direntes options.

    Pour voir ses propres processus en cours dexcution on peut utiliser le commande

    $ ps x

    Pour voir lensemble des processus du systme, on peut utiliser la commande

    $ ps -aux

    Pour voir lensemble des attributs des processus, on peut utiliser loption -f. Par exemple,pour lensemble des attributs de ses propres processus, on peut utiliser

    $ ps -f x

    Un programme C peut accder au PID de son instance en cours dexcusion par la fonctiongetpid, qui retourne le PID :

    pid_t getpid(\void);

    Nous allons voir dans ce chapitre comment un programme C en cours dexcution peutcrer un nouveau processus (fonction fork), puis au chapitre suivant comment un programmeC en cours dexcution peut se faire remplacer par un autre programme,tout en gardant lemme numro de processus (fonction exec). Lensemble de ces deux fonction permettra unprogramme C de lancer un autre programme. Nous verrons ensuite la fonction system, quipermet directement de lancer un autre programme, ainsi que les problmes de scurit lis lutilisation de cette fonction.

    10

  • Chapitre 2 : Processus

    2.1.2 Privilges, UID,Set-UIDChaque processus possde aussi un User ID, not UID, qui identie lutilisateur qui a lancle processus. Cest en fonction de lUID que le processus se voit accord ou refuser les droitsdaccs en lecture, critue ou excution certains chiers ou certaines commandes. On cxeles droits daccs dun chier avec la commande chmod. Lutilisateur root possde un UID gal 0. Un programme C peut accder lUID de son instance en cours dexcution par la fonctiongetuid :

    uid_t getuid(\void);

    Il existe une permission spciale, uniquement pour les excutables binaires, appele la per-mission Set-UID. Cette permission permet un ustilisateur ayant les droits en excution sur lechier dexcuter le chier avec les privilge du propritaire du chier. On met les droitsSet-UID avec chmod +s.

    $ chmod +x fichier$ ls -l-rwxr-xr-x 1 remy remy 7145 Sep 6 14:04 fichier$ chmod +s fichier-rwsr-sr-x 1 remy remy 7145 Sep 6 14:05 fichier

    2.2 La fonction forkLa fonction fork permet un programme en cours dexcution de crer un nouveau processus.Le processus dorigine est appel processus pre, et il garde son PID, et le nouveau processuscr sappelle processus ls, et possde un nouveau PID. Le processus pre et le processus lsont le mme code source, mais la valeur retourne par fork permet de savoir si on est dans leprocessus pre ou ls. Ceci permet de faire deux choses direntes dans le processus pre etdans le processus ls (en utilisant un if et un else ou un switch), mme si les deux processuson le mme code source.

    La fonction fork retourne -1 en cas derreur, retourne 0 dans le processus ls, et retournele PID du ls dans le processus pre. Ceci permet au pre de connatre le PID de son ls.

    #include #include #include

    int main(void){pid_t pid_fils;

    pid_fils = fork();if (pid_fils == -1){puts("Erreur de cration du nouveau processus");exit (1);

    }

    11

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    if (pid_fils == 0){printf("Nous sommes dans le fils\n");/* la fonction getpid permet de connatre son propre PID */printf("Le PID du fils est \%d\n", getpid());/* la fonction getppid permet de connatre le PPID(PID de son pre) */printf("Le PID de mon pre (PPID) est \%d", getppid());

    }else{printf("Nous sommes dans le pre\n");printf("Le PID du fils est \%d\n", pid_fils);printf("Le PID du pre est \%d\n", getpid());printf("PID du grand-pre : \%d", getppid());

    }return 0;

    }

    2.3 Terminaison dun processus lsLorsque le processus ls se termine (soit en sortant du main soit par un appel exit) avant leprocessus pre, le processus ls ne disparat pas compltement, mais devient un zombie. Pourpermettre un processus ls ltat de zombie de disparatre compltement, le processus prepeut appeler linstruction suivante qui se trouve dans la bibliothque sys/wait.h :

    wait(NULL);

    Cependant, il faut prendre garde lappel de wait est bloquant, cest dire que lorsque lafonction wait est appele, lexcution du pre est suspendue jusqu ce quun ls se termine.De plus, il faut mettre autant dappels de wait quil y a de ls. La fonction wait renvoiele code derreur 1 dans le cas o le processus na pas de ls.

    La fonction wait est frquement utilise pour permettre au processus pre dattendre la nde ses ls avnt de se terminer lui-mme, par exemple pour rcuprer le rsultat produit par unls.

    Il est possible de mettre le processus pre en attente de la n dun processus ls particulierpar linstruction

    waitpid(pid_fils, NULL, 0);

    Le paramtre de la fonction wait, et de deuxime paramtre de waitpid est un passage paradresse dun entier qui donne des informations sur le statut du ls lorsquil se termine : termi-naison normale, avortement (par exemple par ctrl-C) ou processus temporairement stopp).Exemple. Voici un exemple o le pre rcupre le code renvoy par le ls dans la fonctionexit.

    #include #include

    12

  • Chapitre 2 : Processus

    #include #include #include /* permet de rcuprer les codes d'erreur */

    pid_t pid_fils

    int main(void){

    int status;switch (pid_fils=fork()){

    case -1 : perror("Problme dans fork()\n");exit(errno); /* retour du code d'erreur */break;

    case 0 : puts("Je suis le fils");puts("Je retourne le code 3");exit(3);

    default : puts("Je suis le pre");puts("Je rcupre le code de retour");wait(&status);printf("code de sortie du fils %d : %d\n",

    pid_fils, WEXITSTATUS(status));break;

    }return 0;

    }

    La trace de ce programme est la suivante :Je suis le filsJe retourne le code 3Je suis le preJe rcupre le code de retourcode de sortie : 3

    2.4 ExercicesExercice 2.1 (E) crire un programme qui cre un ls. Le pre doit acher je suis le preet le ls doit acher je suis le ls.

    Exercice 2.2 (E) crire un programme qui cre deux ls appels ls 1 et ls 2. Le pre doitacher je suis le pre et le ls 1 doit acher je suis le ls 1, et le ls 2 doit acher jesuis le ls 2.

    13

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    Exercice 2.3 (E) crire un programme qui cre 5 ls en utilisant une boucle for. On remar-quera que pour que le ls ne cre pas lui-mme plusieurs ls, il faut interompre la boucle parun break dans le ls.

    Exercice 2.4 (E) crire un programme avec un processus pre qui engendre 5 ls dans unebouble for. Les ls sont nomms ls 1 ls 5. Le ls 1 doit acher je suis le ls 1 et le ls 2doit acher je suis le ls 2, et ainsi de suite.Indication. on pourra utiliser une variable globale.

    Exercice 2.5 (E) crire un programme qui cre deux ls appels ls 1 et ls 2. Chaque lsdoit attendre un nombre de secondes alatoire entre 1 et 10, en utilisant la fonction sleep.Le programme attend que le ls le plus long se termine et ache la dure totale. On pourrautiliser la fonction time de la bibliothque time.h, qui retourne le nombre de secondes depuisle premier janvier 1970 0h (en temps universel).

    14

  • Chapitre 3Lancement dun programme : exec

    3.1 Rappels : Arguments en ligne de commandeLa fonction main dun programme peut prendre des arguments en ligne de commande. Parexemple, si un chier monprog.c a permis de gnrer un excutable monprog la compilation,$ gcc monprog.c -o monprog

    on peut invoquer le programme monprog avec des arguments$ ./monprog argument1 argment2 argument3Exemple. La commande cp du bash prend deux arguments :$ cp nomfichier1 nomfichier2

    Pour rcuprer les arguments dans le programme C, on utilise les paramtres argc et argvdu main. Lentier argc donne le nombre darguments rentrs dans la ligne de commande plus1, et le paramtre argv est un tableau de chanes de caractres qui contient comme lments :

    Le premier lment argv[0] est une chane qui contient le nom du chier executable duprogramme ;

    Les lments suivants argv[1], argv[2],etc... sont des chanes de caractres qui contiennentles arguments passs en ligne de commande.

    #include

    int main(int argc, char *argv[]){

    int i;if (argc == 1)

    puts("Le programme n'a reu aucun argument");if (argc >= 2)

    {puts("Le programme a reu les arguments suivants :");for (i=1 ; i

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    3.2 Lappel systme exec3.2.1 Arguments en listeLappel systme exec permet de remplacer le programme en cours par un autre programme sanschanger de numro de processus (PID). Autrement dit, un programme peut se faire remplacerpar un autre code source ou un script shell en faisant appel exec. Il y a en fait plusieursfonctions de la famille exec qui sont lgrement direntes.

    La fonction execl prend en paramtre une liste des arguments passer au programme (listetermine par NULL).

    #include #include #include

    int main(){

    /* dernier lment NULL, OBLIGATOIRE */execl("/usr/bin/emacs", "emacs", "fichier.c", "fichier.h", NULL);

    perror("Problme : cette partie du code ne doit jamais tre excute");return 0;

    }

    Le premier paramtre est une chane qui doit contenir le chemin daccs complet (dans lesystme de chiers) au chier excutable ou au script shell excuter. Les paramtres suivantssont des chanes de caractre qui reprsentent les arguments passs en ligne de commande aumain de ce programme. La chane argv[0] doit donner le nom du programme (sans chemindaccs), et les chanes suivantes argv[1], argv[2],etc... donnent les arguments.

    Concernant le chemin daccs, il est donn partir du rpertoire de travail ($PWD), ou partir du rpertoire racine / sil commence par le caractre /(exemple : /home/remy/enseignement/systeme/script1).

    La fonction execlp permet de rechercher les excutables dans les rpertoires apparassantdans le PATH, ce qui vite souvent davoir spcier le chemin complet.

    #include #include #include

    int main(){

    /* dernier lment NULL, OBLIGATOIRE */execlp("emacs", "emacs", "fichier.c", "fichier.h", NULL);

    perror("Problme : cette partie du code ne doit jamais tre excute");return 0;

    }

    16

  • Chapitre 3 : Lancement dun programme : exec

    3.2.2 Arguments en vecteurNous allons tudier lune dentre elles (la fonction execv). La dirence avec execl est quelon na pas besoin de conatre la liste des arguments lavance (ni mme leur nombre). Cettefonction a pour prototype :

    int execv(const char* application, const char* argv[]);

    Le mot const signie seulement que la fonction execv ne modie pas ses paramtres. Lepremier paramtre est une chane qui doit contenir le chemin daccs (dans le systme dechiers) au chier excutable ou au script shell excuter. Le deuxime paramtre est untableau de chanes de caractres donnant les arguments passs au programme lancer dans unformat similaire au paramtre argv du main de ce programme. La chane argv[0] doit donnerle nom du programme (sans chemin daccs), et les chanes suivants argv[1], argv[2],etc...donnent les arguments.

    Le dernier lment du tableau de pointeurs argv doit tre NULL pour marquerla n du tableau. Ceci est d au fait que lon ne passe pas de paramtre argcdonnant le nombre dargument

    Concernant le chemin daccs, il est donn partir du rpertoire de travail ($PWD), ou partir du rpertoire racine / sil commence par le caractre /(exemple : /home/remy/enseignement/systeme/script1).

    Exemple. Le programme suivant dite les chiers .c et .h du rpertoire de travail avecemacs.

    Dans le programme, le chemin daccs la commande emacs est donn partir de la racine/usr/bin/emacs.

    #include #include #include

    int main(){

    char * argv[] = {"emacs", "fichier.c", "fichier.h", NULL}/* dernier lment NULL, obligatoire */execv("/usr/bin/emacs", argv);

    puts("Problme : cette partie du code ne doit jamais tre excute");return 0;

    }

    Remarque 3.2.1 Pour excuter un script shell avec execv, il faut que la premire ligne de cescript soit

    #! /bin/sh

    ou quelque chose danalogue.

    17

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    En utilisant fork, puis en faisant appel exec dans le processus ls, un programme peutlancer un autre programme et continuer tourner dans le processus pre.

    Il existe une fonction execvp qui lance un programme en le recherchant dansla variable denvironnement PATH. Lutilisation de cette fonction dans un pro-gramme Set-UID pose des problmes de scurit (voir explications plus loin pourla fonction system

    3.3 La fonction system3.3.1 La variable PATH dans unixLa variable denvironnement PATH sous unix et linux donne un certain nombre de cheminsvers des rpertoires o se trouve les excutables et scripts des commandes. Les chmins dans lePATH sont spars par des :.

    $ echo $PATH/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/remy/bin:.

    Lorsquon lance une commande dans une console, le systme va cherche lexcutable oule script de cette commande dans les rpertoires donns dans le PATH. Chaque utilisateurpeut rajouter des chemins dans son PATH (en modiant son chier .bashrc sous linux). Enparticulier, lutilisateur peut rajouter le rpertoire . (point) dans le PATH, ce qui signie quele systme va chercher les commandes dans le rpertoire de travail donn dans la variabledenvironnement PWD. La recherche des commandes dans les rpertoires a lieu dans lordredans lequel les rpertoires apparassent dans le PATH. Par exemple, pour le PATH donn ci-dessus, la commande sera recherche dabord dans le rpertoire /usr/local/bin, puis dans lerpertoire /usr/bin. Si deux commandes de mme nom se trouve dans deux rpertoires duPATH, cest la premire commande trouve qui sera excute.

    3.3.2 La fonction systemLa fonction system de la bibliothque stdlib.h permet directement de lancer un programmedans un programme C sans utiliser fork et exec. Pour cel, on utilise linstruction :

    #include ...system("commande");

    Exemple. La commande unix clear permet deacer la console. Pour eacer la consoledans un programme C avec des entres-sorties dans la console, on peut ustilser :

    system("clear");

    Lorsquon utilise la fonction system, la commande quon excute est recherche dans lesrpertoires du PATH comme si lon excutait la commande dans la console.

    18

  • Chapitre 3 : Lancement dun programme : exec

    3.4 Applications suid et problmes des scurit lis system,execlp ou exevp

    Dans le systme unix, les utilisateurs et ladministrateur (utilisateur) on des droits (que lonappelle privilges), et laccs certaines commandes leur sont interdites. Cest ainsi que, parexemple, si le systme est bien administr, un utilisateur ordinaire ne peut pas facilementendomager le systme.

    Exemple. Imaginons que les utilisateurs aient tous les droits et quun utilisateur malin-tention ou distrait tape la commande$ rm -r /

    Cela supprimerait tous les chiers du systme et des autres utilisateurs et porterait unprjudice important pour tous les utilisateurs du systme. En fait, beaucoup de chiers sontinterdits lutilisateur en criture, ce qui fait que la commande rm sera ineective sur ceschiers.

    Pour cel, lorsque lutilisateur lance une commande ou un script (comme la commande rm),les privilges de cet utilsateur sont pris en compte lors de lexcution de la commande.

    Sous unix, un utilisateur A (par exemple root) peut modier les permissions sur un chierexcutable pour que tout autre utilisateur B puisse excuter ce chier avec ses propres privilges(les privilges de A). Cela sappelle les permissions suid.

    Exemple. Supposons que lutilisateur root tape les commandes suivantes :$ gcc monprog.c -o monprog$ ls -l-rwxr-xr-x 1 root root 18687 Sep 7 08:28 monprog-rw-r--r-- 1 root root 3143 Sep 4 15:07 monprog.c$ chmod +s monprog$ ls -l-rwsr-sr-s 1 root root 18687 Sep 7 08:28 monprog-rw-r--r-- 1 root root 3143 Sep 4 15:07 monprog.c

    Le programme moprog est alors suid et nimporte quel utilisateur peut lexcuter avec lesprivilges du propritaire de monprog, cest dire root.

    Supposons maintenant que dans le chier monprog.c il y ait linstructionsystem("clear");

    Considrons un utilisateur malintentionn remy. Cet utilisateur modie son PATH pourrajouter le rpertoire ., (point) mais met le rpertoire . au tout dbut de PATH$ PATH=.:\PATH$ export PATH$ echo \PATH.:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/remy/bin:.

    Dans la recherche des commandes dans les rpertoires du PATH, le systme chercheradabord les commandes dans le rpertoire de travail .. Supposons maintenant que lutilisateurremy cre un script appel clear dans son rpertoire de travail. qui contienne la lignerm -r /

    19

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    $ echo "rm -r /" > clear$ cat clearrm -r /$ chmod +x clear$ monprog

    Lorsque lutilisateur remy va lancer lexcutable monprog avec les privilges de root, le pro-gramme va excuter le script clear de lutilisateur (au lieu de la commande /usr/bin/clear)avec les privilges de root, et va supprimer tous les chiers du systme.

    Il ne faut jamais utiliser la fonction system ou la fonction execvp dans uneapplication suid, car un utilisateur malintention pourrait excuter nimportequel script avec vos privilges.

    3.5 ExercicesExercice 3.1 () crire un programme qui prend deux arguments en ligne de commande ensupposant qu ce sont des nombres entiers, et qui ache laddition de ces deux nombres.

    Exercice 3.2 () Ecrire un programme qui prend en argument un chemin vers un rpertoireR, et copie le rpertoire courant dans ce rpertoire R.

    Exercice 3.3 () Ecrire un programme qui saisit un nom de chier texte au clavier et ouvre cechier dans lditeur emacs, dont le chier excutable se trouve lemplacement /usr/bin/emacs.

    Exercice 3.4 () Ecrire un programme qui saisit des noms de rpertoires au clavier et copiele rpertoire courant dans tous ces rpertoires. Le programme doit se poursuivre jusqu ce quelutilisateur demande de quitter le programme.

    Exercice 3.5 () Ecrire un programme qui saisit des nom de chiers texte au clavier etouvre tous ces chiers dans lditeur emacs. Le programme doit se poursuivre jusqu ce quelutilisateur demande de quitter.

    Exercice 3.6 ( ) Considrons les coecients binmiaux Ckn tels que

    C0i = 1 et Cii = 1 pour tout i

    Ckn = Ckn1 + C

    k1n1

    crire un programme pour calculerCkn qui nutilise aucune boucle (ni while ni for), et qui naitcomme seule fonction que la fonction main. La fonction main ne doit contenir aucun appel elle-mme.On pourra utiliser des chiers textes temporaires dans le rpertoire /tmp.

    20

  • Chapitre 4Communication entre processus

    Dans ce chapitre, nous voyons comment faire communiquer des processus entre eux par destubes. Pour le moment, les processus qui communiquent doivent tre des processus de la mmemachine. Cependant, le principe de communication avec les fonctions read et write sera ruti-lis par la suite lorsque nous aborderons la programmation rseau, qui permet de faire com-muniquer des processus se trouvant sur des stations de travail distinctes.

    4.1 Tubes et forkUn tube de communication est un tuyau (en anglais pipe) dans lequel un processus peut criredes donnes et un autre processus peut lire. On cre un tube par un appel la fonction pipe,dclare dans unistd.h :

    int pipe(int descripteur[2]);

    La fonction renvoie 0 si elle russit, et elle cre alors un nouveau tube. La fonction piperemplit le tableau descripteur pass en paramtre, avec :

    descripteur[0] dsigne la sortie du tube (dans laquelle on peut lire des donnes) ; descripteur[1] dsigne lentre du tube (dans laquelle on peut crire des donnes) ;Le principe est quun processus va crire dans descripteur[1] et quun autre processus va

    lire les mmes donnes dans descripteur[0]. Le problme est quon ne cre le tube dans unseul processus, et un autre processus ne peut pas deviner les valeurs du tableau descripteur.Pour faire communiquer plusieurs processus entre eux, il faut appeler la fonction pipe avantdappeler la fonction fork. Ensuite, le processus pre et le processus ls auront les mmesdescripteurs de tubes, et pourront donc communiquer entre eux. De plus, un tube ne permetde communiquer que dans un seul sens. Si lon souhaite que les processus communiquent dansles deux sens, il faut crer deux pipes.

    Pour crire dans un tube, on utilise la fonction write :

    ssize_t write(int descripteur1, const void *bloc, size_t taille);

    Le descripteur doit crrespondre lentre dun tube. La taille est le nombre doctets quonsouhaite crire, et le bloc est un pointeur vers la mmoire contenant ces octets.

    Pour lire dans un tube, on utilise la fonction read :

    21

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    ssize_t read(int descripteur0, void *bloc, size_t taille);

    Le descripteur doit correspondre la sortie dun tube, le bloc pointe vers la mmoiredestine recevoir les octets, et la taille donne le nombre doctets quon souhaite lire. Lafonction renvoie le nombre doctets eectivement lus. Si cette valeur est infrieure taille,cest quune erreur sest produite en cours de lecture (par exemple la fermeture de lentre dutube suite la terminaison du processus qui crit).

    Dans la pratique, on peut transmettre un buer qui a une taille xe (256 octets danslexemple ci-dessous). Lessentiel est quil y ait exactement le mme nombre doctets en lectureet en criture de part et dautre du pipe. La partie signicative du buer est termine par un\0 comme pour nimporte quelle chane de caractre.

    #include #include #include #include

    #define BUFFER_SIZE 256

    int main(void){pid_t pid_fils;int tube[2];unsigned char bufferR[256], bufferW[256];

    puts("Cration d'un tube");if (pipe(tube) != 0) {/* pipe */{

    fprintf(stderr, "Erreur dans pipe\(\backslash\)n");exit(1);

    }pid_fils = fork(); {/* fork */if (pid_fils == -1){

    fprintf(stderr, "Erreur dans fork\(\backslash\)n");exit(1);

    }if (pid_fils == 0) {/* processus fils */{printf("Fermeture entre dans le fils (pid = %d)\(\backslash\)n", getpid());close(tube[1]);read(tube[0], bufferR, BUFFER_SIZE);printf("Le fils (%d) a lu : %s\(\backslash\)n", getpid(), bufferR);

    }else {/* processus pre */{printf("Fermeture sortie dans le pre (pid = %d)\(\backslash\)n", getpid());

    22

  • Chapitre 4 : Communication entre processus

    close(tube[0]);sprintf(bufferW, "Message du pre (%d) au fils", getpid());write(tube[1], bufferW, BUFFER_SIZE);wait(NULL);

    }return 0;

    }

    La sortie de ce programme est :

    Cration d'un tubeFermeture entre dans le fils (pid = 12756)Fermeture sortie dans le pre (pid = 12755)Ecriture de 31 octets du tube dans le preLecture de 31 octets du tube dans le filsLe fils (12756) a lu : Message du pre (12755) au fils

    Il faut noter que les fonctions read et write permettent de transmettre uniquement destableaux des octets. Toute donne (nombre ou texte) doit tre convertie en tableau de caractrepour tre transmise, et la taille des ces donnes doit tre connue dans les deux processuscommuniquants.

    4.2 Transmettre des donnes binairesVoici un exemple de programme qui saisit une valeur x au clavier dans le processus pre, ettransmet le sinus de ce nombre, en tant que double au processus ls.

    #include #include #include #include #include #include

    int main(\void){pid_t pid_fils;int tube[2];double x, valeurW, valeurR;

    puts("Cration d'un tube");if (pipe(tube) != 0) /* pipe */{

    fprintf(stderr, "Erreur dans pipe\n");exit(1);

    }switch(pid_fils = fork()) /* fork */

    23

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    {case -1 :

    perror("Erreur dans fork\n");exit(errno);

    case 0 : /* processus fils */close(tube[1]);read(tube[0], &valeurR, sizeof(double));printf("Le fils (%d) a lu : %.2f\(\backslash\)n", getpid(), valeurR);break;

    default : /* processus pre */printf("Fermeture sortie dans le pre (pid = %d)\n", getpid());close(tube[0]);puts("Entrez x :");scanf("%lf", &x);valeurW = sin(x);write(tube[1], &valeurW, \sizeof(\double));wait(NULL);break;

    }return 0;

    }Complments

    Dune manire gnrale, ladresse dune variable peut toujours tre considre comme untableau dont le nombre doctet est gal la taille de cette variable. Ceci est d au faitquun tableau est seulement une adresse qui pointe vers une zone mmoire rserve pourle programme (statiquement ou dynamiquement).

    4.3 Rediriger les ots dentres-sorties vers des tubesOn peut lier la sortie tube[0] du tube stdin. Par la suite, tout ce qui sort du tube arrivesur le ot dentre standard stdin, et peut tre lu avec scanf, fgets, etc... Pour cel, il sutde mettre linstruction :

    dup2(tube[0], STDIN_FILENO);De mme, on peut lier lentre tube[1] du tube stdout. Par la suite, tout ce qui sort sur

    le ot de sortie standard stdout entre ans le tube, et on peut crire dans le tube avec printf,puts, etc... Pour cel, il sut de mettre linstruction :

    dup2(tube[1], STDOUT_FILENO);

    Complments

    Plus gnralement, la fonction dup2 copie le descripteur de chier pass en premier ar-gument dans le descripteur pass en deuxime argument.

    24

  • Chapitre 4 : Communication entre processus

    4.4 Tubes nommsOn peut faire communiquer deux processus traver un tube nomm. Le tube nomm passe parun chier sur le disque. Lintrt est que les deux processus nont pas besoin davoir unlien de parent. Pour crer un tube nomm, on utilise la fonction mkfifo de la bibliothquesys/stat.h.Exemple. Dans le code suivant, le premier programme transmet le mot coucou au deuximeprogramme. Les deux programmes nont pas besoin dtre lis par un lien de parent.

    #include #include #include #include #include

    int main(){

    int fd;FILE *fp;char *nomfich="/tmp/test.txt"; /* nom du fichier */if(mkfifo(nomfich, 0644) != 0) /* cration du fichier */{

    perror("Problme de cration du noeud de tube");exit(1);

    }fd = open(nomfich, O_WRONLY); /* ouverture en criture */fp=fdopen(fd, "w"); /* ouverture du flot */fprintf(fp, "coucou\(\backslash\)n"); /* criture dans le flot */unlink(nomfich); /* fermeture du tube */return 0;

    }

    La fonction mkfifo prend en paramtre, outre le chemin vers le chier, le masque des permis-sions (lecture, criture) sur la structure fo.

    #include #include #include #include #include

    int main(){

    int fd;FILE *fp;char *nomfich="/tmp/test.txt", chaine[50];fd = open(nomfich, O_RDONLY); /* ouverture du tube */fp=fdopen(fd, "r"); /* ouverture du flot */

    25

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    fscanf(fp, "%s", chaine); /* lecture dans le flot */puts(chaine); /* affichage */unlink(nomfich); /* fermeture du flot */return 0;

    }

    4.5 ExercicesExercice 4.1 (exobasepipe) crire un programme qui cre deux processus. Le processus preouvre un chier texte en lecture. On suppose que le chier est compos de mots forms decaractres alphabtiques spars par des espaces. Le processus ls saisit un mot au clavier. Leprocessus pre recherche le mot dans le chier, et transmet au ls la valeur 1 si le mot est dansle chier, et 0 sinon. Le ls ache le rsultat.

    Exercice 4.2 (R) eprendre les programmes de lexercice ??. Nous allons faire un programmequi fait la mme chose, mais transmet les donnes direment. Dans le programme pre, onliera les ots stdout et stdin un tube.

    Exercice 4.3 (exotubeexec) crire un programme qui cre un tube, cre un processus ls,puis, dans le ls, lance par execv un autre programme, appel programme ls. Le programmepre transmets les descripteurs de tubes au programmes ls en argument, et transmet unmessage au ls par le tube. Le programme ls ache le message.

    Exercice 4.4 (M) me question qu lexercice ?? mais en passant les descripteurs de tubecomme variables denvironnement.

    26

  • Chapitre 5

    Threads Posix

    5.1 Pointeurs de fonctionUn pointeur de fonctions en C est une variable qui permet de dsigner une fonction C. Commenimporte quelle variable, on peut mettre un pointeur de fonctions soit en variable dans unefonction, soit en paramtre dans une fonction.

    On dclare un pointeur de fonction comme un prototype de fonction, mais on ajoute unetoile () devant le nom de la fonction. Dans lexemple suivant, on dclare dans le main unpointeur sur des fonctions qui prennent en paramtre un int, et un pointeur sur des fonctionsqui retournent un int.

    #include

    int SaisisEntier(void){

    int n;printf("Veuillez entrer un entier : ");scanf("%d", &n);return n;

    }

    void AfficheEntier(int n){

    printf("L'entier n vaut %d\(\backslash\)n", n);}

    int main(void){void (*foncAff)(int); {/* dclaration d'un pointeur foncAff */int (*foncSais)(void); {/*dclaration d'un pointeur foncSais */int entier;

    foncSais = SaisisEntier; {/* affectation d'une fonction */foncAff = AfficheEntier; {/* affectation d'une fonction */

    27

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    entier = foncSais(); {/* on excute la fonction */foncAff(entier); {/* on excute la fonction */return 0;

    }

    Dans lexemple suivant, la fonction est passe en paramtre une autre fonction, puisexcute.

    #include

    int SaisisEntier(void){

    int n;printf("Veuillez entrer un entier : ");scanf("%d", &n);getchar();return n;

    }

    void AfficheDecimal(int n){

    printf("L'entier n vaut %d\(\backslash\)n", n);}

    void AfficheHexa(int n){

    printf("L'entier n vaut %x\(\backslash\)n", n);}

    void ExecAffiche(void (*foncAff)(int), int n){foncAff(n); {/* excution du paramtre */

    }

    int main(void){int (*foncSais)(void); {/*dclaration d'un pointeur foncSais */int entier;char rep;

    foncSais = SaisisEntier; {/* affectation d'une fonction */entier = foncSais(); {/* on excute la fonction */

    puts("Voulez-vous afficher l'entier n en dcimal (d) ou en hexa (x) ?");rep = getchar();{/* passage de la fonction en paramtre : */if (rep == 'd')

    28

  • Chapitre 5 : Threads Posix

    ExecAffiche(AfficheDecimal, entier);if (rep == 'x')ExecAffiche(AfficheHexa, entier);

    return 0;}

    Pour prvoir une utilisation plus gnrale de la fonction ExecAffiche, on peut utiliser desfonctions qui prennent en paramtre un void* au lieu dun int. Le void* peut tre ensuitereconverti en dautres types par un cast.

    void AfficheEntierDecimal(void *arg){

    inte n = (int)arg; /* un void* et un int sont sur 4 octets */printf("L'entier n vaut \%d\n", n);

    }

    void ExecFonction(void (*foncAff)(void* arg), void *arg){foncAff(arg); /* excution du paramtre */

    }

    int main(void){

    int n;...ExecFonction(AfficheEntierDecimal, (void*)n);...

    }

    On peut utiliser la mme fonction ExecFonction pour acher tout autre chose que desentiers, par exemple un tableau de float.

    typedef struct{

    int n; /* nombre d'lments du tableau */double *tab; /* tableau de double */

    }TypeTableau;

    void AfficheTableau(void *arg){

    inte i;TypeTableau *T = (TypeTableau*)arg; /* cast de pointeurs */for (i=0 ; in ; i++)

    {printf("%.2f", T->tab[i]);

    }}

    29

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    void ExecFonction(void (*foncAff)(void* arg), void *arg){foncAff(arg); /* excution du paramtre */

    }

    int main(void){

    TypeTableau tt;...ExecFonction(AfficheTableau, (void*)&tt);...

    }

    5.2 Thread Posix (sous linux)5.2.1 Quest-ce quun thread ?Un thread (ou l dexcution en franais) est une parie du code dun programme (une fonction),qui se droule paralllement dautre parties du programme. Un premier intert peut tredeectuer un calcul qui dure un peu de temps (plusieurs secondes, minutes, ou heures) sansque linterface soit bloque (le programme continue rpondre aux signaux). Lutilisateurpeut alors intervenir et interrompre le calcul sans taper un ctrl-C brutal. Un autre intrt estdeectuer un calcul parallle sur les machines multi-processeur. Les fonctions lies aux threadsont dans la bibliothque pthread.h, et il faut compiler avec la librairie libpthread.a :

    $ gcc -lpthread monprog.c -o monprog

    5.2.2 Cration dun thread et attente de terminaisonPour crer un thread, il faut crer une fonction qui va sexcuter dans le thread, qui a pourprototype :

    void *ma_fonction_thread(void *arg);

    Dans cette fonction, on met le code qui doit tre excut dans le thread. On cre ensuitele thread par un appel la fonction pthread_create, et on lui passe en argument la fonc-tion ma_fonction_thread dans un pointeurs de fonction (et son argument arg). La fonctionpthread_create a pour prototype :

    int pthread_create(pthread_t *thread, pthread_attr_t *attributs,void * (*fonction)(void *arg), void *arg);

    Le premier argument est un passage par adresse de lidentiant du thread (de type pthread_t).La fonction pthread_create nous retourne ainsi lidentiant du thread, qui lon utilise ensuitepour dsigner le thread. Le deuxime argument attributs dsigne les attributs du thread, eton peut mettre NULL pour avoir les attibuts par dfaut. Le troisime argument est un pointeur

    30

  • Chapitre 5 : Threads Posix

    sur la fonstion excuter dans le thread (par exemple ma_fonction_thread, et le quatrimeargument est largument de la fonction de thread.

    Le processus qui excute le main (lquivalent du processus pre) est aussi un thread etsappelle le thread principal. Le thread principal peut attendre la fon de lexcution dun autrethread par la fonction pthread_join (similaire la fonction wait dans le fork. Cette fonctionpermet aussi de rcuprer la valeur retourne par la fonction ma_fonction_thread du thread.Le prototype de la fonction pthread_join est le suivant :

    int pthread_join(pthread_t thread, void **retour);

    Le premier paramtre est ldentiant du thread (que lon obtient dans pthread_create),et le second paramtre est un passage par adresse d'un pointeur qui permet de rcuprerla valeur retourne par ma_fonction_thread.

    5.2.3 ExemplesLe premier exemple cre un thread qui dort un nombre de secondes pass en argument, pendantque le thread principal attend quil se termine.

    #include #include #include #include #include

    void *ma_fonction_thread(void *arg){int nbsec = (int)arg;printf("Je suis un thread et j'attends %d secondes\(\backslash\)n", nbsec);sleep(nbsec);puts("Je suis un thread et je me termine");pthread_exit(NULL); {/* termine le thread proprement */

    }

    int main(void){int ret;pthread_t my_thread;int nbsec;time_t t1;srand(time(NULL));t1 = time(NULL);nbsec = rand()%10; {/* on attend entre 0 et 9 secondes */{/* on cre le thread */ret = pthread_create(&my_thread, NULL,

    ma_fonction_thread, (void*)nbsec);if (ret != 0){

    31

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    fprintf(stderr, "Erreur de cration du thread");exit (1);

    }pthread_join(my_thread, NULL); {/* on attend la fin du thread */printf("Dans le main, nbsec = %d\(\backslash\)n", nbsec);printf("Duree de l'operation = %d\(\backslash\)n", time(NULL)-t1);return 0;

    }

    Le deuxime exemple cre un thread qui lit une valeur entire et la retourne au main.

    #include #include #include #include #include

    void *ma_fonction_thread(void *arg){int resultat;printf("Je suis un thread. Veuillez entrer un entier\(\backslash\)n");scanf("%d", &resultat);pthread_exit((void*)resultat); {/* termine le thread proprement */

    }

    int main(void){int ret;pthread_t my_thread;{/* on cre le thread */ret = pthread_create(&my_thread, NULL,

    ma_fonction_thread, (void*)NULL);if (ret != 0){

    fprintf(stderr, "Erreur de cration du thread");exit (1);

    }pthread_join(my_thread, (void*)&ret); {/* on attend la fin du thread */printf("Dans le main, ret = %d\(\backslash\)n", ret);return 0;

    }

    5.3 Donne partages et exclusion mutuelleLorsquun nouveau processus est cr par un fork, toutes les donnes (variables globales,variables locales, mmoire alloue dynamiquement), sont dupliques et copies, et le processuspre et le processus ls travaillent ensuite sur des variables direntes.

    32

  • Chapitre 5 : Threads Posix

    Dans le cas de threads, la mmoire est partage, cest dire que les variables globales sontpartages entre les dirents threads qui sexcutent en parallle. Cela pose des problmeslorsque deux threads dirents essaient dcrire et de lire une mme donne.

    Deux types de problmes peuvent se poser :

    Deux threads concurrents essaient en mme temps de modier une variable globale ; Un thread modie une structure de donne tandis quun autre thread essaie de la lire.Il est alors possible que le thread lecteur lise la structure alors que le thread crivain acrit la donne moiti. La donne est alors incohrente.

    Pour accder des donnes globales, il faut donc avoir recours un mcanisme dexclusionmutuelle, qui fait que les threads ne peuvent pas accder en mme temps une donne. Pourcel, on introduit des donnes appels mutex, de type pthread_mutex_t.

    Un thread peut verrouiller un mutex, avec la fonction pthread_mutex_lock(), pour pouvoiraccder une donne globale ou un ot (par exemple pour crire sur la sortie stdout). Unefois laccs termin, le thread dvrouille le mutex, avec la fonction pthread_mutex_unlock().Si un thread A essaie de vrouiller le un mutex alors quil est dj verrouill par un autre threadB, le thread A reste bloqu sur lappel de pthread_mutex_lock() jusqu ce que le thread Bdvrouille le mutex. Une fois le mutex dvrouill par B, le thread A verrouille immdiatementle mutex et son excution se poursuit. Cela permet au thread B daccder tranquillement desvariables globales pendant que le thread A attend pour accder aux mmes variables.

    Pour dclarer et initialiser un mutex, on le dclare en variable globale (pour quil soitaccessible tous les threads) :

    pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;

    La fonction pthread_mutex_lock(), qui permet de verrouiller un mutex, a pour prototype :

    int pthread_mutex_lock(pthread_mutex_t *mutex);

    Il faut viter de verrouiller deux fois un mme mutex dans le mme thread sansle dverrouiller entre temps. Il y a un risque de blocage dnitif du thread.Certaines versions du systme grent ce problme mais leur comportement nestpas portable.

    La fonction pthread_mutex_unlock(), qui permet de dverrouiller un mutex, a pour pro-totype :

    int pthread_mutex_unlock(pthread_mutex_t *mutex);

    Dans lexemple suivant, dirents threads font un travail dune dure alatoire. Ce travailest fait alors quun mutex est verrouill.

    #include #include #include #include

    33

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;

    void* ma_fonction_thread(void *arg);

    int main(void){int i;pthread_t thread[10];srand(time(NULL));

    for (i=0 ; i

  • Chapitre 5 : Threads Posix

    Le thread numero 1 a fini son calculLe thread numro 7 commence son calculLe thread numero 7 a fini son calculLe thread numro 1 commence son calculLe thread numero 1 a fini son calculLe thread numro 1 commence son calculLe thread numero 1 a fini son calculLe thread numro 9 commence son calculLe thread numero 9 a fini son calculLe thread numro 4 commence son calculLe thread numero 4 a fini son calcul...

    En mettant en commentaire les lignes avec pthread_mutex_lock() et pthread_mutex_unlock(),on obtient :

    ...Le thread numro 9 commence son calculLe thread numero 0 a fini son calculLe thread numro 0 commence son calculLe thread numero 1 a fini son calculLe thread numro 1 commence son calculLe thread numero 4 a fini son calculLe thread numero 8 a fini son calculLe thread numro 8 commence son calculLe thread numero 8 a fini son calculLe thread numro 8 commence son calculLe thread numero 1 a fini son calculLe thread numro 1 commence son calculLe thread numero 3 a fini son calculLe thread numro 3 commence son calculLe thread numero 3 a fini son calculLe thread numro 3 commence son calculLe thread numero 5 a fini son calculLe thread numero 9 a fini son calcul...

    On voit que plusieurs threads interviennent pendant le calcul du thread numro 9 et 4.

    5.4 SmaphoresEn gnral, une section critique est une partie du code o un processus ou un thread ne peutrentrer qu une certaine condition. Lorsque le processus (ou un thread) entre dans la sectioncritique, il modie la condition pour les autres processus/threads.

    Par exemple, si une section du code ne doit pas tre excute simultanment par plus den threads. Avant de rentrer dans la section critique, un thread doit vrier quau plus n-1threads y sont dj. Lorsquun thread entre dans la section critique, il modie la conditions

    35

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    sur le nombre de threads qui se trouvent dans la section critique. Ainsi, un autre thread peutse trouver empch dentrer dans la section critique.

    La dicult est quon ne peut pas utiliser une simple variable comme compteur. En eet,si le test sur le nombre de thread et la modication du nombre de threads lors de lentre dansla section critique se font squentiellement par deux instructions, si lon joue de malchance unautre thread pourrait tester le condition sur le nombre de threads justement entre lexcutionde ces deux instructions, et deux threads passeraient en mme temps dans la section critiques.Il y a donc ncessit de tester et modier la condition de manire atomique, cest direquaucun autre processus/thread de peut rien excuter entre le test et la modication. Cestune opration atomique appele Test and Set Lock.

    Les smaphores sont un type sem_t et une ensemble de primitives de base qui permettentdimplmenter des conditions assez gnrales sur les sections critiques. Un smaphore possdeun compteur dont la valeur est un entier positif ou nul. On entre dans une section critique sila valeur du compteur est strictement positive.

    Pour utiliser une smaphore, on doit le dclarer et linitialiser une certaine valeur avec lafonction sem_init.

    \inte sem_init(sem_t *semaphore, \inte partage, {\bf unsigned} \inte valeur)

    Le premier argument est un passage par adresse du smaphore, le deuxime argumentindique si le smaphore peut tre partag par plusieurs processus, ou seulement par les threadsdu processus appelant (partage gale 0). Enn, le troisime argument est la valeur initiale dusmaphore.

    Aprs utilisation, il faut systmatiquement librer le smaphore avec la fonction sem_destroy.

    int sem_destroy (sem_t *semaphore)

    Les primitives de bases sur les smaphores sont :

    sem_wait : Reste bloque si le smaphore est nul et sinon dcrmente le compteur (op-ration atomique) ;

    sem_post : incrmente le compteur ; sem_getvalue : rcupre la valeur du compteur dans une variable passe par adresse ; sem_trywait : teste si le smaphore est non nul et dcrmente le smaphore, mais sansbloquer. Provoque une erreur en cas de valeur nulle du smaphore. Il faut utiliser cettefonction avec prcaution car elle est prompte gnrer des bogues.

    Les prototypes des fonctions sem_wait, sem_post et sem_getvalue sont :

    \inte sem_wait (sem_t * semaphore)\inte sem_post(sem_t *semaphore)\inte sem_getvalue(sem_t *semaphore, \inte *valeur)

    Exemple. Le programme suivant permet au plus n smaphores dans la section critique, o nen pass en argument.

    36

  • Chapitre 5 : Threads Posix

    #include #include #include #include #include

    sem_t semaphore; {/* variable globale : smaphore */

    void* ma_fonction_thread(void *arg);

    int main(int argc, char **argv){int i;pthread_t thread[10];srand(time(NULL));

    if (argc != 2){

    printf("Usage : %s nbthreadmax\(\backslash\)n", argv[0]);exit(0);

    }sem_init(&semaphore, 0, atoi(argv[1])); {/* initialisation */

    {/* cration des threads */for (i=0 ; i

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    sem_post(&semaphore);sleep(rand()%9+1);

    }pthread_exit(NULL);

    }

    Exemples de trace :

    \ gcc -lpthread semaphore.c -o semaphore\ ./semaphore 2Le thread 0 entre dans la section critiqueLe thread 1 entre dans la section critiqueLe thread 0 sort de la section critiqueLe thread 2 entre dans la section critiqueLe thread 1 sort de la section critiqueLe thread 3 entre dans la section critiqueLe thread 2 sort de la section critiqueLe thread 4 entre dans la section critiqueLe thread 3 sort de la section critiqueLe thread 5 entre dans la section critiqueLe thread 4 sort de la section critiqueLe thread 6 entre dans la section critiqueLe thread 6 sort de la section critiqueLe thread 7 entre dans la section critiqueLe thread 7 sort de la section critiqueLe thread 8 entre dans la section critique...

    Autre exemple avec trois threads dans la section critique :

    \ ./semaphore 3Le thread 0 entre dans la section critiqueLe thread 1 entre dans la section critiqueLe thread 2 entre dans la section critiqueLe thread 1 sort de la section critiqueLe thread 3 entre dans la section critiqueLe thread 0 sort de la section critiqueLe thread 4 entre dans la section critiqueLe thread 2 sort de la section critiqueLe thread 5 entre dans la section critiqueLe thread 3 sort de la section critiqueLe thread 6 entre dans la section critiqueLe thread 6 sort de la section critiqueLe thread 7 entre dans la section critiqueLe thread 5 sort de la section critiqueLe thread 8 entre dans la section critique

    38

  • Chapitre 5 : Threads Posix

    Le thread 4 sort de la section critiqueLe thread 9 entre dans la section critiqueLe thread 9 sort de la section critiqueLe thread 1 entre dans la section critiqueLe thread 1 sort de la section critiqueLe thread 2 entre dans la section critiqueLe thread 7 sort de la section critiqueLe thread 0 entre dans la section critiqueLe thread 8 sort de la section critiqueLe thread 6 entre dans la section critiqueLe thread 2 sort de la section critiqueLe thread 3 entre dans la section critiqueLe thread 3 sort de la section critique

    5.5 ExercicesExercice 5.1 () crire un programme qui cre un thread qui prend en paramtre un tableaudentiers et lache dans la console.

    Exercice 5.2 () crire un programme qui cre un thread qui alloue un tableau dentiers,initialise les lments par des entiers alatoires entre 0 et 99, et retourne le tableau dentiers.

    Exercice 5.3 () Crer une structure TypeTableau qui contient :

    Un tableau dentiers ; Le nombre dlments du tableau ; Un entier x.

    crire un programme qui cre un thread qui initialise un TypeTableau avec des valeursalatoires entre 0 et 99. Le nombre dlments du tableau est pass en paramtre. Dans lemme temps, le thread principal lit un entiers x au clavier. Lorsque le tableau est ni degnrer, le programme cre un thread qui renvoie 1 si llment x est dans le tableau, et 0sinon.

    Exercice 5.4 () a) Reprendre la fonction de thread de gnration dun tableau alatoiredu 1. Le thread principal cre en parallle deux tableaux T1 et T2, avec le nombre dlmentsde T1 plus petit que le nombre dlemnts de T2.b) Lorsque les tableaux sont nis de gnrer, lancer un thread qui dtermine si le tableau T1est inclus dans le tableau T2. Quelle est la complexit de lalgorithme ?c) Modier le programme prcdent pour quun autre thread puisse terminer le programme silutilsateur appuie sur la touche A (par exit(0)). Le programme doit acher un message encas dannulation, et doit acher le rsultat du calcul sinon.

    39

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    Exercice 5.5 () crire un programme, avec un compteur global compt, et qui cre deuxthreads :

    Le premier thread itre lopration suivante : on incrmente le compteur et attend untemps allatoire entre 1 et 5 secondes.

    Le deuxime thread ache la valeur du compteur toutes les deux secondes.Les accs au compteur seront bien sr protgs par un mutex. Les deux threads se terminent

    lorsque le compteur atteint une valeur limite passe en argument (en ligne de commande) auprogramme.

    Exercice 5.6 () Crer un programme qui a en variable globale un tableau de N double,avec N=100.

    Dans le main, le tableau sera initialis avec des valeurs relles alatoires entre 0 et 100, saufles valeurs tableau[0] et tableau[99] qui vallent 0.

    Le programme cre deux threads : Le premier thread remplace chaque valeur tableau[i], avec i = 1,2,. . .,98 par la moyenne

    (tableau[i-1]+tableau[i]+tableau[i+1])/3Il attend ensuite un temps allatoire entre 1 et 3 secondes ;

    Le deuxime thread ache le tableau toutes les 4 secondes.

    Exercice 5.7 () Dans un programme prvu pour utiliser des threads, crer un compteurglobal pour compter le nombre ditrations, et une variable globale relle u. Dans le main, oninitialisera u la valeur 1

    Le programme cre deux threads T1 et T2. Dans chaque thread Ti, on incrmente le comp-teur du nombre ditration, et on applique une aectation :

    u = f_i(u);

    pour une fonction f_i qui dpend du thread.

    f_1(x) = 14(x 1)2 et f_2(x) = 1

    6(x 2)2

    De plus, le thread ache la valeur de u et attend un temps alatoire (entre 1 et 5 secondes)entre deux itrations.

    Exercice 5.8 () (Problme du rendez-vous)a) Les smaphores permettent de raliser simplement des rendez-vous Deux threads T1 et T2itrent un traitement 10 fois. On souhaite qu chaque itration le thread T1 attende la n deson traitement qui dure 2 secondes le thread T2 ralisant un traitement dune dure alatoireentre 4 et 9 secondes. crire le programme principal qui cre les deux threads, ainsi que lesfonctions de threads en organisant le rendez-vous avec des smaphores.b) Dans cette version N threads doivent se donner rendez-vous, N tant pass en argument auprogramme. Les threads ont tous une dure alatoire entre 1 et 5 secondes.

    40

  • Chapitre 5 : Threads Posix

    Exercice 5.9 ( ) (Problme de lmetteur et du rcepteur) Un thread metteurdpose , intervalle variable entre 1 et 3 secondes, un octet dans une variable globale destination dun processus rcepteur. Le rcepteur lit cet octet intervalle variable aussi entre1 et 3 secondes. Quelle solution proposez-vous pour que lmetteur ne dpose pas un nouveloctet alors que le rcepteur na pas encore lu le prcdent et que le rcepteur ne lise pas deuxfois le mme octet ?

    Exercice 5.10 ( ) (Problme des producteurs et des consommateurs) Des proces-sus producteurs produisent des objets et les insre un par un dans un tampon de n places. Bienentendu des processus consommateurs retirent, de temps en temps les objets (un par un).

    Rsolvez le problme pour quaucun objet ne soit ni perdu ni consomm plusieurs fois.crire une programme avec N threads producteurs et M threads consomateurs, les nombres Net M tant saisis au clavier. Les producteurs et les consomateurs attendent un temps alatoireentre 1 et 3 secondes entre deux produits. Les produits sont des octets que lon stocke dans untableau de 10 octets avec gestion LIFO. Sil ny a plus de place, les producteurs restent bloqusen attendant que des places se librent.

    Exercice 5.11 ( ) (Problme des lecteurs et des rdacteurs) Ce problme modliseles accs une base de donnes. On peut accepter que plusieurs processus lisent la base en mmetemps mais si un processus est en train de la modier, aucun processus, pas mme un lecteur,ne doit tre autoris y accder. Comment programmer les lecteurs et les rdacteurs ? Proposerune solution avec famine des crivains : les crivains attendent quil ny ait aucun lecteur. crireune programme avec N threads lecteurs et M threads rdacteurs, les nombres N et M tantsaisis au clavier. La base de donne est reprsente par un tableau de 15 octets initialiss 0. chaque lecture/criture, le lecteur/crivain lit/modie loctet un emplacement alatoire.Entre deux lectures, les lecteurs attendent un temps alatoire entre 1 et 3 secondes. Entre deuxcritures, les crivains attendent un temps alatoire entre 1 et 10 secondes.

    41

  • Chapitre 6Gestion du disque dr et des chiers

    6.1 Organisation du disque dur6.1.1 Plateaux, cylindres, secteursUn disque dur possde plusieurs plateaux, chaque plateau possde deux faces, chaque facepossde plusieurs secteurs et plusieurs cylindres (voir cgure ci-dessous).

    Figure 6.1: Organisation dune face de plateau

    Le disque possde un secteur de boot, qui contient des informations sur les partitionsbootables et le boot-loader, qui permet de choisir le systme sous lequel on souhaite booter.Un disque est divis en partitions (voir la gure 6.2) Les donnes sur les partitions (tellesque les cylindre de dbut et de n ou les types de partition) sont stockes dans une table despartitions. Le schma dorganisation dune partition UNIX est montr sur la gure 6.3

    6.1.2 Grer les partitions et priphriques de stockageLoutil fdisk permet de modier les partitions sur un disque dur. Par exemple, pour voir etmodier les partitions sur le disque dur SATA /dev/sda, on lance fdisk :

    # fdisk /dev/sda

    The number of cylinders for this disk is set to 12161.There is nothing wrong with that, but this is larger than 1024,

    42

  • Chapitre 6 : Gestion du disque dr et des chiers

    Figure 6.2: Organisation dun disque dur

    and could in certain setups cause problems with:1) software that runs at boot time (e.g., old versions of LILO)2) booting and partitioning software from other OSs

    (e.g., DOS FDISK, OS/2 FDISK)

    Command (m for help): mCommand action

    a toggle a bootable flagb edit bsd disklabelc toggle the dos compatibility flagd delete a partitionl list known partition typesm print this menun add a new partitiono create a new empty DOS partition tablep print the partition tableq quit without saving changess create a new empty Sun disklabelt change a partition's system idu change display/entry unitsv verify the partition tablew write table to disk and exitx extra functionality (experts only)

    Command (m for help): p

    Disk /dev/sda: 100.0 GB, 100030242816 bytes255 heads, 63 sectors/track, 12161 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytes

    43

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    Figure 6.3: Organisation dune partition

    Device Boot Start End Blocks Id System/dev/sda1 * 1 1459 11719386 7 HPFS/NTFS/dev/sda2 1460 2638 9470317+ 83 Linux/dev/sda3 2639 12161 76493497+ 5 Extended/dev/sda5 11796 12161 2939863+ 82 Linux swap / Solaris/dev/sda6 2639 11795 73553571 83 Linux

    Partition table entries are not in disk order

    Command (m for help): q

    #

    On voit en particulier le nombre total des cylindres, la taille de chaque cylindre, et pourchaque partition, le cylindre de dbut et de n. Ici, on voit que /dev/sda1 est une partitionNTFS (type de partition pour windows), les partitions /dev/sda2 et /dev/sda6 sont de typeext3 (linux), et la partition /dev/sda5 est un espace de swap (utilise par les programmes lorsdun dpassement de la RAM.

    Les types de partition quil est (actuellement) possible de crer avec fdisc sont :0 Empty 1e Hidden W95 FAT1 80 Old Minix be Solaris boot1 FAT12 24 NEC DOS 81 Minix / old Lin bf Solaris2 XENIX root 39 Plan 9 82 Linux swap / So c1 DRDOS/sec (FAT-3 XENIX usr 3c PartitionMagic 83 Linux c4 DRDOS/sec (FAT-4 FAT16

  • Chapitre 6 : Gestion du disque dr et des chiers

    8 AIX 4e QNX4.x 2nd part 88 Linux plaintext de Dell Utility9 AIX bootable 4f QNX4.x 3rd part 8e Linux LVM df BootIta OS/2 Boot Manag 50 OnTrack DM 93 Amoeba e1 DOS accessb W95 FAT32 51 OnTrack DM6 Aux 94 Amoeba BBT e3 DOS R/Oc W95 FAT32 (LBA) 52 CP/M 9f BSD/OS e4 SpeedStore W95 FAT16 (LBA) 53 OnTrack DM6 Aux a0 IBM Thinkpad hi eb BeOS fsf W95 Ext'd (LBA) 54 OnTrackDM6 a5 FreeBSD ee EFI GPT10 OPUS 55 EZ-Drive a6 OpenBSD ef EFI (FAT-12/16/11 Hidden FAT12 56 Golden Bow a7 NeXTSTEP f0 Linux/PA-RISC b12 Compaq diagnost 5c Priam Edisk a8 Darwin UFS f1 SpeedStor14 Hidden FAT16

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    /dev/sda2 / ext3 defaults,errors=remount-ro 0 1/dev/sda6 /home ext3 defaults 0 2/dev/sda5 none swap sw 0 0/dev/scd0 /media/cdrom0 udf,iso9660 user,noauto 0 0/dev/sdb1 /mnt/usb vfat user,noauto 0 0/dev/mmcblk0p1 /mnt/sdcard vfat user,noauto 0 0/dev/sda1 /mnt/windows ntfs uid=1001,autoPar exemple ici le lecteur de CDROM, la clef USB et la caret SD peuvent tre monte partous les utilisateurs (option user). Par contre, laccs la partition windows est rsearv lutilisateur dUID 1001 (voir /etc/passwd pour trouver lIUD dun utilisateur).

    On peut voir la liste des partitions qui sont montes, ainsi que lespace libre dans chacunedelles, par la commande df :# dfFilesystem 1K-blocks Used Available Use% Mounted on/dev/sda2 9322492 8362396 486584 95% /tmpfs 517544 0 517544 0% /lib/init/rwudev 10240 72 10168 1% /devtmpfs 517544 0 517544 0% /dev/shm/dev/sda6 72397784 34359196 34360912 50% /home/dev/sda1 11719384 6920456 4798928 60% /mnt/windows/dev/sdb1 3991136 126880 3864256 4% /mnt/usb/dev/scd0 713064 713064 0 100% /media/cdrom0/dev/mmcblk0p1 2010752 528224 1482528 27% /mnt/sdcard

    6.1.3 Fichiers, inodes et liensUn inode identie un chier et dcrit ses proprites, telles quin peut les vois par ls -l :

    donnes sur le propritaire :UID et GID ; Droits daccs ; Dates de cration, de dernire modication, de dernier accs ; Nombre de chier ayant cet inode (en cas de liens durs) ; Taille en octets ; Adresse dun block de donnes.Il existe dans un disque dur des liens, lorsque plusieurs noms de chiers conduisent aux

    mmes donnes. Ces liens sont de deux types :1. On parle dun lien dur lorsque deux noms de chiers sont associs au mme inode (les

    dirents liens durs a exactement les mmes proprits mais des noms dirents). Enparticulier, ladresse des donnes est la mme dans tous les liens durs. La commande rmdcrmente le nombre de liens durs. La suppression nest eective que lorsque le nombrede lien durs (visible par ls -l ou stat) devient nul. Les liens durs sont crs par lacommande ln.

    46

  • Chapitre 6 : Gestion du disque dr et des chiers

    2. On parle dun lien symbolique lorsque le block de donnes dun chier contient ladressedu bloc de donnes dun autre chier. Les liens symboliques sont crs par la commandeln -s (optin -s).

    6.2 Obtenir les informations sur un chier en COn peut obtenir les informations sur un chier ou un rpertoire (ID du propritaire, tailleinode,...) avec la fonction stat.Exemple. Lexemple suivant ache des informations sur le chier dont le nom est pass enargument.

    #include #include #include #include #include #include

    int main(int argc, char**argv){struct stat st; /* pour rcuprer les informations sur un fichier */struct tm *temps; /* pour traduire les dates (voir ctime(3)) */

    if (argc != 2){

    fprintf(stderr, "Usage : %s nom_de_fichier\n", argv[0]);exit(1);

    }if (stat(argv[1], &st)!= 0){

    perror("Erreur d'accs au fichier\n");exit(1);

    }if (S_ISDIR(st.st_mode))printf("Le nom %s correspond un rpertoire\n", argv[1]);

    if (S_ISREG(st.st_mode)){

    printf("%s est un fichier ordinaire\n", argv[1]);printf("La taille du fichier en octets est %d\n", st.st_size);temps = localtime(&st.st_mtime);printf("Le jour de dernire mofification est %d/%d/%d\n",

    temps->tm_mday, temps->tm_mon+1, temps->tm_year+1900);}

    return 0;}

    Exemple de trace dexcution de ce programme :

    47

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    $ gcc testStat.c -o testStat$ ls -l-rwxr-xr-x 1 remy remy 8277 Feb 1 13:03 testStat-rw-r--r-- 1 remy remy 935 Feb 1 13:03 testStat.c$ ./testStat testStat.ctestStat.c est un fichier ordinaireLa taille du fichier en octets est 935Le jour de dernire mofification est 1/2/2008

    Toutes les informatione renvoyes par stat sont stockes dans la structure stat, dont ladclaration est la suivante (voir stat(2))

    struct stat {dev_t st_dev; /* ID of device containing file */ino_t st_ino; /* inode number */mode_t st_mode; /* protection */nlink_t st_nlink; /* number of hard links */uid_t st_uid; /* user ID of owner */gid_t st_gid; /* group ID of owner */dev_t st_rdev; /* device ID (if special file) */off_t st_size; /* total size, in bytes */blksize_t st_blksize; /* blocksize for filesystem I/O */blkcnt_t st_blocks; /* number of blocks allocated */time_t st_atime; /* time of last access */time_t st_mtime; /* time of last modification */time_t st_ctime; /* time of last status change */

    };

    6.3 Parcourir les rpertoires en CLa fonction opendir permet douvrir un rpertoire et retourne un pointeur de rpertoire (typeDIR*). On peut alors parcourir la liste des lments (chiers, liens ou rpertoires) qui sontcontenus dans ce rpertoire (y compris les rpertoires "." et "..").

    #include #include #include #include

    int main(int argc, char**argv){

    DIR *dir;struct dirent *ent;int i;

    for (i=1 ; i

  • Chapitre 6 : Gestion du disque dr et des chiers

    {dir = opendir(argv[i]); /* ouverture du rpertoire */if (dir==NULL){fprintf(stderr, "Erreur d'ouverture du rperoire %s\(\backslash\)n",

    argv[i]);fprintf(stderr, "Droits inssufisant ou rpertoire incorrect\n");exit(1);

    }printf("Rpertoire %s\n", argv[i]);while ((ent=readdir(dir)) != NULL) /* on parcourt la liste */printf("%s ", ent->d_name);

    }printf("\n");retu 0;

    }

    Exemple de trace dexcution de ce programme :

    $ gcc parcoursRep.c -o parcoursRep$ lsparcoursRep parcoursRep.c$ ./parcoursRep .Rpertoire .parcoursRep .. parcoursRep.c .

    6.4 Descripteurs de chiersUn descripteur de chier est un entier qui identie un chier dans un programme C. Ne pasconfondre un descripteur de chier avec un pointeur de chier. La fonction fdopen permetdobtenir un pointeur de chier partir dun descripteur.

    6.4.1 Ouverture et cration dun chierLa fonction open permet dobtenir un descripteur de chier partir du nom de chier surle disque, de la mme faon que fopen permet dobtenir un pointeur de chier. Une grandedirence est que la fonction open ore beaucoup plus doptions pour tester les permissions.Le prototype de la fonction open est le suivant :

    int open(const char *pathname, int flags, mode_t mode);

    La fonction retourne une valeur strictement ngative en cas derreur.Le paramtre flags permet de prciser si, pour le programme, le chier est en lecture seule

    (masque O_RDONLY), criture seule (masque O_WRONLY), ou lecture-criture (masque O_RDWR).Par un ou bit bit (|), on peut aussi prciser si le chier est ouvert en mode ajout (masqueO_APPEND), ou si le chier doit tre cras (masque O_TRUNC) ou ouvert en mode cration (sile chier nexiste pas il sera cr, masque O_CREAT).

    49

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    Le mode permet de xer les permissions lors de la cration du chier (le cas chant),lorsque le paramtre flag est spci avec O_CREAT. Les permissions peuvent tre dnies pardes chires octaux (7 pour rwx, 0 pour aucune permission) pour lutilisateur, le groupe et lesautres ustilisateur. Cependant, un et bit bit est eectu avec la ngation bit bit de variabledenvironnement umask (mode & mask)). (voir man umask et man open(2))

    6.4.2 Lecture et criture via un descripteurPour crire des octets via descripteur de chier, on utilise la fonction write :

    ssize_t write(int descripteur1, const void *bloc, size_t taille);

    Le chier (ou tube ou socket...) doit tre ouvert en criture (options O_WRONLY, O_RDWR) Lataille est le nombre doctets quon souhaite crire, et le bloc est un pointeur vers la mmoirecontenant ces octets.

    Pour lire des octets via un descripteur de chiers, on utilise la fonction read :

    ssize_t read(int descripteur0, void *bloc, size_t taille);

    Le chier (ou tube ou socket...) doit tre ouvert en lecture (options O_RDONLY, O_RDWR)On peut aussi utiliser fdopen pour obtenir un FILE* ce qui permet dutiliser des fonctions

    plus haut niveau telles que fprintf et fscanf ou fread et fwrite.

    6.5 ExercicesExercice 6.1 ([) sortez vos calculettes !] Soit un dique du ayant 2 plateaux, chaque face ayant1000 cylindres et 60 secteurs, chaque secteur ayant 1024 octets.a) Calculez la capacit totale du disque.b) Caluculez la position (le numro) de doctet 300 su secteur 45 du cylindre 350 de la face 2du premier plateau.c) Sur quel secteur et en quelle position se trouve loctet numro 78000000 ?

    Exercice 6.2 () Ecrire un programme qui prend en argument des noms de rpertoire et achela liste des chiers de ces rpertoires qui ont une taille suprieure ( peu prs) 1Mo aveclUID du propritaire du chier.

    Exercice 6.3 (a)) crire un programme qui saisit au clavier un tableau dentiers et sauve-garde ce tableau au format binaire dans un chier ayant permission en criture pour le groupedu chier et en lecture seule pour les autres utilisateurs.b) crire une programme qui charge en mmoire un tableau dentiers tel que gnr au a). Lechier dentiers ne contient pas le nombre dlments. Le programme doit fonctionner pour unnombre quelconque de donnes entires dans le chier.

    50

  • Chapitre 7

    Signaux

    7.1 Prliminaire : Pointeurs de fonctionsUn pointeur de fonctions en C est une variable qui permet de dsigner une fonction C. Commenimporte quelle variable, on peut mettre un pointeur de fonctions soit en variable dans unefonction, soit en paramtre dans une fonction.

    On dclare un pointeur de fonction comme un prototype de fonction, mais on ajoute unetoile () devant le nom de la fonction. Dans lexemple suivant, on dclare dans le main unpointeur sur des fonctions qui prennent en paramtre un int, et un pointeur sur des fonctionsqui retournent un int.

    #include

    int SaisisEntier(void){

    int n;printf("Veuillez entrer un entier : ");scanf("%d", &n);return n;

    }

    void AfficheEntier(int n){

    printf("L'entier n vaut %d\(\backslash\)n", n);}

    int main(void){void (*foncAff)(int); /* dclaration d'un pointeur foncAff */int (*foncSais)(void); \em{}/*dclaration d'un pointeur foncSais */inte entier;

    foncSais = SaisisEntier; {/* affectation d'une fonction */foncAff = AfficheEntier; /* affectation d'une fonction */

    51

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    entier = foncSais(); /* on excute la fonction */foncAff(entier); /* on excute la fonction */\retu 0;

    }

    Dans lexemple suivant, la fonction est passe en paramtre une autre fonction, puisexcute.

    #include

    int SaisisEntier(void){

    int n;printf("Veuillez entrer un entier : ");scanf("%d", &n);getchar();return n;

    }

    void AfficheDecimal(int n){

    printf("L'entier n vaut %d\n", n);}

    void AfficheHexa(int n)}

    printf("L'entier n vaut %x\(\backslash\)n", n);}

    void ExecAffiche(void (*foncAff)(int), int n){foncAff(n); /* excution du paramtre */

    }

    int main(void){int (*foncSais)(\void); /*dclaration d'un pointeur foncSais */int entier;char rep;

    foncSais = SaisisEntier; /* affectation d'une fonction */entier = foncSais(); /* on excute la fonction */

    puts("Voulez-vous afficher l'entier n en dcimal (d) ou en hexa (x) ?");rep = getchar();/* passage de la fonction en paramtre : */if (rep == 'd')

    52

  • Chapitre 7 : Signaux

    ExecAffiche(AfficheDecimal, entier);if (rep == 'x')ExecAffiche(AfficheHexa, entier);

    return 0;}

    7.2 Les principaux signauxUn signal permet de prvenir un processus quun vennement particulier cest produit dans lesystme pour dans un (ventuellement autre) processus. Certains signaux sont envoys par lenoyau (comme en cas derreur de violation mmoire ou division par 0), mais un programmeutilisateur peut envoyer un signal avec la fonction ou la commande kill, ou encore par certainescombinaison de touches au clavier (comme Ctrl-C). Un utilisateur ( lexception de root) nepeut envoyer un signal qu un processus dont il est propritaire.

    Les principaux signaux (dcrits dans la norme POSIX.1-1990) (faire man 7 signal pourdes complments) sont expliqus sur le table 7.1.

    7.3 Envoyer un signalLa mthode la plus gnrale pour envoyer un signal est dutiliser soit la commande sheelkill(1), soit la fonction C kill(2).

    7.3.1 La commande killLa commande kill prend une option -signal et un pid.Exemple.

    $ kill -SIGINT 14764 {\em# interromp processus de pid 14764}$ kill -SIGSTOP 22765 {\em# stoppe temporairement le process 22765}$ kill -SIGCONT 22765 {\em# reprend l'excution du prcessus 22765}

    Complments

    Le signal par dfaut, utilis en cas est SIGTERM, qui termine le processus. On peut utiliser un PID ngatif pour indiquer un groupe de processus, tel quil est indiqu

    par le PGID en utilisant loption -j de la commande ps. Cela permet denvoyer un signal tout un groupe de processus.

    7.3.2 La fonction killLa fonction C kill est similaire la commande kill du shell. Elle a pour prototype :

    int kill(pid_t pid, int signal);

    53

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    Signal Valeur Action CommentaireSIGHUP 1 Term Terminaison du leader de session (exemple : terminaison

    du terminal qui a lanc le programme ou logout)SIGINT 2 Term Interruption au clavier (par Ctrl-C par dfaut)SIGQUIT 3 Core Quit par frappe au clavier (par Ctr AltGr \ par dfaut)SIGILL 4 Core Dtection dune instruction illgaleSIGABRT 6 Core Avortement du processus par la fonction abort(3)

    (gnralement appele par le programmeuren cas de dtection dune erreur)

    SIGFPE 8 Core Exception de calcul ottant (division par 0racine carres dun nombre ngatif, etc...)

    SIGKILL 9 Term Processus tu (kill)SIGSEGV 11 Core Violation mmoire. Le comportement par dfaut

    termine le processus sur une erreur de segmentationSIGPIPE 13 Term Erreur de tube : tentative dcrire dans un tube

    qui na pas de sortieSIGALRM 14 Term Signal de timer suite un appel de alarm(2)

    qui permet denvoyer un signal une certaine dateSIGTERM 15 Term Signal de terminaisonSIGUSR1 30,10,16 Term Signal utilisateur 1 : permet au programmeur

    de dnir son propre signalpour une utilisation libre

    SIGUSR2 31,12,17 Term Signal utilisateur 23 : permet au programmeurde dnir son propre signalpour une utilisation libre

    SIGCHLD 20,17,18 Ign Lun des processus ls est stopp(par SIGSTOP ou termin)

    SIGSTOP 17,19,23 Stop Stoppe temporairement le processus. Le processusse ge jusqu recevoir un signal SIGCONT

    SIGCONT 19,18,25 Cont Reprend lexcution dun processus stopp.SIGTSTP 18,20,24 Stop Processus stopp partir dun terminal (tty)

    (par Ctrl-S par dfaut)SIGTTIN 21,21,26 Stop saisie dans un terminal (tty) pour un

    processus en tche de fond (lanc avec &)SIGTTOU 22,22,27 Stop Achage dans un terminal (tty) pour un

    processus en tche de fond (lanc avec &)

    Table 7.1: Liste des principaux signaux

    54

  • Chapitre 7 : Signaux

    Exemple. Le programme suivant tue le processus dont le PID est pass en argument seulementsi lutilisateur conrme.

    #include #include #include #include

    int main(int argc, char **argv){pid_t pidToSend;char rep;if (argc != 2){

    fprintf(stderr, "Usage %s pid\n", argv[0]);exit(1);

    }pidToSend = atoi(argv[1]);printf("Etes-vous sr de vouloir tuer le processus %d ? (o/n)",

    pidToSend);rep = getchar();if (rep == 'o')

    kill(pidToSend, SIGTERM);return 0;

    }

    7.3.3 Envoi dun signal par combinaison de touches du clavierUn certain nombre de signaux peuvent tre envoy partir du terminal par une combinaisonde touche. On peut voir ces combinaisons de touches par stty -a :

    stty -aspeed 38400 baud; rows 48; columns 83; line = 0;intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?;swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;flush = ^O; min = 1; time = 0;-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclcixany imaxbel iutf8opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctlechoke

    55

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    7.4 Capturer un signal7.4.1 Crer un gestionnaire de signal (signal handler)Un gestionnaire de signal (signal handler) permet de changer le comportement du processuslors de la rception du signal (par exemple, se terminer).

    Voici un exemple de programme qui sauvegarde des donnes avant de se terminer lors duneinterruption par Ctrl-C dans le terminal. Le principe est de modier le comportement lors dela rception du signal SIGINT.#include #include #include #include

    int donnees[5];

    void gestionnaire(int numero){FILE *fp;int i;if (numero == SIGINT){

    printf("\nSignal d'interruption, sauvegarde...\n");fp = fopen("/tmp/sauve.txt", "w");for (i=0 ; i

  • Chapitre 7 : Signaux

    for (i=0 ; i

  • Rmy Malgouyres, http ://www.malgouyres.fr/ Initiation la Programmation Systme

    void gestionnaire(int numero){FILE *fp;int i;if (numero == SIGFPE){

    printf("Signal d'erreur de calcul flottant.\n");printf("Entrez x diffrent de zro : ");scanf("%d", &x);siglongjmp(env, 1); /* retour au sigsetjmp */

    }}

    int main(void){int i;char continuer='o';struct sigaction action;action.sa_handler = gestionnaire; /* pointeur de fonction */sigemptyset(&action.sa_mask); /* ensemble de signaux vide */action.sa_flags = 0; /* options par dfaut */if (sigaction(SIGFPE, &action, NULL) != 0){

    fprintf(stderr, "Erreur sigaction\n");exit(1);

    }

    printf("Veuillez entrer x et y : ");scanf("%d %d", &x, &y); getchar();sigsetjmp(env, 1); /* on mmorise la ligne */z = y/x; /* opration qui risque de provoquer une erreur */printf("%d/%d=%d\(\backslash\)n", y, x, z);return 0;

    }

    Voici la trace de ce programme lorsquon saisit un dnominateur x gal 0 :

    Veuillez entrer x et y : 0 8Signal d'erreur de calcul flottant.Entrez x diffrent de zro : 0Signal d'erreur de calcul flottant.Entrez x diffrent de zro : 28/2=4

    58

  • Chapitre 7 : Signaux

    7.5 ExercicesExercice 7.1 () Ecrire un programme qui cre un ls qui fait un calcul sans n. Le processuspre propose alors un menu :

    Lorsque lutilisateur appuie sur la touche 's', le processus pre endort son ls. Lorsque lutilisateur appuie sur la touche 'r', le processus pre redmare son ls. Lorsque lutilisateur appuie sur la touche 'q', le processus prs tue son ls avant de seterminer.

    Exercice 7.2 () Ecrire un programme saisit.c qui saisit un int au clavier, et lenregistredans un chier /tmp/entier.txt. crire un programme affiche.c qui attend (avec sleep) unsignal utilisateur du programme saisit.c. Lorsque lentier a t saisi, le programme affiche.cache la valeur de lentier.

    Exercice 7.3 (a)) crire un programme qui cre 5 processus ls qui font une boucle while1. Le processus pre proposera dans une boucle sans n un menu lutilisateur :

    Endormir un ls ; Rveiller un ls ; Terminer un ls ;

    b) Modier les ls pour quils achent un message lorsquils sont tus. Le processus preachera un autre message sil est tu.

    Exercice 7.4 () Ecrire un programme qui saisit les valeurs dun tableau dentier tab de nlments allou dynamiquement. Lentier n sera saisi au clavier. Le programme ache la valeurdun lment tab[i] o i est saisi au clavier. En cas derreur de segmentation le programmefait resaisir la valeur de i avant de lacher.

    59

  • Chapitre 8

    Programmation rseaux

    Le but de la programmation rseau est de permettre des programmes de dialoguer (dchangerdes donnes) avec dautres programmes qui se trouvent sur des ordinateurs distants, connectspar un rseau. Nous verrons tout dabord des notions gnrales telles que les adresse IP oule protocole TCP, avant dtudier les sockets unix/linux qui permettent des programmesdtablir une communication et de dialoguer.

    8.1 Adresses IP et MACChaque interface de chaque ordinateur sera identi par

    Son adresse IP : une adresse IP (version 4, protocole IPV4) permet didentier un hteet un sous-rseau. Ladresse IP est code sur 4 octets. (les adresses IPV6, ou IP nextgeneration seront codes sur 6 octets).

    Ladresse mac de sa carte rseau (carte ethernet ou carte wi) ;

    Une adresse IP permet didentier un hte. Une passerelle est un ordinateur qui possdeplusieurs interfaces et qui transmet les paquets dune interface lautre. La passerelle peut ainsifaire communiquer dirents rseaux. Chaque carte rseau possde une adresse MAC uniquegarantie par le constructeur. Lorsquun ordinateur a plusieurs plusieurs interfaces, chacunepossde sa propre adresse MAC et son adresse IP. On peut voir sa conguration rseau parifconfig.