Vous êtes sur la page 1sur 206

Programmation avance sous Linux

par Michal Todorovic (Autres articles) (Blog)


Date de publication : 28/04/2007 Dernire mise jour : 28/04/2007

Ce document est une traduction du livre Advanced Linux Programming (http:// www.advancedlinuxprogramming.com) de Mark Mitchell, Jeffrey Oldham et Alex Samuel. La traduction originale est disponible sur http://www.advancedlinuxprogramming-fr.org

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

1 - Pour Commencer................................................................................................................................................... 7 1-1 - L'diteur Emacs.............................................................................................................................................7 1-1-1 - Ouvrir un Fichier Source C ou C++..................................................................................................... 7 1-1-2 - Formatage Automatique........................................................................................................................8 1-1-3 - Coloration Syntaxique........................................................................................................................... 8 1-2 - Compiler avec GCC...................................................................................................................................... 8 1-2-1 - Compiler un Fichier Source Isol......................................................................................................... 9 1-2-2 - Lier les Fichiers Objet.........................................................................................................................10 1-3 - Automatiser le Processus avec GNU Make................................................................................................11 1-4 - Dboguer avec le Dbogueur GNU (GDB).................................................................................................13 1-4-1 - Compiler avec les Informations de Dbogage................................................................................... 13 1-4-2 - Lancer GDB........................................................................................................................................ 13 1-5 - Obtenir Plus d'Informations......................................................................................................................... 15 1-5-1 - Pages de Manuel................................................................................................................................15 1-5-2 - Info...................................................................................................................................................... 16 1-5-3 - Fichiers d'entte..................................................................................................................................16 1-5-4 - Code Source....................................................................................................................................... 16 2 - crire des Logiciels GNU/Linux de Qualit......................................................................................................... 18 2-1 - Interaction Avec l'Environnement d'Excution............................................................................................ 18 2-1-1 - La Liste d'Arguments.......................................................................................................................... 18 2-1-2 - Conventions de la Ligne de Commande GNU/Linux..........................................................................19 2-1-3 - Utiliser getopt_long............................................................................................................................. 19 2-1-4 - E/S Standards.....................................................................................................................................22 2-1-5 - Codes de Sortie de Programme.........................................................................................................23 2-1-6 - L'Environnement..................................................................................................................................24 2-1-7 - Utilisation de Fichiers Temporaires.....................................................................................................25 2-1-7-1 - Utilisation de mkstemp............................................................................................................... 26 2-1-7-2 - Utilisation de tmpfile................................................................................................................... 27 2-2 - Crer du Code Robuste.........................................................................................................................27 2-2-1 - Utiliser assert.................................................................................................................................27 2-2-2 - Problmes Lors d'Appels Systme.....................................................................................................29 2-2-3 - Codes d'Erreur des Appels Systme..................................................................................................29 2-2-4 - Erreurs et Allocation de Ressources.................................................................................................. 31 2-3 - crire et Utiliser des Bibliothques............................................................................................................. 32 2-3-1 - Archives...............................................................................................................................................33 2-3-2 - Bibliothques Partages..................................................................................................................... 34 2-3-2-1 - Utiliser LD_LIBRARY_PATH.......................................................................................................35 2-3-3 - Bibliothques Standards..................................................................................................................... 35 2-3-4 - Dpendances entre Bibliothques...................................................................................................... 35 2-3-5 - Avantages et Inconvnients................................................................................................................36 2-3-6 - Chargement et Dchargement Dynamiques...................................................................................... 37 3 - Processus.............................................................................................................................................................39 3-1 - Aperu des Processus................................................................................................................................ 39 3-1-1 - Identifiants de Processus....................................................................................................................39 3-1-2 - Voir les Processus Actifs.................................................................................................................... 39 3-1-3 - Tuer un Processus..............................................................................................................................40 3-2 - Crer des Processus...................................................................................................................................41 3-2-1 - Utiliser system.....................................................................................................................................41 3-2-2 - Utiliser fork et exec.............................................................................................................................41 3-2-2-1 - Appeler fork et exec...................................................................................................................41 3-2-2-2 - Utiliser la Famille de exec..........................................................................................................42 3-2-2-3 - Utiliser fork et exec.................................................................................................................... 43 3-2-3 - Ordonnancement de Processus......................................................................................................... 43 3-3 - Signaux........................................................................................................................................................ 44 3-4 - Fin de Processus........................................................................................................................................ 46 3-4-1 - Attendre la Fin d'un Processus.......................................................................................................... 47 3-4-2 - Les Appels Systme wait................................................................................................................... 47 3-4-3 - Processus Zombies............................................................................................................................ 48
-2Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

3-4-4 - Librer les Ressources des Fils de Faon Asynchrone..................................................................... 49 4 - Threads................................................................................................................................................................ 51 4-1 - Cration de Threads................................................................................................................................... 51 4-1-1 - Transmettre des Donnes un Thread............................................................................................. 52 4-1-2 - Synchroniser des Threads..................................................................................................................53 4-1-3 - Valeurs de Retour des Threads..........................................................................................................54 4-1-4 - Plus d'Informations sur les Identifiants de Thread............................................................................. 55 4-1-5 - Attributs de Thread............................................................................................................................. 55 4-2 - Annulation de Thread.................................................................................................................................. 56 4-2-1 - Threads Synchrones et Asynchrones.................................................................................................57 4-2-2 - Sections Critiques Non-Annulables.................................................................................................... 57 4-2-3 - Quand Utiliser l'Annulation de Thread?..............................................................................................58 4-3 - Donnes Propres un Thread................................................................................................................... 58 4-3-1 - Libration de Ressources de Thread en C++.................................................................................... 61 4-4 - Synchronisation et Sections Critiques.........................................................................................................62 4-4-1 - Conditions de Concurrence Critique...................................................................................................62 4-4-2 - Mutexes...............................................................................................................................................63 4-4-3 - Interblocage de Mutexes.................................................................................................................... 65 4-4-4 - Vrification de Mutex non Bloquante..................................................................................................66 4-4-5 - Smaphores pour les Threads........................................................................................................... 66 4-4-6 - Variables de Condition........................................................................................................................68 4-4-7 - Interblocage avec Deux Threads ou Plus.......................................................................................... 71 4-5 - Implmentation des Threads sous GNU/Linux........................................................................................... 72 4-5-1 - Gestion de Signaux............................................................................................................................ 73 4-5-2 - L'appel Systme clone........................................................................................................................73 4-6 - Comparaison Processus/Threads............................................................................................................... 73 5 - Communication Interprocessus............................................................................................................................75 5-1 - Mmoire Partage....................................................................................................................................... 75 5-1-1 - Communication Locale Rapide........................................................................................................... 76 5-1-2 - Le Modle Mmoire............................................................................................................................ 76 5-1-3 - Allocation.............................................................................................................................................76 5-1-4 - Attachement et Dtachement............................................................................................................. 77 5-1-5 - Contrler et Librer la Mmoire Partage.......................................................................................... 77 5-1-6 - Programme Exemple.......................................................................................................................... 78 5-1-7 - Dbogage............................................................................................................................................78 5-1-8 - Avantages et Inconvnients................................................................................................................79 5-2 - Smaphores de Processus......................................................................................................................... 79 5-2-1 - Instanciation et Libration...................................................................................................................79 5-2-2 - Initialisation des Smaphores.............................................................................................................80 5-2-3 - Oprations d'Attente et de Rveil.......................................................................................................81 5-2-4 - Dbogage des Smaphores............................................................................................................... 82 5-3 - Mmoire Mappe.........................................................................................................................................82 5-3-1 - Mapper un Fichier Ordinaire...............................................................................................................82 5-3-2 - Programmes Exemples.......................................................................................................................83 5-3-3 - Accs Partag un Fichier................................................................................................................ 84 5-3-4 - Mises en Correspondance Prives.....................................................................................................85 5-3-5 - Autres Utilisations de mmap...............................................................................................................85 5-4 - Tubes........................................................................................................................................................... 86 5-4-1 - Crer des Tubes................................................................................................................................. 86 5-4-2 - Communication entre Processus Pre et Fils.................................................................................... 86 5-4-3 - Rediriger les Flux d'Entre, de Sortie et d'Erreur Standards..............................................................88 5-4-4 - popen et pclose.................................................................................................................................. 89 5-4-5 - FIFO.................................................................................................................................................... 90 5-4-5-1 - Crer une FIFO.......................................................................................................................... 90 5-4-5-2 - Accder une FIFO.................................................................................................................. 90 5-4-5-3 - Diffrences avec les Canaux Nomms Windows...................................................................... 91 5-5 - Sockets...................................................................................................................................................91 5-5-1 - Concepts relatifs aux Sockets.......................................................................................................91
-3Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-5-2 - Appels Systme.................................................................................................................................. 92 5-5-2-1 - Crer et Dtruire des Sockets................................................................................................... 92 5-5-2-2 - Appeler connect......................................................................................................................... 93 5-5-2-3 - Envoyer des Informations.......................................................................................................... 93 5-5-3 - Serveurs.............................................................................................................................................. 93 5-5-4 - Sockets Locaux...................................................................................................................................93 5-5-5 - Un Exemple Utilisant les Sockets Locaux.......................................................................................... 94 5-5-6 - Sockets Internet.................................................................................................................................. 96 5-5-7 - Couples de Sockets............................................................................................................................98 6 - Priphriques....................................................................................................................................................... 99 6-1 - Types de Priphriques...............................................................................................................................99 6-2 - Numros de Priphrique......................................................................................................................... 100 6-3 - Fichiers de Priphriques..........................................................................................................................100 6-3-1 - Le Rpertoire /dev............................................................................................................................ 101 6-3-2 - Accder des Priphriques en Ouvrant des Fichiers.................................................................... 101 6-4 - Priphriques Matriels............................................................................................................................. 102 6-5 - Priphriques Spciaux.............................................................................................................................104 6-5-1 - /dev/null............................................................................................................................................. 104 6-5-2 - /dev/zero............................................................................................................................................ 105 6-5-3 - /dev/full.............................................................................................................................................. 105 6-5-4 - Dispositifs de Gnration de Nombres Alatoires............................................................................105 6-5-5 - Priphriques Loopback................................................................................................................... 107 6-6 - PTY............................................................................................................................................................ 109 6-6-1 - Exemple d'utilisation des PTY.......................................................................................................... 109 6-7 - ioctl.............................................................................................................................................................110 7 - Le Systme de Fichiers /proc............................................................................................................................ 111 7-1 - Obtenir des Informations partir de /proc................................................................................................ 111 7-2 - Rpertoires de Processus......................................................................................................................... 113 7-2-1 - /proc/self............................................................................................................................................ 114 7-2-2 - Liste d'Arguments d'un Processus................................................................................................... 114 7-2-3 - Environnement de Processus........................................................................................................... 116 7-2-4 - Excutable de Processus................................................................................................................. 117 7-2-5 - Descripteurs de Fichiers d'un Processus......................................................................................... 117 7-2-6 - Statistiques Mmoire de Processus................................................................................................. 119 7-2-7 - Statistiques sur les Processus..........................................................................................................119 7-3 - Informations sur le Matriel.......................................................................................................................119 7-3-1 - Informations sur le Processeur.........................................................................................................119 7-3-2 - Informations sur les Priphriques................................................................................................... 120 7-3-3 - Informations sur le Bus PCI............................................................................................................. 120 7-3-4 - Informations sur le Port Srie...........................................................................................................120 7-4 - Informations sur le Noyau......................................................................................................................... 120 7-4-1 - Informations de Version.................................................................................................................... 120 7-4-2 - Noms d'Hte et de Domaine............................................................................................................ 121 7-4-3 - Utilisation Mmoire........................................................................................................................... 121 7-5 - Lecteurs et Systmes de Fichiers.............................................................................................................122 7-5-1 - Systmes de Fichiers....................................................................................................................... 122 7-5-2 - Lecteurs et Partitions........................................................................................................................ 122 7-5-3 - Points de Montage............................................................................................................................123 7-5-4 - Verrous.............................................................................................................................................. 124 7-6 - Statistiques Systme................................................................................................................................. 125 8 - Appels Systme Linux....................................................................................................................................... 126 8-1 - Utilisation de strace................................................................................................................................... 126 8-2 - access : Tester les Permissions d'un Fichier............................................................................................ 127 8-3 - fcntl : Verrous et Oprations sur les Fichiers............................................................................................128 8-4 - fsync et fdatasync : Purge des Tampons Disque......................................................................................130 8-5 - getrlimit et setrlimit : Limites de Ressources.............................................................................................131 8-6 - getrusage : Statistiques sur les Processus............................................................................................... 132 8-7 - gettimeofday : Heure Systme.................................................................................................................. 133
-4Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

8-8 - La famille mlock : Verrouillage de la Mmoire Physique.......................................................................... 134 8-9 - mprotect : Dfinir des Permissions Mmoire............................................................................................ 135 8-10 - nanosleep : Pause en Haute Prcision...................................................................................................137 8-11 - readlink : Lecture de Liens Symboliques.................................................................................................138 8-12 - sendfile : Transferts de Donnes Rapides.............................................................................................. 139 8-13 - setitimer : Crer des Temporisateurs...................................................................................................... 140 8-14 - sysinfo : Rcupration de Statistiques Systme..................................................................................... 141 8-15 - uname...................................................................................................................................................... 141 9 - Code Assembleur en Ligne............................................................................................................................... 143 9-1 - Quand Utiliser du Code Assembleur.........................................................................................................143 9-2 - Assembleur en Ligne Simple.................................................................................................................... 144 9-2-1 - Convertir un asm en Instructions Assembleur..................................................................................144 9-3 - Syntaxe Assembleur Avance...................................................................................................................145 9-3-1 - Instructions Assembleur....................................................................................................................145 9-3-2 - Sorties............................................................................................................................................... 145 9-3-3 - Entres.............................................................................................................................................. 146 9-3-4 - Dclaration des Modifications........................................................................................................... 146 9-3-5 - Exemple............................................................................................................................................ 147 9-4 - Problmes d'Optimisation..........................................................................................................................148 9-5 - Problmes de Maintenance et de Portabilit............................................................................................ 148 10 - Scurit............................................................................................................................................................ 149 10-1 - Utilisateurs et Groupes............................................................................................................................149 10-1-1 - Le superutilisateur...........................................................................................................................150 10-2 - User et Group ID de Processus..............................................................................................................150 10-3 - Permissions du Systme de Fichiers......................................................................................................151 10-3-1 - Faille de scurit: les Programmes non Excutables.................................................................... 154 10-3-2 - Sticky Bits....................................................................................................................................... 154 10-4 - ID Rels et Effectifs................................................................................................................................ 155 10-4-1 - Programmes Setuid........................................................................................................................ 156 10-5 - Authentifier les utilisateurs...................................................................................................................... 157 10-6 - Autres failles de scurit.........................................................................................................................159 10-6-1 - Dpassement de tampon................................................................................................................159 10-6-2 - Conditions de concurrence critique dans /tmp............................................................................... 161 10-6-3 - Utilisation de system ou popen...................................................................................................... 163 11 - Autres Outils de Dveloppement..................................................................................................................... 165 11-1 - Analyse Statique de Programmes........................................................................................................... 165 11-2 - Dtection des Erreurs d'Allocation Dynamique....................................................................................... 166 11-2-1 - Programme de Test d'Allocation et de Libration Mmoire............................................................ 167 11-2-2 - Vrification par malloc.....................................................................................................................167 11-2-3 - Recherche de Fuites Mmoire avec mtrace...................................................................................168 11-2-4 - Utiliser ccmalloc.............................................................................................................................. 169 11-2-5 - Electric Fence..................................................................................................................................170 11-2-6 - Choisir Parmi les Diffrents Outils de Dbogage Mmoire............................................................ 170 11-2-7 - Code Source du Programme d'Allocation Dynamique de Mmoire................................................171 11-3 - Profilage................................................................................................................................................... 173 11-3-1 - Une Calculatrice Simplifie............................................................................................................. 173 11-3-2 - Collecter des Informations de Profilage..........................................................................................174 11-3-3 - Affichage des Donnes de Profilage.............................................................................................. 174 11-3-4 - Comment gprof Collecte les Donnes............................................................................................ 176 11-3-5 - Code Source de la Calculatrice...................................................................................................... 176 12 - E/S de Bas Niveau.......................................................................................................................................... 181 12-1 - Lire et crire des Donnes..................................................................................................................... 181 12-1-1 - Ouvrir un Fichier............................................................................................................................. 181 12-1-2 - Fermer un fichier.............................................................................................................................183 12-1-3 - crire des donnes.........................................................................................................................183 12-1-4 - Lecture de Donnes....................................................................................................................... 185 12-1-5 - Se Dplacer dans un Fichier.......................................................................................................... 186 12-2 - stat........................................................................................................................................................... 188
-5Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

13 14

15

16

12-3 - criture et Lecture Vectorielles............................................................................................................... 189 12-4 - Lien avec les Functions d'E/S Standards du C...................................................................................... 191 12-5 - Autres Oprations sur les Fichiers..........................................................................................................191 12-6 - Lire le Contenu d'un Rpertoire.............................................................................................................. 192 - Tableau des Signaux....................................................................................................................................... 195 - Ressources en Ligne....................................................................................................................................... 197 14-1 - Informations Gnrales........................................................................................................................... 197 14-2 - Informations sur les Logiciels GNU/Linux............................................................................................... 197 14-3 - Autres Sites............................................................................................................................................. 197 - Open Publication License Version 1.0.............................................................................................................198 15-1 - Conditions Applicables aux Versions Modifies ou Non......................................................................... 198 15-2 - Copyright..................................................................................................................................................198 15-3 - Porte de cette Licence.......................................................................................................................... 198 15-4 - Conditions Applicables aux Travaux Modifis.........................................................................................198 15-5 - Bonnes Pratiques.................................................................................................................................... 199 15-6 - Options Possibles....................................................................................................................................199 15-7 - Annexe la Licence Open Publication................................................................................................... 199 - Licence Publique Gnrale GNU.....................................................................................................................201 16-1 - Prambule................................................................................................................................................201 16-2 - Conditions de copie, distribution et modification de la Licence Publique Gnrale GNU........................202 16-2-1 - ABSENCE DE GARANTIE............................................................................................................. 204 16-3 - Comment appliquer ces conditions vos nouveaux programmes..........................................................205

-6Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

1 - Pour Commencer
Ce chapitre prsente les tapes de base ncessaires la cration d'un programme Linux en C ou C++. En particulier, il explique comment crer et modifier un code source C ou C++, compiler ce code et dboguer le rsultat. Si vous tes dj familier avec la programmation sous Linux, vous pouvez aller directement au Chapitre logicielsQualite, Chapitre logicielsQualite; lisez attentivement la Section ecrireutiliserbiblio, Section ecrireutiliserbiblio, pour plus d'informations sur la comparaison des ditions de liens statique et dynamique que vous ne connaissez peut-tre pas. Dans la suite de ce livre, nous supposerons que vous tes familier avec le langage de programmation C ou C++ et avec les fonctions les plus courantes de la bibliothque C standard. Les exemples de code source dans ce livre sont en C, except lorsqu'ils montrent une fonctionnalit ou une difficult propre au C++. Nous supposerons galement que vous savez comment effectuer les oprations lmentaires avec le shell de commande Linux, comme crer des rpertoires et copier des fichiers. Comme beaucoup de programmeurs Linux ont commenc programmer dans un environnement Windows, nous soulignerons occasionnellement les similitudes et les contrastes entre Windows et Linux.

1-1 - L'diteur Emacs


Un diteur est le programme que vous utilisez pour diter le code source. Beaucoup d'diteurs diffrents sont disponibles sous Linux, mais le plus populaire et celui offrant le plus de fonctionnalits est certainement GNU Emacs. Emacs est beaucoup plus qu'un simple diteur. Il s'agit d'un programme incroyablement puissant, tel point que chez CodeSourcery, il est appel affectueusement le Seul Vrai Programme (One True Program), ou simplement OTP pour faire court. Vous pouvez crire et lire vos e-mails depuis Emacs et vous pouvez le personnaliser et l'tendre de faon trop vaste pour que nous en parlions ici. Vous pouvez mme surfer sur le Web depuis Emacs ! Si vous tes familier avec un autre diteur, vous pouvez certainement l'utiliser la place. Rien dans le reste du livre ne dpend de l'utilisation d'Emacs. Si vous n'avez pas dj un diteur favori sous Linux, vous pouvez continuer avec le mini-didacticiel fourni ici. Si vous aimez Emacs et voulez en savoir plus sur ses fonctionnalits avances, vous pouvez lire un des nombreux livres disponibles sur le sujet. Un excellent didacticiel, Introduction GNU Emacs, a t crit par Debra Cameron, Bill Rosenblatt et Eric Raymond (O'Reilly 1997).

1-1-1 - Ouvrir un Fichier Source C ou C++


Vous pouvez lancer Emacs en saisissant emacs suivi de la touche Entre dans votre terminal. Lorsque Emacs a dmarr, vous pouvez utiliser les menus situs dans la partie suprieure pour crer un nouveau fichier source. Cliquez sur le menu File, slectionnez Open File puis saisissez le nom du fichier que vous voulez ouvrir dans le minibuffer au bas de l'cran(Si vous n'utilisez pas un systme X Window, vous devrez appuyer sur F10 pour accder aux menus.). Si vous voulez crer un fichier source C, utilisez un nom de fichier se terminant par .c ou .h. Si vous dsirez crer un fichier C++, utilisez un nom de fichier se terminant par .cpp, .hpp, .cxx, .hxx, .C ou .H. Lorsque le fichier est ouvert, vous pouvez taper comme vous le feriez dans un programme de traitement de texte. Pour sauvegarder le fichier, slectionnez l'entre Save Buffer dans le menu File. Lorsque vous avez termin d'utiliser Emacs, vous pouvez choisir l'option Exit Emacs dans le menu File. Si vous n'aimez pas cliquer, vous pouvez utiliser les raccourcis clavier pour ouvrir ou fermer un fichier et sortir d'Emacs. Pour ouvrir un fichier, saisissez C-x C-f (C-x signifie de maintenir la touche Control enfonce tout en appuyant sur la touche x). Pour sauvegarder un fichier, saisissez C-x C-s. Pour sortir d'Emacs, saisissez simplement C-x C-c. Si vous voulez devenir un peu plus familier avec Emacs, slectionnez l'entre Emacs Tutorial dans le menu Help. Le didacticiel vous propose quantit d'astuces sur l'utilisation efficace d'Emacs.

-7Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

1-1-2 - Formatage Automatique


Si vous tes un habitu de la programmation dans un Environnement de Dveloppement Intgr (Integrated Development Evironment, IDE), vous tes habitu l'assistance au formatage fourni par l'diteur. Emacs peut vous offrir le mme type de fonctionnalit. Si vous ouvrez un fichier C ou C++, Emacs devine qu'il contient du code, pas simplement du texte ordinaire. Si vous appuyez sur la touche Tab sur une ligne blanche, Emacs dplace le curseur au point d'indentation appropri. Si vous appuyez sur la touche Tab sur une ligne contenant dj du texte, Emacs indente le texte. Donc, par exemple, supposons que vous ayez saisi ce qui suit:
int main() { printf("Hello, world\n"); }

Si vous pressez la touche Tab sur la ligne de l'appel printf, Emacs reformatera votre code comme suit:
int main() { printf("Hello, world\n"); }

Remarquez comment la ligne a t correctement indente. En utilisant Emacs, vous verrez comment il peut vous aider effectuer toutes sortes de tches de formatage compliques. Si vous tes ambitieux, vous pouvez programmer Emacs pour effectuer littralement tout formatage que vous pourriez imaginer. Des gens ont utilis ces fonctionnalits pour implmenter des modifications d'Emacs pour diter peu prs n'importe quelle sorte de documents, implmenter des jeux(Essayez la commande M-x dunnet si vous voulez jouer un jeu d'aventures en mode texte l'ancienne.) et des interfaces vers des bases de donnes.

1-1-3 - Coloration Syntaxique


En plus de formater votre code, Emacs peut faciliter la lecture du code C et C++ en colorant les diffrents lments de sa syntaxe. Par exemple, Emacs peut colorer les mots cls d'une certaine faon, les types intgrs comme int d'une autre et les commentaires d'une autre encore. Utiliser des couleurs facilite la dtection de certaines erreurs de syntaxe courantes. La faon la plus simple d'activer la coloration est d'diter le fichier ~/.emacs et d'y insrer la chane suivante:
(global-font-lock-mode t)

Sauvegardez le fichier, sortez d'Emacs et redmarrez-le. Ouvrez un fichier C ou C++ et admirez ! Vous pouvez avoir remarqu que la chane que vous avez insr dans votre .emacs ressemble du code LISP. C'est parce-qu'il s'agit de code LISP ! La plus grande partie d'Emacs est crite en LISP. Vous pouvez ajouter des fonctionnalits Emacs en crivant en LISP.

1-2 - Compiler avec GCC


Un compilateur transforme un code source lisible par un humain en code objet lisible par la machine qui peut tre excut. Les compilateurs de choix disponibles sur les systmes Linux font tous partie de la GNU Compiler Collection, plus communment appele GCC(Pour plus d'informations sur GCC, visitez http://gcc.gnu.org/.) . GCC inclut galement des compilateurs C, C++, Java, Objective-C, Fortran et Chill. Ce livre se concentre plus particulirement sur la programmation C et C++.
-8Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Supposons que vous ayez un projet comme celui du Listing recisrc avec un fichier source C++ (reciprocal.cpp) et un fichier source C (main.c) comme dans le Listing recimain. Ces deux fichiers sont supposs tre compils puis lis entre eux pour produire un programme appel reciprocal(Sous Windows, les excutables portent habituellement des noms se terminant en .exe. Les programmes Linux par contre, n'ont habituellement pas d'extension. Donc, l'quivalent Windows de ce programme s'appellerait probablement reciprocal.exe; la version Linux est tout simplement reciprocal.). Ce programme calcule l'inverse d'un entier. Fichier source C main.c
#include <stdio.h> #include <stdlib.h> #include "reciprocal.hpp" int main (int argc, char **argv) { int i; i = atoi (argv[1]); printf ("L'inverse de %d est %g\n", i, reciprocal (i)); return 0;

Fichier source C++ reciprocal.cpp


#include <cassert> #include "reciprocal.hpp" double reciprocal (int i) { // i doit tre diffrent de zro assert (i != 0); return 1.0/i; }

Il y a galement un fichier d'entte appel reciprocal.hpp (voir Listing reciheader). Fichier d'entte reciprocal.hpp
#ifdef %%__%%cplusplus extern "C" { #endif extern double reciprocal (int i); #ifdef %%__%%cplusplus } #endif

La premire tape est de traduire le code C et C++ en code objet.

1-2-1 - Compiler un Fichier Source Isol


Le nom du compilateur C est gcc. Pour compiler un fichier source C, utilisez l'option -c. Donc par exemple, cette commande compile le fichier source main.c:
% gcc -c main.c

Le fichier objet rsultant est appel main.o. Le compilateur C++ s'appelle g++. Son mode opratoire est trs similaire gcc; la compilation de reciprocal.cpp s'effectue via la commande suivante:
% g++ -c reciprocal.cpp -9Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

L'option -c indique g++ de ne compiler le fichier que sous forme d'un fichier objet; sans cela, g++ tenterait de lier le programme afin de produire un excutable. Une fois cette commande saisie, vous obtenez un fichier objet appel reciprocal.o. Vous aurez probablement besoin de quelques autres options pour compiler un programme d'une taille raisonnable. L'option -I est utilise pour indiquer GCC o rechercher les fichiers d'entte. Par dfaut, GCC cherche dans le rpertoire courant et dans les rpertoires o les enttes des bibliothques standards sont installs. Si vous avez besoin d'inclure des fichiers d'entte situs un autre endroit, vous aurez besoin de l'option -I. Par exemple, supposons que votre projet ait un rpertoire appel src, pour les fichiers source, et un autre appel include. Vous compileriez reciprocal.cpp comme ceci pour indiquer g++ qu'il doit utiliser en plus le rpertoire include pour trouver reciprocal.hpp:
% g++ -c -I ../include reciprocal.cpp

Parfois, vous pourriez vouloir dfinir des macros au niveau de la ligne de commande. Par exemple, dans du code de production, vous ne voudriez pas du surcot de l'assertion prsente dans reciprocal.cpp; elle n'est l que pour vous aider dboguer votre programme. Vous dsactivez la vrification en dfinissant la macro NDEBUG. Vous pourriez ajouter un #define explicite dans reciprocal.cpp, mais cela ncessiterait de modifier la source elle-mme. Il est plus simple de dfinir NDEBUG sur la ligne de commande, comme ceci:
% g++ -c -D NDEBUG reciprocal.cpp

Si vous aviez voulu donner une valeur particulire NDEBUG, vous auriez pu saisir quelque chose de ce genre:
% g++ -c -D NDEBUG=3 reciprocal.cpp

Si vous tiez rellement en train de compiler du code de production, vous voudriez probablement que GCC optimise le code afin qu'il s'excute aussi rapidement que possible. Vous pouvez le faire en utilisant l'option en ligne de commande -O2 (GCC a plusieurs niveaux d'optimisation; le second niveau convient pour la plupart des programmes). Par exemple, ce qui suit compile reciprocal.cpp avec les optimisations actives:
% g++ -c -O2 reciprocal.cpp

Notez que le fait de compiler avec les optimisations peut rendre votre programme plus difficile dboguer avec un dbogueur (voyez la Section debgdb, Section debgdb). De plus, dans certaines circonstances, compiler avec les optimisations peut rvler des bogues qui n'apparaissaient pas auparavant. Vous pouvez passer un certain nombre d'autres options gcc et g++. Le meilleur moyen d'en obtenir une liste complte est de consulter la documentation en ligne. Vous pouvez le faire en saisissant ceci l'invite de commandes:
% info gcc

1-2-2 - Lier les Fichiers Objet


Maintenant que vous avez compil main.c et reciprocal.cpp, vous devez les lier. Vous devriez toujours utiliser g++ pour lier un programme qui contient du code C++, mme s'il contient galement du code C. Si votre programme ne contient que du code C, vous devriez utiliser gcc la place. Comme ce programme contient la fois du code C et du code C++, vous devriez utiliser g++, comme ceci:
% g++ -o reciprocal main.o reciprocal.o

- 10 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

L'option -o donne le nom du fichier gnrer l'issue de l'tape d'dition de liens. Vous pouvez maintenant lancer reciprocal comme ceci:
% ./reciprocal 7 L'inverse de 7 est 0.142857

Comme vous pouvez le voir, g++ a automatiquement inclus les bibliothques d'excution C standards contenant l'implmentation de printf. Si vous aviez eu besoin de lier une autre bibliothque (comme un kit de dveloppement d'interfaces utilisateur), vous auriez indiqu la bibliothque avec l'option -l. Sous Linux, les noms des bibliothques commencent quasiment toujours par lib. Par exemple, la bibliothque du Module d'Authentification Enfichable (Pluggable Authentication Module, PAM) est appele libpam.a. Pour inclure libpam.a lors de l'dition de liens, vous utiliserez une commande de ce type:
% g++ -o reciprocal main.o reciprocal.o -lpam

Le compilateur ajoutera automatiquement le prfixe lib et le suffixe .a. Comme avec les fichiers d'entte, l'diteur de liens recherche les bibliothques dans certains emplacements standards, ce qui inclut les rpertoires /lib et /usr/lib qui contiennent les bibliothques systme standards. Si vous voulez que l'diteur de liens cherche en plus dans d'autres rpertoires, vous devez utiliser l'option -L, qui est l'quivalent de l'option -I dont nous avons parl plus tt. Vous pouvez utiliser cette commande pour indiquer l'diteur de liens de rechercher les bibliothques dans le rpertoire /usr/local/lib/pam avant de les rechercher dans les emplacements habituels:
% g++ -o reciprocal main.o reciprocal.o -L/usr/local/lib/pam -lpam

Bien que vous n'ayez pas utiliser l'option -I pour que le prprocesseur effectue ses recherches dans le rpertoire courant, vous devez utiliser l'option -L pour que l'diteur de liens le fasse. Par exemple, vous devrez utiliser ce qui suit pour indiquer l'diteur de liens de rechercher la bibliothque test dans le rpertoire courant:
% gcc -o app app.o -L. -ltest

1-3 - Automatiser le Processus avec GNU Make


Si vous tes habitu la programmation pour le systme d'exploitation Windows, vous avez probablement l'habitude de travailler avec un Environnement de Dveloppement Intgr (IDE). Vous ajoutez les fichiers votre projet puis l'IDE compile ce projet automatiquement. Bien que des IDE soient disponibles pour Linux, ce livre n'en traite pas. Au lieu de cela, il vous montre comment vous servir de GNU Make pour recompiler votre code automatiquement, comme le font en fait la majorit des programmeurs Linux. L'ide de base derrire make est simple. Vous indiquez make quelles cibles vous dsirez compiler puis donnez des rgles expliquant comment les compiler. Vous pouvez galement spcifier des dpendances qui indiquent quand une cible particulire doit tre recompile. Dans notre projet exemple reciprocal, il y a trois cibles videntes: reciprocal.o, main.o et reciprocal lui-mme. Vous avez dj l'esprit les rgles ncessaires la compilation de ces cibles sous forme des lignes de commande donnes prcdemment. Les dpendances ncessitent un minimum de rflexion. Il est clair que reciprocal dpend de reciprocal.o et main.o car vous ne pouvez pas passer l'tape d'dition de liens avant d'avoir compil chacun des fichiers objets. Les fichiers objets doivent tre recompils chaque fois que le fichier source correspondant est modifi. Il y a encore une subtilit: une modification de reciprocal.hpp doit entraner la recompilation des deux fichiers objets car les deux fichiers source incluent ce fichier d'entte.

- 11 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

En plus des cibles videntes, il devrait toujours y avoir une cible clean. Cette cible supprime tous les fichiers objets gnrs afin de pouvoir recommencer sur des bases saines. La rgle pour cette cible utilise la commande rm pour supprimer les fichiers. Vous pouvez fournir toutes ces informations make en les plaant dans un fichier nomm Makefile. Voici ce qu'il contient:
reciprocal: main.o reciprocal.o g++ $(CFLAGS) -o reciprocal main.o reciprocal.o main.o: main.c reciprocal.hpp gcc $(CFLAGS) -c main.c reciprocal.o: reciprocal.cpp reciprocal.hpp g++ $(CFLAGS) -c reciprocal.cpp clean: rm -f *.o reciprocal

Vous pouvez voir que les cibles sont listes sur la gauche, suivies de deux-points puis des dpendances. La rgle pour la construction d'une cible est place sur la ligne suivante (ignorez le $(CFLAGS) pour l'instant). La ligne dcrivant la rgle doit commencer par un caractre de tabulation ou make ne s'y retrouvera pas. Si vous ditez votre Makefile dans Emacs, Emacs vous assistera dans le formatage. Si vous supprimez les fichiers objets que vous avez dj cr et que vous tapez simplement:
% make

sur la ligne de commande, vous obtiendrez la sortie suivante:


% make gcc -c main.c g++ -c reciprocal.cpp g++ -o reciprocal main.o reciprocal.o

Vous constatez que make a automatiquement compil les fichiers objet puis les a lis. Si vous modifiez maintenant main.c d'une faon quelconque puis saisissez make de nouveau, vous obtiendrez la sortie suivante:
% make gcc -c main.c g++ -o reciprocal main.o reciprocal.o

Vous constatez que make recompile main.o et rdite les liens, il ne recompile pas reciprocal.cpp car aucune des dpendances de reciprocal.o n'a chang. $(CFLAGS) est une variable de make. Vous pouvez dfinir cette variable soit dans le Makefile lui-mme soit sur la ligne de commande. GNU make substituera la variable par sa valeur lorsqu'il excutera la rgle. Donc, par exemple, pour recompiler avec les optimisations actives, vous procderiez de la faon suivante:
% make clean rm -f *.o reciprocal % make CFLAGS=-O2 gcc -O2 -c main.c g++ -O2 -c reciprocal.cpp g++ -O2 -o reciprocal main.o reciprocal.o

- 12 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Notez que le drapeau -O2 a t insr la place de $(CFLAGS) dans les rgles. Dans cette section, nous n'avons prsent que les capacits les plus basiques de make. Vous pourrez en apprendre plus grce la commande suivante:
% info make

Dans ce manuel, vous trouverez des informations sur la faon de rendre un Makefile plus simple maintenir, comment rduire le nombre de rgles crire et comment calculer automatiquement les dpendances. Vous pouvez galement trouver plus d'informations dans GNU, Autoconf, Automake et Libtool de Gary V. Vaughan, Ben Ellitson, Tom Tromey et Ian Lance Taylor (New Riders Publishing, 2000).

1-4 - Dboguer avec le Dbogueur GNU (GDB)


Le dbogueur est le programme que vous utilisez pour trouver pourquoi votre programme ne se comporte pas comme vous pensez qu'il le devrait. Vous y aurez souvent recours(... moins que votre programme ne fonctionne toujours du premier coup.). Le dbogueur GNU (GNU debugger, GDB) est le dbogueur utilis par la plupart des programmeurs Linux. Vous pouvez utiliser GDB pour excuter votre code pas pas, poser des points d'arrt et examiner les valeurs des variables locales.

1-4-1 - Compiler avec les Informations de Dbogage


Pour utiliser GDB, vous devez compiler en activant les informations de dbogage. Pour cela, ajoutez l'option -g sur la ligne de commande de compilation. Si vous utilisez un Makefile comme nous l'avons expliqu plus haut, vous pouvez vous contenter de positionner CFLAGS -g lors de l'excution de make, comme ceci:
% make gcc -g g++ -g g++ -g CFLAGS=-g -c main.c -c reciprocal.cpp -o reciprocal main.o reciprocal.o

Lorsque vous compilez avec -g, le compilateur inclut des informations supplmentaires dans les fichiers objets et les excutables. Le dbogueur utilise ces informations pour savoir quelle adresse correspond quelle ligne et dans quel fichier source, afficher les valeurs des variables et ctera.

1-4-2 - Lancer GDB


Vous pouvez dmarrer gdb en saisissant:
% gdb reciprocal

Lorsque GDB dmarre, il affiche l'invite:


(gdb)

La premire tape est de lancer votre programme au sein du dbogueur. Entrez simplement la commande run et les arguments du programme. Essayez de lancer le programme sans aucun argument, comme ceci:
(gdb) run Starting program: reciprocal - 13 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Program received signal SIGSEGV, Segmentation fault. %%__%%strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0) at strtol.c:287 287 strtol.c: No such file or directory. (gdb)

Le problme est qu'il n'y a aucun code de contrle d'erreur dans main. Le programme attend un argument, mais dans ce cas, il n'en a reu aucun. Le message SIGSEGV indique un plantage du programme. GDB sait que le plantage a eu lieu dans une fonction appele %%__%%strtol_internal. Cette fonction fait partie de la bibliothque standard, et les sources ne sont pas installes ce qui explique le message No such file or directory (Fichier ou rpertoire inexistant). Vous pouvez observer la pile en utilisant la commande where:
(gdb) where #0 %%__%%strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0) at strtol.c:287 #1 0x40096fb6 in atoi (nptr=0x0) at ../stdlib/stdlib.h:251 #2 0x804863e in main (argc=1, argv=0xbffff5e4) at main.c:8

Vous pouvez voir d'aprs cet extrait que main a appel la fonction atoi avec un pointeur NULL ce qui est la source de l'erreur. Vous pouvez remonter de deux niveaux dans la pile jusqu' atteindre main en utilisant la commande up:
(gdb) up 2 #2 0x804863e in main (argc=1, argv=0xbffff5e4) at main.c:8 8 i = atoi (argv[1]);

Notez que GDB est capable de trouver le fichier source main.c et qu'il affiche la ligne contenant l'appel de fonction erron. Vous pouvez inspecter la valeurs des variables en utilisant la commande print:
(gdb) print argv[1] $2 = 0x0

Cela confirme que le problme vient d'un pointeur NULL pass atoi. Vous pouvez placer un point d'arrt en utilisant la commande break:
(gdb) break main Breakpoint 1 at 0x804862e: file main.c, line 8.

Cette commande place un point d'arrt sur la premire ligne de main(Certaines personnes ont fait la remarque que break main (NdT. littralement casser main) est amusant car vous ne vous en servez en fait uniquement lorsque main a dj un problme.). Essayez maintenant de relancer le programme avec un argument, comme ceci:
(gdb) run 7 Starting program: reciprocal 7 Breakpoint 1, main (argc=2, argv=0xbffff5e4) at main.c:8 8 i = atoi (argv[1])

Vous remarquez que le dbogueur s'est arrt au niveau du point d'arrt. Vous pouvez passer l'instruction se trouvant aprs l'appel atoi en utilisant la commande next:

- 14 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) gdb) next

printf ("L'inverse de %d est %g\n", i, reciprocal (i));

Si vous voulez voir ce qui se passe l'intrieur de la fonction reciprocal, utilisez la commande step, comme ceci:
(gdb) step reciprocal (i=7) at reciprocal.cpp:6 6 assert (i != 0);

Vous tes maintenant au sein de la fonction reciprocal. Vous pouvez trouver plus commode d'excuter gdb au sein d'Emacs plutt que de le lancer directement depuis la ligne de commande. Utilisez la commande M-x gdb pour dmarrer gdb dans une fentre Emacs. Si vous stoppez au niveau d'un point d'arrt, Emacs ouvre automatiquement le fichier source appropri. Il est plus facile de se rendre compte de ce qui se passe en visualisant le fichier dans son ensemble plutt qu'une seule ligne.

1-5 - Obtenir Plus d'Informations


Quasiment toutes les distributions Linux disposent d'une masse importante de documentation. Vous pourriez apprendre la plupart des choses dont nous allons parler dans ce livre simplement en lisant la documentation de votre distribution Linux (bien que cela vous prendrait probablement plus de temps). La documentation n'est cependant pas toujours trs bien organise, donc la partie la plus subtile est de trouver ce dont vous avez besoin. La documentation date quelquefois un peu, aussi, prenez tout ce que vous y trouvez avec un certain recul. Si le systme ne se comporte pas comme le dit une page de manuel, c'est peut-tre parce que celle-ci est obsolte. Pour vous aider naviguer, voici les sources d'information les plus utiles sur la programmation avance sous Linux.

1-5-1 - Pages de Manuel


Les distributions Linux incluent des pages de manuel pour les commandes les plus courantes, les appels systme et les fonctions de la bibliothque standard. Les pages de manuel sont divises en sections numrotes; pour les programmeurs, les plus importantes sont celles-ci: (1) Commandes utilisateur (2) Appels systme (3) Fonctions de la bibliothque standard (8) Commandes systme/d'administration

Les numros indiquent les sections des pages de manuel. Les pages de manuel de Linux sont installes sur votre systme; utilisez la commande man pour y accder. Pour accder une page de manuel, invoquez simplement man nom, o nom est un nom de commande ou de fonction. Dans un petit nombre de cas, le mme nom apparat dans plusieurs sections; vous pouvez indiquer explicitement la section en plaant son numro devant le nom. Par exemple, si vous saisissez la commande suivante, vous obtiendrez la page de manuel pour la commande sleep (dans la section~1 des pages de manuel Linux):
% man sleep

Pour visualiser la page de manuel de la fonction sleep de la bibliothque standard, utilisez cette commande:
% man 3 sleep

Chaque page de manuel comprend un rsum sur une ligne de la commande ou fonction. La commande whatis nom liste toutes les pages de manuel (de toutes les sections) pour une commande ou une fonction correspondant nom.
- 15 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si vous n'tes pas sr de la commande ou fonction utiliser, vous pouvez effectuer une recherche par mot-cl sur les rsums via man -k mot-cl. Les pages de manuel contiennent des informations trs utiles et devraient tre le premier endroit vers lequel vous orientez vos recherches. La page de manuel d'une commande dcrit ses options en ligne de commande et leurs arguments, ses entres et sorties, ses codes d'erreur, sa configuration et ce qu'elle fait. La page de manuel d'un appel systme ou d'une fonction de bibliothque dcrit les paramtres et valeurs de retour, liste les codes d'erreur et les effets de bord et spcifie le fichier d'entte inclure si vous utilisez la fonction.

1-5-2 - Info
Le systme de documentation Info contient des informations plus dtailles pour beaucoup de composants fondamentaux du systme GNU/Linux et quelques autres programmes. Les pages Info sont des documents hypertextes, similaires aux pages Web. Pour lancer le navigateur texte Info, tapez simplement info l'invite de commande. Vous obtiendrez un menu avec les documents Info prsents sur votre systme (appuyez sur Ctrl+H pour afficher les touches permettant de naviguer au sein d'un document Info). Parmi les documents Info les plus utiles, on trouve: gcc Le compilateur gcc libc La bibliothque C GNU, avec beaucoup d'appels systme gdb Le dbogueur GNU emacs L'diteur de texte Emacs info Le systme Info lui-mme

Presque tous les outils de programmation standards sous Linux (y compris ld, l'diteur de liens; as, l'assembleur et gprof, le profiler) sont accompagns de pages Info trs utiles. Vous pouvez accder directement un document Info en particulier en indiquant le nom de la page sur la ligne de commandes:
% info libc

Si vous programmez la plupart du temps sous Emacs, vous pouvez accder au navigateur Info intgr en appuyant sur M-x info ou C-h i.

1-5-3 - Fichiers d'entte


Vous pouvez en apprendre beaucoup sur les fonctions systme disponibles et comment les utiliser en observant les fichiers d'entte. Ils sont placs dans /usr/include et /usr/include/sys. Si vous obtenez des erreurs de compilation lors de l'utilisation d'un appel systme, par exemple, regardez le fichier d'entte correspondant pour vrifier que la signature de la fonction est la mme que celle prsente sur la page de manuel. Sur les systmes Linux, beaucoup de dtails obscurs sur le fonctionnement des appels systmes apparaissent dans les fichiers d'entte placs dans les rpertoires /usr/include/bits, /usr/include/asm et /usr/include/linux. Par exemple, le fichier /usr/include/bits/signum.h dfinit les valeurs numriques des signaux (dcrits dans la Section signaux, Section signaux du Chapitre processus, Chapitre processus). Ces fichiers d'entte constituent une bonne lecture pour les esprits curieux. Ne les incluez pas directement dans vos programmes, cependant; utilisez toujours les fichiers d'entte de /usr/include ou ceux mentionns dans la page de manuel de la fonction que vous utilisez.

1-5-4 - Code Source


Nous sommes dans l'Open Source, non? Le juge en dernier ressort de la faon dont doit fonctionner le systme est le code source, et par chance pour les programmeurs Linux, ce code source est disponible librement. Il y a des chances pour que votre systme Linux comprenne tout le code source du systme et des programmes fournis; si
- 16 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

ce n'est pas le cas, vous avez le droit, selon les termes de la Licence Publique Gnrale GNU, de les demander au distributeur (le code source n'est toutefois pas forcment install. Consultez la documentation de votre distribution pour savoir comment l'installer). Le code source du noyau Linux lui-mme est habituellement stock sous /usr/src/linux. Si ce livre vous laisse sur votre faim concernant les dtails sur le fonctionnement des processus, de la mmoire partage et des priphriques systme, vous pouvez toujours apprendre directement partir du code. La plupart des fonctions dcrites dans ce livre sont implmentes dans la bibliothque C GNU ; consultez la documentation de votre distribution pour connatre l'emplacement du code source de la bibliothque C.

- 17 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

2 - crire des Logiciels GNU/Linux de Qualit


Ce chapitre prsente quelques techniques de base utilises par la plupart des programmeurs GNU/Linux. En suivant grossirement les indications que nous allons prsenter, vous serez mme d'crire des programmes qui fonctionnent correctement au sein de l'environnement GNU/Linux et correspondent ce qu'attendent les utilisateurs au niveau de leur faon de fonctionner.

2-1 - Interaction Avec l'Environnement d'Excution


Lorsque vous avez tudi pour la premire fois le langage C ou C++, vous avez appris que la fonction spciale main est le point d'entre principal pour un programme. Lorsque le systme d'exploitation excute votre programme, il offre un certain nombre de fonctionnalits qui aident le programme communiquer avec le systme d'exploitation et l'utilisateur. Vous avez probablement entendu parler des deux paramtres de main, habituellement appels argc et argv, qui reoivent les entres de votre programme. Vous avez appris que stdin et stdout (ou les flux cin et cout en C++) fournissent une entre et une sortie via la console. Ces fonctionnalits sont fournies par les langages C et C+ +, et elles interagissent avec le systme d'une certaine faon. GNU/Linux fournit en plus d'autres moyens d'interagir avec l'environnement d'excution.

2-1-1 - La Liste d'Arguments


Vous lancez un programme depuis l'invite de commandes en saisissant le nom du programme. ventuellement, vous pouvez passer plus d'informations au programme en ajoutant un ou plusieurs mots aprs le nom du programme, spars par des espaces. Ce sont des arguments de ligne de commande (vous pouvez passer un argument contenant des espaces en le plaant entre guillemets). Plus gnralement, on appelle cela la liste d'arguments du programme car ils ne viennent pas ncessairement de la ligne de commande. Dans le Chapitre 3, Processus , vous verrez un autre moyen d'invoquer un programme, avec lequel un programme peut indiquer directement la liste d'arguments d'un autre programme. Lorsqu'un programme est invoqu depuis la ligne de commande, la liste d'arguments contient toute la ligne de commande, y compris le nom du programme et tout argument qui aurait pu lui tre pass. Supposons, par exemple, que vous invoquiez la commande ls depuis une invite de commandes pour afficher le contenu du rpertoire racine et les tailles de fichiers correspondantes au moyen de cette commande:
% ls -s /

La liste d'arguments que le programme ls reoit est compose de trois lments. Le premier est le nom du programme lui-mme, saisi sur la ligne de commande, savoir ls. Les second et troisime lment sont les deux arguments de ligne de commande, -s et /. La fonction main de votre programme peut accder la liste d'arguments via ses paramtres argc et argv (si vous ne les utilisez pas, vous pouvez simplement les omettre). Le premier paramtre, argc, est un entier qui indique le nombre d'lments dans la liste. Le second paramtre, argv, est un tableau de pointeurs sur des caractres. La taille du tableau est argc, et les lments du tableau pointent vers les lments de la liste d'arguments, qui sont des chanes termines par zro. Utiliser des arguments de ligne de commande consiste examiner le contenu de argc et argv. Si vous n'tes pas intress par le nom du programme, n'oubliez pas d'ignorer le premier lment. Le Listing argcv montre l'utilisation de argv et argc. Utiliser argc et argv arglist.c
#include <stdio.h> int main (int argc, char* argv[]) { - 18 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utiliser argc et argv arglist.c

printf ("Le nom de ce programme est '%s'.\n", argv[0]); printf ("Ce programme a t invoqu avec %d arguments.\n", argc - 1); /* A-t-on spcifi des arguments sur la ligne de commande ? */ if (argc > 1) { /* Oui, les afficher. */ int i; printf ("Les arguments sont :\n"); for (i = 1; i < argc; ++i) printf (" %s\n", argv[i]); } return 0;

2-1-2 - Conventions de la Ligne de Commande GNU/Linux


Presque tous les programmes GNU/Linux obissent un ensemble de conventions concernant l'interprtation des arguments de la ligne de commande. Les arguments attendus par un programme sont classs en deux catgories : les options (ou drapeaux(NdT. flags en anglais)) et les autres arguments. Les options modifient le comportement du programme, alors que les autres arguments fournissent des entres (par exemple, les noms des fichiers d'entre). Les options peuvent prendre deux formes: Les options courtes sont formes d'un seul tiret et d'un caractre isol (habituellement une lettre en majuscule ou en minuscule). Elles sont plus rapides saisir. Les options longues sont formes de deux tirets suivis d'un nom compos de lettres majuscules, minuscules et de tirets. Les options longues sont plus faciles retenir et lire (dans les scripts shell par exemple).

Gnralement, un programme propose une version courte et une version longue pour la plupart des options qu'il prend en charge, la premire pour la brivet et la seconde pour la lisibilit. Par exemple, la plupart des programmes acceptent les options -h et --help et les traitent de faon identique. Normalement, lorsqu'un programme est invoqu depuis la ligne de commande, les options suivent immdiatement le nom du programme. Certaines options attendent un argument immdiatement leur suite. Beaucoup de programmes, par exemple, acceptent l'option --output foo pour indiquer que les sorties du programme doivent tre rediriges vers un fichier appel foo. Aprs les options, il peut y avoir d'autres arguments de ligne de commande, typiquement les fichiers ou les donnes d'entre. Par exemple, la commande ls -s / affiche le contenu du rpertoire racine. L'option -s modifie le comportement par dfaut de ls en lui demandant d'afficher la taille (en kilooctets) de chaque entre. L'argument / indique ls quel rpertoire lister. L'option --size est synonyme de -s, donc la commande aurait pu tre invoque sous la forme ls -size /. Les Standards de Codage GNU dressent la liste des noms d'options en ligne de commande couramment utiliss. Si vous avez l'intention de proposer des options identiques, il est conseill d'utiliser les noms prconiss dans les standards de codage. Votre programme se comportera de faon similaire aux autres et sera donc plus simple prendre en main pour les utilisateurs. Vous pouvez consulter les grandes lignes des Standards de Codage GNU propos des options en ligne de commandes via la commande suivante depuis une invite de commande sur la plupart des systmes GNU/Linux:
% info "(standards)User Interfaces"

2-1-3 - Utiliser getopt_long


L'analyse des options de la ligne de commande est une corve. Heureusement, la bibliothque GNU C fournit une fonction que vous pouvez utiliser dans les programmes C et C++ pour vous faciliter la tche (quoiqu'elle reste toujours quelque peut ennuyeuse). Cette fonction, getopt_long, interprte la fois les options courtes et longues. Si vous utilisez cette fonction, incluez le fichier d'en-tte &lt;getopt.h&gt;.

- 19 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Supposons par exemple que vous criviez un programme acceptant les trois options du Tableau exopt. Exemple d'Options pour un Programme -h Affiche l'aide-mmoire et quitte Forme courte -h -o nom fichier -v Forme longue --help --output nom fichier --verbose Fonction Affiche l'aide mmoire et quitte Indique le nom du fichier de sortie Affiche des messages dtaills

Le programme doit par ailleurs accepter zro ou plusieurs arguments supplmentaires, qui sont les noms de fichiers d'entre. Pour utiliser getopt_long, vous devez fournir deux structures de donnes. La premire est une chane contenant les options courtes valables, chacune sur une lettre. Une option qui requiert un argument est suivie par deux-points. Pour notre programme, la chane ho:v indique que les options valides sont -h, -o et -v, la seconde devant tre suivie d'un argument. Pour indiquer les options longues disponibles, vous devez construire un tableau d'lments struct option. Chaque lment correspond une option longue et dispose de quatre champs. Gnralement, le premier champ est le nom de l'option longue (sous forme d'une chane de caractres, sans les deux tirets); le second est 1 si l'option prend un argument, 0 sinon; le troisime est NULL et le quatrime est un caractre qui indique l'option courte synonyme de l'option longue. Tous les champs du dernier lment doivent tre zro. Vous pouvez construire le tableau comme ceci:
const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "output", 1, NULL, 'o' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } };

Vous invoquez la fonction getopt_long en lui passant les arguments argc et argv de main, la chane de caractres dcrivant les options courtes et le tableau d'lments struct option dcrivant les options longues. chaque fois que vous appelez getopt_long, il n'analyse qu'une seule option et renvoie la lettre de l'option courte pour cette option ou -1 s'il n'y a plus d'option analyser. Typiquement, vous appelez getopt_long dans une boucle, pour traiter toutes les options que l'utilisateur a spcifi et en les grant au sein d'une structure switch. Si getopt_long rencontre une option invalide (une option que vous n'avez pas indique comme tant une option courte ou longue valide), il affiche un message d'erreur et renvoie le caractre ? (un point d'interrogation). La plupart des programmes s'interrompent dans ce cas, ventuellement aprs avoir affich des informations sur l'utilisation de la commande. Lorsque le traitement d'une option requiert un argument, la variable globale optarg pointe vers le texte constituant cet argument. Une fois que getopt_long a fini d'analyser toutes les options, la variable globale optind contient l'index (dans argv) du premier argument qui n'est pas une option.

Le Listing getoptlong montre un exemple d'utilisation de getopt_long pour le traitement des arguments.

- 20 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utilisation de getopt_long getopt_long.c


#include <getopt.h> #include <stdio.h> #include <stdlib.h> /* Nom du programme. */ const char* program_name; /* Envoie les informations sur l'utilisation de la commande vers STREAM (typiquement stdout ou stderr) et quitte le programme avec EXIT_CODE. Ne retourne jamais. */ void print_usage (FILE* stream, int exit_code) { fprintf (stream, "Utilisation : %s options [fichierentre ...]\n", program_name); fprintf (stream, " -h --help Affiche ce message.\n" " -o --output filename Redirige la sortie vers un fichier.\n" " -v --verbose Affiche des messages dtaills.\n"); exit (exit_code); } /* Point d'entre du programme. ARGC contient le nombre d'lments de la liste d'arguments ; ARGV est un tableau de pointeurs vers ceux-ci. */ int main (int argc, char* argv[]) { int next_option; /* Chane listant les lettres valides pour les options courtes. */ const char* const short_options = "ho:v"; /* Tableau dcrivant les options longues valides. */ const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "output", 1, NULL, 'o' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } /* Requis la fin du tableau. */ }; /* Nom du fichier vers lequel rediriger les sorties, ou NULL pour la sortie standard. */ const char* output_filename = NULL; /* Indique si l'on doit afficher les messages dtaills. */ int verbose = 0; /* Mmorise le nom du programme, afin de l'intgrer aux messages. Le nom est contenu dans argv[0]. */ program_name = argv[0]; do { next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case 'h': /* -h or --help */ /* L'utilisateur a demand l'aide-mmoire. L'affiche sur la sortie standard et quitte avec le code de sortie 0 (fin normale). */ print_usage (stdout, 0); case 'o': /* -o ou --output */ /* Cette option prend un argument, le nom du fichier de sortie. */ output_filename = optarg; break; case 'v': /* -v ou --verbose */ verbose = 1; break; case '?': /* L'utilisateur a saisi une option invalide. */ /* Affiche l'aide-mmoire sur le flux d'erreur et sort avec le code de sortie un (indiquant une fin anormale). */ print_usage (stderr, 1); case -1: /* Fin des options. */ break; default: /* Quelque chose d'autre : inattendu. */ abort (); } } while (next_option != -1); /* Fin des options. OPTIND pointe vers le premier argument qui n'est pas une option. des fins de dmonstration, nous les affichons si l'option

- 21 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utilisation de getopt_long getopt_long.c

verbose est spcifie. */ if (verbose) { int i; for (i = optind; i < argc; ++i) printf ("Argument : %s\n", argv[i]); } /* Le programme principal se place ici. */ return 0;

L'utilisation de getopt_long peut sembler ncessiter beaucoup de travail, mais crire le code ncessaire l'analyse des options de la ligne de commandes vous-mme vous prendrait encore plus longtemps. La fonction getopt_long est trs sophistique et permet une grande flexibilit dans la spcification des types d'options acceptes. Cependant, il est bon de se tenir l'cart des fonctionnalits les plus avances et de conserver la structure d'options basiques dcrite ici.

2-1-4 - E/S Standards


La bibliothque standard du C fournit des flux d'entre et de sortie standards (stdin et stdout respectivement). Il sont utiliss par printf, scanf et d'autres fonctions de la bibliothque. Dans la tradition UNIX, l'utilisation de l'entre et de la sortie standard est frquente pour les programmes GNU/Linux. Cela permet l'enchanement de plusieurs programmes au moyen des pipes(NdT. appels aussi parfois tubes ou canaux.) et de la redirection des entres et sorties (consultez la page de manuel de votre shell pour savoir comment les utiliser). La bibliothque C fournit galement stderr, le flux d'erreurs standard. Il est d'usage que les programmes envoient les messages d'erreur et d'avertissement vers la sortie des erreurs standard plutt que vers la sortie standard. Cela permet aux utilisateurs de sparer les messages normaux des messages d'erreur, par exemple, en redirigeant la sortie standard vers un fichier tout en laissant les erreurs s'afficher sur la console. La fonction fprintf peut tre utilise pour crire sur stderr, par exemple:
% fprintf (stderr, "Erreur : ...");

Ces trois flux sont galement accessibles via les commandes d'E/S UNIX de bas niveau (read, write, etc), par le biais des descripteurs de fichiers. Les descripteurs de fichiers sont 0 pour stdin, 1 pour stdout et 2 pour stderr. Lors de l'appel d'un programme, il peut tre utile de rediriger la fois la sortie standard et la sortie des erreurs vers un fichier ou un pipe. La syntaxe utiliser diffre selon les shells; la voici pour les shells de type Bourne (y compris bash, le shell par dfaut sur la plupart des distributions GNU/Linux):
% programme > fichier_sortie.txt 2>&1 % programme 2>&1 | filtre

La syntaxe 2>&1 indique que le descripteur de fichiers 2 (stderr) doit tre fusionn avec le descripteur de fichiers 1 (stdout). Notez que 2>&1 doit tre plac aprs une redirection vers un fichier (premier exemple) mais avant une redirection vers un pipe (second exemple).

- 22 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Notez que stdout est bufferise. Les donnes crites sur stdout ne sont pas envoyes vers la console (ou un autre dispositif si l'on utilise la redirection) avant que le tampon ne soit plein, que le programme ne se termine normalement ou que stdout soit ferm. Vous pouvez purger explicitement le tampon de la faon suivante:
fflush (stdout);

Par contre, stderr n'est pas bufferise; les donnes crites sur stderr sont envoyes directement vers la console(En C++, la mme distinction s'applique cout et cerr, respectivement. Notez que le token endl purge un flux en plus d'y envoyer un caractre de nouvelle ligne; si vous ne voulez pas purger le flux (pour des raisons de performances par exemple), utilisez une constante de nouvelle ligne, 'n', la place.). Cela peut conduire des rsultats quelque peu surprenants. Par exemple, cette boucle n'affiche pas un point toutes les secondes; au lieu de cela, les points sont placs dans le tampon, et ils sont affichs par groupe lorsque le tampon est plein.
while (1) { printf ("."); sleep (1); }

Avec cette boucle, par contre, les points sont affichs au rythme d'un par seconde:
while (1) { fprintf (stderr, "."); sleep (1); }

2-1-5 - Codes de Sortie de Programme


Lorsqu'un programme se termine, il indique son tat au moyen d'un code de sortie. Le code de sortie est un entier court ; par convention, un code de sortie zro indique une fin normale, tandis qu'un code diffrent de zro signale qu'une erreur est survenue. Certains programmes utilisent des codes de sortie diffrents de zro varis pour distinguer les diffrentes erreurs. Avec la plupart des shells, il est possible d'obtenir le code de sortie du dernier programme excut en utilisant la variable spciale $?. Voici un exemple dans lequel la commande ls est invoque deux fois et son code de sortie est affich aprs chaque invocation. Dans le premier cas, ls se termine correctement et renvoie le code de sortie 0. Dans le second cas, ls rencontre une erreur (car le fichier spcifi sur la ligne de commande n'existe pas) et renvoie donc un code de sortie diffrent de 0.
% ls / bin coda etc lib misc nfs proc boot dev home lost+found mnt opt root % echo $? 0 % ls fichierinexistant ls: fichierinexistant: Aucun fichier ou rpertoire de ce type % echo $? 1

Un programme C ou C++ donne son code de sortie en le retournant depuis la fonction main. Il y a d'autres mthodes pour fournir un code de sortie et des codes de sortie spciaux sont assigns aux programmes qui se terminent de faon anormale (sur un signal). Ils sont traits de manire plus approfondie dans le Chapitre 3.

- 23 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

2-1-6 - L'Environnement
GNU/Linux fournit tout programme s'excutant un environnement. L'environnement est une collection de paires variable/valeur. Les variables d'environnement et leurs valeurs sont des chanes de caractres. Par convention, les variables d'environnement sont en majuscules d'imprimerie. Vous tes probablement dj familier avec quelques variables d'environnement courantes. Par exemple : USER contient votre nom d'utilisateur. HOME contient le chemin de votre rpertoire personnel. PATH contient une liste de rpertoires spars par deux-points dans lesquels Linux recherche les commandes que vous invoquez. DISPLAY contient le nom et le numro d'affichage du serveur X Window sur lequel apparaissent les fentres des programmes graphiques X Window.

Votre shell, comme n'importe quel autre programme, dispose d'un environnement. Les shells fournissent des mthodes pour examiner et modifier l'environnement directement. Pour afficher l'environnement courant de votre shell, invoquez le programme printenv. Tous les shells n'utilisent pas la mme syntaxe pour la manipulation des variables d'environnement; voici la syntaxe pour les shells de type Bourne: Le shell cre automatiquement une variable shell pour chaque variable d'environnement qu'il trouve, afin que vous puissiez accder aux valeurs des variables d'environnement en utilisant la syntaxe $nomvar. Par exemple:
% echo $USER samuel % echo $HOME /home/samuel

Vous pouvez utiliser la commande export pour exporter une variable shell vers l'environnement. Par exemple, pour positionner la variable d'environnement EDITOR, vous utiliserez ceci:
% EDITOR=emacs % export EDITOR

Ou, pour faire plus court:


% export EDITOR=emacs

Dans un programme, vous pouvez accder une variable d'environnement au moyen de la fonction getenv de <stdlib.h>. Cette fonction accepte le nom d'une variable et renvoie la valeur correspondante sous forme d'une chane de caractres ou NULL si cette variable n'est pas dfinie dans l'environnement. Pour positionner ou supprimer une variable d'environnement, utilisez les fonctions setenv et unsetenv, respectivement. numrer toutes les variables de l'environnement est un petit peu plus subtil. Pour cela, vous devez accder une variable globale spciale appele environ, qui est dfinie dans la bibliothque C GNU. Cette variable, de type char %%**%%, est un tableau termin par NULL de pointeurs vers des chanes de caractres. Chaque chane contient une variable d'environnement, sous la forme VARIABLE=valeur. Le programme du Listing printenv, par exemple, affiche tout l'environnement en bouclant sur le tableau environ. Afficher l'Environnement d'Excution print-env.c
#include <stdio.h> - 24 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Afficher l'Environnement d'Excution print-env.c

/* La variable ENVIRON contient l'environnement. */ extern char** environ; int main () { char** var; for (var = environ; *var != NULL; ++var) printf ("%s\n", *var); return 0; }

Ne modifiez pas environ vous-mme; utilisez plutt les fonctions setenv et getenv. Lorsqu'un nouveau programme est lanc, il hrite d'une copie de l'environnement du programme qui l'a invoqu (le shell, s'il a t invoqu de faon interactive). Donc, par exemple, les programmes que vous lancez depuis le shell peuvent examiner les valeurs des variables d'environnement que vous positionnez dans le shell. Les variables d'environnement sont couramment utilises pour passer des paramtres de configuration aux programmes. Supposons, par exemple, que vous criviez un programme qui se connecte un serveur Internet pour obtenir des informations. Vous pourriez crire le programme de faon ce que le nom du serveur soit saisi sur la ligne de commande. Cependant, supposons que le nom du serveur ne soit pas quelque chose que les utilisateurs changent trs souvent. Vous pouvez utiliser une variable d'environnement spciale -- disons SERVER_NAME -- pour spcifier le nom du serveur ; si cette variable n'existe pas, une valeur par dfaut est utilise. Une partie de votre programme pourrait ressembler au Listing client. Extrait d'un Programme Client Rseau client.c
#include <stdio.h> #include <stdlib.h> int main () { char* server_name = getenv ("SERVER_NAME"); if (server_name == NULL) /* La variable SERVER_NAME n'est pas dfinie. Utilisation de la valeur par dfaut. */ server_name = "server.my-company.com"; printf ("Accs au serveur %s\n", server_name); /* Accder au serveur ici... */ return 0; }

Supposons que ce programme s'appelle client. En admettant que vous n'ayez pas dfini la variable SERVER_NAME, la valeur par dfaut pour le nom du serveur est utilise:
% client Accs au serveur server.my-company.com

Mais il est facile de spcifier un serveur diffrent:


% export SERVER_NAME=backup-server.elsewhere.net % client Accs au serveur backup-server.elsewhere.net

2-1-7 - Utilisation de Fichiers Temporaires


Parfois, un programme a besoin de crer un fichier temporaire, pour stocker un gros volume de donnes temporairement ou passer des informations un autre programme. Sur les systmes GNU/Linux, les fichiers temporaires sont stocks dans le rpertoire /tmp. Lors de l'utilisation de fichiers temporaires, vous devez viter les piges suivants:
- 25 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Plus d'une copie de votre programme peuvent tre lances simultanment (par le mme utilisateur ou par des utilisateurs diffrents). Les copies devraient utiliser des noms de fichiers temporaires diffrents afin d'viter les collisions. Les permissions du fichier devraient tre dfinies de faon viter qu'un utilisateur non autoris puisse altrer la manire dont s'excute le programme en modifiant ou remplaant le fichier temporaire. Les noms des fichiers temporaires devraient tre gnrs de faon imprvisible de l'extrieur; autrement, un attaquant pourrait exploiter le dlai entre le test d'existence du nom de fichier et l'ouverture du nouveau fichier temporaire.

GNU/Linux fournit des fonctions, mkstemp et tmpfile, qui s'occupent de ces problmes votre place (en plus de fonctions qui ne le font pas). Le choix de la fonction dpend de l'utilisation que vous aurez du fichier, savoir le passer un autre programme ou utiliser les fonctions d'E/S UNIX (open, write, etc.) ou les fonctions de flux d'E/S de la bibliothque C (fopen, fprintf, etc.).

2-1-7-1 - Utilisation de mkstemp


La fonction mkstemp cre un nom de fichier temporaire partir d'un modle de nom de fichier, cre le fichier avec les permissions adquates afin que seul l'utilisateur courant puisse y accder, et ouvre le fichier en lecture/criture. Le modle de nom de fichier est une chane de caractres se terminant par "XXXXXX" (six X majuscules); mkstemp remplace les X par des caractres afin que le nom de fichier soit unique. La valeur de retour est un descripteur de fichier; utilisez les fonctions de la famille de write pour crire dans le fichier temporaire. Les fichiers temporaires crs par mkstemp ne sont pas effacs automatiquement. C'est vous de supprimer le fichier lorsque vous n'en avez plus besoin (les programmeurs devraient tre attentifs supprimer les fichiers temporaires; dans le cas contraire, le systme de fichiers /tmp pourrait se remplir, rendant le systme inutilisable). Si le fichier temporaire n'est destin qu' tre utilis par le programme et ne sera pas transmis un autre programme, c'est une bonne ide d'appeler unlink sur le fichier temporaire immdiatement. La fonction unlink supprime l'entre de rpertoire correspondant un fichier, mais comme le systme tient jour un dcompte du nombre de rfrences sur chaque fichier, un fichier n'est pas effac tant qu'il reste un descripteur de fichier ouvert pour ce fichier. Comme Linux ferme les descripteurs de fichiers quand un programme se termine, le fichier temporaire sera effac mme si votre programme se termine de manire anormale. Les deux fonctions du Listing tempfile prsentent l'utilisation de mkstemp. Utilises ensemble, ces fonctions facilitent l'criture d'un tampon mmoire vers un fichier temporaire (afin que la mmoire puisse tre libre ou rutilise) et sa relecture ultrieure. Utiliser mkstemp temp_file.c
#include <stdlib.h> #include <unistd.h> /* Handle sur un fichier temporaire cr avec write_temp_file. Avec cette implmentation, il s'agit d'un descripteur de fichier. */ typedef int temp_file_handle; /* crit LENGTH octets de BUFFER dans un fichier temporaire. Unlink est appel immdiatement sur le fichier temporaire. Renvoie un handle sur le fichier temporaire. */ temp_file_handle write_temp_file (char* buffer, size_t length) { /* Cre le nom du fichier et le fichier. XXXXXX sera remplac par des caractres donnant un nom de fichier unique. */ char temp_filename[] = "/tmp/temp_file.XXXXXX"; int fd = mkstemp (temp_filename); /* Appelle unlink immdiatement afin que le fichier soit supprim ds que le descripteur sera ferm. */ unlink (temp_filename); /* crit le nombre d'octets dans le fichier avant tout. */ write (fd, &length, sizeof (length)); /* crit des donnes proprement dites. */ write (fd, buffer, length); /* Utilise le descripteur de fichier comme handle sur le fichier temporaire. */ return fd; } /* Lit le contenu du fichier temporaire TEMP_FILE cr avec - 26 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utiliser mkstemp temp_file.c

write_temp_file. La valeur de retour est un tampon nouvellement allou avec ce contenu, que l'appelant doit librer avec free. *LENGTH est renseign avec la taille du contenu, en octets. Le fichier temporaire est supprim. */ char* read_temp_file (temp_file_handle temp_file, size_t* length) { char* buffer; /* Le handle sur TEMP_FILE est le descripteur du fichier temporaire. */ int fd = temp_file; /* Se place au dbut du fichier. */ lseek (fd, 0, SEEK_SET); /* Lit les donnes depuis le fichier temporaire. */ read (fd, length, sizeof (*length)); /* Alloue un buffer et lit les donnes. */ buffer = (char*) malloc (*length); read (fd, buffer, *length); /* Ferme le descripteur de fichier ce qui provoque la suppression du fichier temporaire. */ close (fd); return buffer; }

2-1-7-2 - Utilisation de tmpfile


Si vous utilisez les fonctions d'E/S de la bibliothque C et n'avez pas besoin de passer le fichier temporaire un autre programme, vous pouvez utiliser la fonction tmpfile. Elle cre et ouvre un fichier temporaire, et renvoie un pointeur de fichier. Le fichier temporaire a dj t trait par unlink, comme dans l'exemple prcdent, afin d'tre supprim automatiquement lorsque le pointeur sur le fichier est ferm (avec fclose) ou lorsque le programme se termine. GNU/Linux propose diverses autres fonctions pour gnrer des fichiers temporaires et des noms de fichiers temporaires, par exemple, mktemp, tmpnam et tempnam. N'utilisez pas ces fonctions, cependant, car elles souffrent des problmes de fiabilit et de scurit mentionns plus haut.

2-2 - Crer du Code Robuste


crire des programmes s'excutant correctement dans des conditions d'utilisation "normales" est dur; crire des programmes qui se comportent avec lgance dans des conditions d'erreur l'est encore plus. Cette section propose quelques techniques de codage pour trouver les bogues plus tt et pour dtecter et traiter les problmes dans un programme en cours d'excution. Les exemples de code prsents plus loin dans ce livre n'incluent dlibrment pas de code de vrification d'erreur ou de rcupration sur erreur car cela risquerait d'alourdir le code et de masquer la fonctionnalit prsente. Cependant, l'exemple final du Chapitre 11, Une Application GNU/Linux d'Exemple , est l pour montrer comment utiliser ces techniques pour produire des applications robustes.

2-2-1 - Utiliser assert


Un bon objectif conserver l'esprit en permanence lorsque l'on code des programmes est que des bogues ou des erreurs inattendues devraient conduire un crash du programme, ds que possible. Cela vous aidera trouver les bogues plus tt dans les cycles de dveloppement et de tests. Il est difficile de reprer les dysfonctionnements qui ne se signalent pas d'eux-mmes et n'apparaissent pas avant que le programme soit la disposition de l'utilisateur.

Une des mthodes les plus simples pour dtecter des conditions inattendues est la macro C standard assert. Elle prend comme argument une expression boolenne. Le programme s'arrte si l'expression est fausse, aprs avoir affich un message d'erreur contenant le nom du fichier, le numro de ligne et le texte de l'expression o l'erreur est survenue. La macro assert est trs utile pour une large gamme de tests de cohrence internes un programme. Par exemple, utilisez assert pour vrifier la validit des arguments passs une fonction, pour tester des prconditions et postconditions lors d'appels de fonctions (ou de mthodes en C++) et pour tester des valeurs de retour inattendues.

- 27 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Chaque utilisation de assert constitue non seulement une vrification de condition l'excution mais galement une documentation sur le fonctionnement du programme au coeur du code source. Si votre programme contient une instruction assert(condition) cela indique quelqu'un lisant le code source que condition devrait toujours tre vraie ce point du programme et si condition n'est pas vraie, il s'agit probablement d'un bogue dans le programme. Pour du code dans lequel les performances sont essentielles, les vrifications l'excution comme celles induites par l'utilisation de assert peuvent avoir un cot significatif en termes de performances. Dans ce cas, vous pouvez compiler votre code en dfinissant la macro NDEBUG, en utilisant l'option -DNDEBUG sur la ligne de commande du compilateur. Lorsque NDEBUG est dfinie, le prprocesseur supprimera les occurrences de la macro assert. Il est conseill de ne le faire que lorsque c'est ncessaire pour des raisons de performances et uniquement pour des fichiers sources concerns par ces questions de performances. Comme il est possible que le prprocesseur supprime les occurrences de assert, soyez attentif ce que les expressions que vous utilisez avec assert n'aient pas d'effet de bord. En particulier, vous ne devriez pas appeler de fonctions au sein d'expressions assert, y affecter des valeurs des variables ou utiliser des oprateurs de modification comme ++. Supposons, par exemple, que vous appeliez une fonction, do_something, de faon rptitive dans une boucle. La fonction do_something renvoie zro en cas de succs et une valeur diffrente de zro en cas d'chec, mais vous ne vous attendez pas ce qu'elle choue dans votre programme. Vous pourriez tre tent d'crire:
for (i = 0; i < 100; ++i) assert (do_something () == 0);

Cependant, vous pourriez trouver que cette vrification entrane une perte de performances trop importante et dcider plus tard de recompiler avec la macro NDEBUG dfinie. Cela supprimerait totalement l'appel assert, l'expression ne serait donc jamais value et do_something ne serait jamais appele. Voici un extrait de code effectuant la mme vrification, sans ce problme:
for (i = 0; i < 100; ++i) { int status = do_something (); assert (status == 0); }

Un autre lment conserver l'esprit est que vous ne devez pas utiliser assert pour tester les entres utilisateur. Les utilisateurs n'apprcient pas lorsque les applications plantent en affichant un message d'erreur obscur, mme en rponse une entre invalide. Vous devriez cependant toujours vrifier les saisies de l'utilisateur et afficher des messages d'erreurs comprhensibles. N'utilisez assert que pour des tests internes lors de l'excution. Voici quelques exemples de bonne utilisation d'assert: Vrification de pointeurs nuls, par exemple, comme arguments de fonction invalides. Le message d'erreur gnr par assert (pointer != NULL),
Assertion 'pointer != ((void *)0)' failed.

est plus utile que le message d'erreur qui serait produit dans le cas du drfencement d'un pointeur nul:
Erreur de Segmentation

- 28 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Vrification de conditions concernant la validit des paramtres d'une fonction. Par exemple, si une fonction ne doit tre appele qu'avec une valeur positive pour le paramtre foo, utilisez cette expression au dbut de la fonction:
assert (foo > 0);

Cela vous aidera dtecter les mauvaises utilisations de la fonction, et montre clairement quelqu'un lisant le code source de la fonction qu'il y a une restriction quant la valeur du paramtre. Ne vous retenez pas, utilisez assert librement partout dans vos programmes.

2-2-2 - Problmes Lors d'Appels Systme


La plupart d'entre nous a appris comment crire des programmes qui s'excutent selon un chemin bien dfini. Nous divisons le programme en tches et sous-tches et chaque fonction accomplit une tche en invoquant d'autres fonctions pour effectuer les oprations correspondant aux sous-tches. On attend d'une fonction qu'tant donn des entres prcises, elle produise une sortie et des effets de bord corrects. Les ralits matrielles et logicielles s'imposent face ce rve. Les ordinateurs ont des ressources limites; le matriel subit des pannes; beaucoup de programmes s'excutent en mme temps; les utilisateurs et les programmeurs font des erreurs. C'est souvent la frontire entre les applications et le systme d'exploitation que ces ralits se manifestent. Aussi, lors de l'utilisation d'appels systme pour accder aux ressources, pour effectuer des E/S ou d'autres fins, il est important de comprendre non seulement ce qui se passe lorsque l'appel fonctionne mais galement comment et quand l'appel peut chouer. Les appels systmes peuvent chouer de plusieurs faons. Par exemple: Le systme n'a plus de ressources (ou le programme dpasse la limite de ressources permises pour un seul programme). Par exemple, le programme peut tenter d'allouer trop de mmoire, d'crire trop de donnes sur le disque ou d'ouvrir trop de fichiers en mme temps. Linux peut bloquer un appel systme lorsqu'un programme tente d'effectuer une opration non permise. Par exemple, un programme pourrait tenter d'crire dans un fichier en lecture seule, d'accder la mmoire d'un autre processus ou de tuer un programme d'un autre utilisateur. Les arguments d'un appel systme peuvent tre invalides, soit parce-que l'utilisateur a fourni des entres invalides, soit cause d'un bogue dans le programme. Par exemple, le programme peut passer une adresse mmoire ou un descripteur de fichier invalide un appel systme; ou un programme peut tenter d'ouvrir un rpertoire comme un fichier rgulier ou passer le nom d'un fichier rgulier un appel systme qui attend un rpertoire. Un appel systme peut chouer pour des raisons externes un programme. Cela arrive le plus souvent lorsqu'un appel systme accde un priphrique matriel. Ce dernier peut tre dfectueux, ne pas supporter une opration particulire ou un lecteur peut tre vide. Un appel systme peut parfois tre interrompu par un vnement extrieur, comme l'arrive d'un signal. Il ne s'agit pas tout fait d'un chec de l'appel, mais il est de la responsabilit du programme appelant de relancer l'appel systme si ncessaire.

Dans un programme bien crit qui utilise abondamment les appels systme, il est courant qu'il y ait plus de code consacr la dtection et la gestion d'erreurs et d'autres circonstances exceptionnelles qu' la fonction principale du programme.

2-2-3 - Codes d'Erreur des Appels Systme


Une majorit des appels systme renvoie zro si tout se passe bien ou une valeur diffrente de zro si l'opration choue (toutefois, beaucoup drogent la rgle; par exemple, malloc renvoie un pointeur nul pour indiquer une erreur. Lisez toujours la page de manuel attentivement lorsque vous utilisez un appel systme). Bien que cette information
- 29 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

puisse tre suffisante pour dterminer si le programme doit continuer normalement, elle ne l'est certainement pas pour une rcupration fiable des erreurs. La plupart des appels systme utilisent une variable spciale appele errno pour stocker des informations additionnelles en cas d'chec(En ralit, pour des raisons d'isolement de threads, errno est implmente comme une macro, mais elle est utilise comme une variable globale.). Lorsqu'un appel choue, le systme positionne errno une valeur indiquant ce qui s'est mal pass. Comme tous les appels systme utilisent la mme variable errno pour stocker des informations sur une erreur, vous devriez copier sa valeur dans une autre variable immdiatement aprs l'appel qui a chou. La valeur de errno sera crase au prochain appel systme. Les valeurs d'erreur sont des entiers; les valeurs possibles sont fournies par des macros prprocesseur, libelles en majuscules par convention et commenant par E ? par exemple, EACCESS ou EINVAL. Utilisez toujours ces macros lorsque vous faites rfrence des valeurs de errno plutt que les valeurs entires. Incluez le fichier d'entte <errno.h> si vous utilisez des valeurs de errno. GNU/Linux propose une fonction utile, strerror, qui renvoie une chane de caractres contenant la description d'un code d'erreur de errno, utilisable dans les messages d'erreur. Incluez <string.h> si vous dsirez utiliser strerror. GNU/Linux fournit aussi perror, qui envoie la description de l'erreur directement vers le flux stderr. Passez perror une chane de caractres ajouter avant la description de l'erreur, qui contient habituellement le nom de la fonction qui a chou. Incluez <stdio.h> si vous utilisez perror. Cet extrait de code tente d'ouvrir un fichier; si l'ouverture choue, il affiche un message d'erreur et quitte le programme. Notez que l'appel de open renvoie un descripteur de fichier si l'appel se passe correctement ou -1 dans le cas contraire.
fd = open ("inputfile.txt", O_RDONLY); if (fd == -1) { /* L'ouverture a chou, affiche un message d'erreur et quitte. */ fprintf (stderr, "erreur lors de l'ouverture de : %s\n", strerror (errno)); exit (1); }

Selon votre programme et la nature de l'appel systme, l'action approprie lors d'un chec peut tre d'afficher un message d'erreur, d'annuler une opration, de quitter le programme, de ressayer ou mme d'ignorer l'erreur. Il est important cependant d'avoir du code qui gre toutes les raisons d'chec d'une faon ou d'une autre. Un code d'erreur possible auquel vous devriez particulirement vous attendre, en particulier avec les fonctions d'E/S, est EINTR. Certaines fonctions, comme read, select et sleep, peuvent mettre un certain temps s'excuter. Elles sont considres comme tant bloquantes car l'excution du programme est bloque jusqu' ce que l'appel se termine. Cependant, si le programme reoit un signal alors qu'un tel appel est en cours, celui-ci se termine sans que l'opration soit acheve. Dans ce cas, errno est positionne EINTR. En gnral, vous devriez relancer l'appel systme dans ce cas. Voici un extrait de code qui utilise l'appel chown pour faire de l'utilisateur user_id le propritaire d'un fichier dsign par path. Si l'appel choue, le programme agit selon la valeur de errno. Notez que lorsque nous dtectons ce qui semble tre un bogue, nous utilisons abort ou assert, ce qui provoque la gnration d'un fichier core. Cela peut tre utile pour un dbogage postmortem. Dans le cas d'erreurs irrcuprables, comme des conditions de manque de mmoire, nous utilisons plutt exit et une valeur de sortie diffrente de zro car un fichier core ne serait pas vraiment utile.
rval = chown (path, user_id, -1); if (rval != 0) { /* Sauvegarde errno car il sera cras par le prochain appel systme */ int error_code = errno; /* L'opration a chou ; chown doit retourner -1 dans ce cas. */ assert (rval == -1); /* Effectue l'action approprie en fonction de la valeur de errno. */ - 30 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) switch (error_code) { case EPERM: /* Permission refuse. */ case EROFS: /* PATH est sur un systme de fichiers en lecture seule */ case ENAMETOOLONG: /* PATH est trop long. */ case ENOENT: /* PATH n'existe pas. */ case ENOTDIR: /* Une des composantes de PATH n'est pas un rpertoire */ case EACCES: /* Une des composantes de PATH n'est pas accessible. */ /* Quelque chose ne va pas. Affiche un message d'erreur. */ fprintf (stderr, "erreur lors du changement de propritaire de %s: %s\n", path, strerror (error_code)); /* N'interrompt pas le programme ; possibilit de proposer l'utilisateur de choisir un autre fichier... */ break; case EFAULT: /* PATH contient une adresse mmoire invalide. Il s'agit srement d'un bogue */ abort (); case ENOMEM: /* Plus de mmoire disponible. */ fprintf (stderr, "%s\n", strerror (error_code)); exit (1); default: /* Autre code d'erreur innatendu. Nous avons tent de grer tous les codes d'erreur possibles ; si nous en avons oubli un il s'agit d'un bogue */ abort (); };

Vous pourriez vous contenter du code suivant qui se comporte de la mme faon si l'appel se passe bien:
rval = chown (path, user_id, -1); assert (rval == 0);

Mais en cas d'chec, cette alternative ne fait aucune tentative pour rapporter, grer ou reprendre aprs l'erreur. L'utilisation de la premire ou de la seconde forme ou de quelque chose entre les deux dpend des besoins en dtection et rcupration d'erreur de votre programme.

2-2-4 - Erreurs et Allocation de Ressources


Souvent, lorsqu'un appel systme choue, il est appropri d'annuler l'opration en cours mais de ne pas terminer le programme car il peut tre possible de continuer l'excution suite cette erreur. Une faon de le faire est de sortir de la fonction en cours en renvoyant un code de retour qui indique l'erreur. Si vous dcidez de quitter une fonction au milieu de son excution, il est important de vous assurer que toutes les ressources alloues prcdemment au sein de la fonction sont libres. Ces ressources peuvent tre de la mmoire, des descripteurs de fichier, des pointeurs sur des fichiers, des fichiers temporaires, des objets de synchronisation, etc. Sinon, si votre programme continue s'excuter, les ressources alloues pralablement l'chec de la fonction seront perdues. Considrons par exemple une fonction qui lit un fichier dans un tampon. La fonction pourrait passer par les tapes suivantes : Allouer le tampon; Ouvrir le fichier; Lire le fichier dans le tampon; Fermer le fichier; Retourner le tampon.

- 31 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Le Listing readfile montre une faon d'crire cette fonction. Si le fichier n'existe pas, l'tape 2 chouera. Une rponse approprie cet vnement serait que la fonction retourne NULL. Cependant, si le tampon a dj t allou l'tape~1, il y a un risque de perdre cette mmoire. Vous devez penser librer le tampon dans chaque bloc de la fonction qui en provoque la sortie. Si l'tape~3 ne se droule pas correctement, vous devez non seulement librer le tampon mais galement fermer le fichier. Librer les Ressources readfile.c
#include <fcntl.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> char* read_from_file (const char* filename, size_t length) { char* buffer; int fd; ssize_t bytes_read; /* Alloue le tampon. */ buffer = (char*) malloc (length); if (buffer == NULL) return NULL; /* Ouvre le fichier. */ fd = open (filename, O_RDONLY); if (fd == -1) { /* L'ouverture a chou. Libre le tampon avant de quitter. */ free (buffer); return NULL; } /* Lit les donnes. */ bytes_read = read (fd, buffer, length); if (bytes_read != length) { /* La lecture a chou. Libre le tampon et ferme fd avant de quitter. */ free (buffer); close (fd); return NULL; } /* Tout va bien. Ferme le fichier et renvoie le tampon. */ close (fd); return buffer; }

Linux libre la mmoire, ferme les fichiers et la plupart des autres ressources automatiquement lorsqu'un programme se termine, il n'est donc pas ncessaire de librer les tampons et de fermer les fichiers avant d'appeler exit. Vous pourriez nanmoins devoir librer manuellement d'autres ressources partages, comme les fichiers temporaires et la mmoire partage, qui peuvent potentiellement survivre un programme.

2-3 - crire et Utiliser des Bibliothques


Pratiquement tous les programmes sont lis une ou plusieurs bibliothques. Tout programme qui utilise une fonction C (comme printf ou malloc) sera li la bibliothque d'excution C. Si votre programme a une interface utilisateur graphique (Graphical User Interface, GUI), il sera li aux bibliothques de fentrage. Si votre programme utilise une base de donnes, le fournisseur de base de donnes vous fournira des bibliothques permettant d'accder la base de donne de faon pratique. Dans chacun de ces cas, vous devez dcider si la bibliothque doit tre lie de faon statique ou dynamique. Si vous choisissez la liaison statique, votre programme sera plus gros et plus difficile mettre jour, mais probablement plus simple dployer. Si vous optez pour la liaison dynamique, votre programme sera petit, plus simple mettre jour mais plus compliqu dployer. Cette section explique comment effectuer une liaison statique et dynamique, examine les deux options en dtail et donne quelques rgles empiriques pour dcider quelle est la meilleure dans votre cas.

- 32 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

2-3-1 - Archives
Une archive (ou bibliothque statique) est tout simplement une collection de fichiers objets stocke dans un seul fichier objet (une archive est en gros quivalent un fichier .LIB sous Windows). Lorsque vous fournissez une archive l'diteur de liens, il recherche au sein de cette archive les fichiers dont il a besoin, les extrait et les lie avec votre programme comme si vous aviez fourni ces fichiers objets directement. Vous pouvez crer une archive en utilisant la commande ar. Les fichiers archives utilisent traditionnellement une extension .a plutt que l'extension .o utilise par les fichiers objets ordinaires. Voici comment combiner test1.o et test2.o dans une seule archive libtest.a:
% ar cr libtest.a test1.o test2.o

Le drapeau cr indique ar de crer l'archive(Vous pouvez utiliser d'autres options pour supprimer un fichier d'une archive ou effectuer d'autres oprations sur l'archive. Ces oprations sont rarement utilises mais sont documentes sur la page de manuel de ar.). Vous pouvez maintenant lier votre programme avec cette archive en utilisant l'option ltest avec gcc ou g++, comme le dcrit la Section 1.2.2, Lier les Fichiers Objets du Chapitre 1, Pour Commencer . Lorsque l'diteur de liens dtecte une archive sur la ligne de commande, il y recherche les dfinitions des symboles (fonctions ou variables) qui sont rfrencs dans les fichiers objets qui ont dj t traits mais ne sont pas encore dfinis. Les fichiers objets qui dfinissent ces symboles sont extraits de l'archive et inclus dans l'excutable final. Comme l'diteur de liens effectue une recherche dans l'archive lorsqu'il la rencontre sur la ligne de commande, il est habituel de placer les archives la fin de la ligne de commande. Par exemple, supposons que test.c contienne le code du Listing testc et que app.c contienne celui du Listing appc. Contenu de la bibliothque test.c
int f () { return 3; }

Programme Utilisant la Bibliothque app.c


int main() { return f (); }

Supposons maintenant que test.o soit combin un autre fichier objet quelconque pour produire l'archive libtest.a. La ligne de commande suivante ne fonctionnerait pas:
% gcc -o app -L. -ltest app.o app.o: In function ?main?: app.o(.text+0x4): undefined reference to ?f? collect2: ld returned 1 exit status

Le message d'erreur indique que mme si libtest.a contient la dfinition de f, l'diteur de liens ne la trouve pas. C'est d au fait que libtest.a a t inspecte lorsqu'elle a t dtecte pour la premire fois et ce moment l'diteur de liens n'avait pas rencontr de rfrence f. Par contre, si l'on utilise la ligne de commande suivante, aucun message d'erreur n'est mis:
% gcc -o app app.o -L. -ltest

- 33 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La raison en est que la rfrence f dans app.o oblige l'diteur de liens inclure le fichier objet test.o depuis l'archive libtest.a.

2-3-2 - Bibliothques Partages


Une bibliothque partage (galement appele objet partag ou bibliothque dynamique) est similaire une archive en ceci qu'il s'agit d'un groupe de fichiers objets. Cependant, il y a beaucoup de diffrences importantes. La diffrence la plus fondamentale est que lorsqu'une bibliothque partage est lie un programme, l'excutable final ne contient pas vraiment le code prsent dans la bibliothque partage. Au lieu de cela, l'excutable contient simplement une rfrence cette bibliothque. Si plusieurs programmes sur le systme sont lis la mme bibliothque partage, ils rfrenceront tous la bibliothque, mais aucun ne l'inclura rellement. Donc, la bibliothque est partage entre tous les programmes auxquels elle est lie. Une seconde diffrence importante est qu'une bibliothque partage n'est pas seulement une collection de fichiers objets, parmi lesquels l'diteur de liens choisit ceux qui sont ncessaires pour satisfaire les rfrences non dfinies. Au lieu de cela, les fichiers objets qui composent la bibliothque sont combins en un seul fichier objet afin qu'un programme li la bibliothque partage inclut toujours tout le code de la bibliothque plutt que de n'inclure que les portions ncessaires. Pour crer une bibliothque partage, vous devez compiler les objets qui constitueront la bibliothque en utilisant l'option -fPIC du compilateur, comme ceci:
% gcc -c -fPIC test1.c

L'option -fPIC indique au compilateur que vous allez utiliser test1.o en tant qu'lment d'un objet partag. PIC signifie code indpendant de la position. Les fonctions d'une bibliothque partage peuvent tre charges diffrentes adresses dans diffrents programmes, le code de l'objet partag ne doit donc pas dpendre de l'adresse (ou position) laquelle il est charg. Cette considration n'a pas d'impact votre niveau, en tant que programmeur, except que vous devez vous souvenir d'utiliser l'option -fPIC lors de la compilation du code utilis pour la bibliothque partage. Puis, vous combinez les fichiers objets au sein d'une bibliothque partage, comme ceci:
% gcc -shared -fPIC -o libtest.so test1.o test2.o

L'option -shared indique l'diteur de liens de crer une bibliothque partage au lieu d'un excutable ordinaire. Les bibliothques partages utilisent l'extension .so, ce qui signifie objet partag (shared object). Comme pour les archives statiques, le nom commence toujours par lib pour indiquer que le fichier est une bibliothque. Lier un programme une bibliothque partage se fait de la mme manire que pour une archive statique. Par exemple, la ligne suivante liera le programme libtest.so si elle est dans le rpertoire courant ou dans un des rpertoires de recherche de bibliothques du systme:
% gcc -o app app.o -L. -ltest

Supposons que libtest.a et libtest.so soient disponibles. L'diteur de liens doit choisir une seule des deux bibliothques. Il commence par rechercher dans chaque rpertoire (tout d'abord ceux indiqus par l'option -L, puis dans les rpertoires standards). Lorsque l'diteur de liens trouve un rpertoire qui contient soit libtest.a soit libtest.so, il interrompt ses recherches. Si une seule des deux variantes est prsente dans le rpertoire, l'diteur de liens la
- 34 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

slectionne. Sinon, il choisit la version partage moins que vous ne lui spcifiiez explicitement le contraire. Vous pouvez utiliser l'option -static pour utiliser les archives statiques. Par exemple, la ligne de commande suivante utilisera l'archive libtest.a, mme si la bibliothque partage libtest.so est disponible:
% gcc -static -o app app.o -L. -ltest

La commande ldd indique les bibliothques partages lies un programme. Ces bibliothques doivent tre disponibles l'excution du programme. Notez que ldd indiquera une bibliothque supplmentaire appele ld-linux.so qui fait partie du mcanisme de liaison dynamique de GNU/Linux.

2-3-2-1 - Utiliser LD_LIBRARY_PATH


Lorsque vous liez un programme une bibliothque partage, l'diteur de liens ne place pas le chemin d'accs complet la bibliothque dans l'excutable. Il n'y place que le nom de la bibliothque partage. Lorsque le programme est excut, le systme recherche la bibliothque partage et la charge. Par dfaut, cette recherche n'est effectue que dans /lib et /usr/lib par dfaut. Si une bibliothque partage lie votre programme est place un autre endroit que dans ces rpertoires, elle ne sera pas trouve et le systme refusera de lancer le programme. Une solution ce problme est d'utiliser les options -Wl, -rpath lors de l'dition de liens du programme. Supposons que vous utilisiez ceci:
% gcc -o app app.o -L. -ltest -Wl,-rpath,/usr/local/lib

Dans ce cas, lorsqu'on lance le programme app, recherche les bibliothques ncessaires dans /usr/local/lib. Une autre solution ce problme est de donner une valeur la variable d'environnement LD_LIBRARY_PATH au lancement du programme. Tout comme la variable d'environnement PATH, LD_LIBRARY_PATH est une liste de rpertoires spars par deux-points. Par exemple, si vous positionnez LD_LIBRARY_PATH /usr/local/lib:/opt/lib alors les recherches seront effectues au sein de /usr/local/lib et /opt/lib avant les rpertoires standards /lib et /usr/lib. Vous devez tre conscient que si LD_LIBRARY_PATH est renseign, l'diteur de liens recherchera les bibliothques au sein des rpertoires qui y sont spcifis avant ceux passs via l'option -L lorsqu'il cre un excutable(Vous pourriez voir des rfrences LD_RUN_PATH dans certaines documentations en ligne. Ne croyez pas ce que vous lisez, cette variable n'a en fait aucun effet sous GNU/Linux.).

2-3-3 - Bibliothques Standards


% gcc -o compute compute.c -lm

Si vous crivez un programme C++ et le liez en utilisant les commandes c++ ou g++, vous aurez galement la bibliothque standard du C++, libstdc++.

2-3-4 - Dpendances entre Bibliothques


Les bibliothques dpendent souvent les unes des autres. Par exemple, beaucoup de systmes GNU/Linux proposent libtiff, une bibliothque contenant des fonctions pour lire et crire des fichiers images au format TIFF. Cette bibliothque utilise son tour les bibliothques libjpeg (routines de manipulation d'images JPEG) et libz (routines de compression). Le Listing libtiff prsente un trs petit programme qui utilise libtiff pour ouvrir une image TIFF.

- 35 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utilisation de libtiff tifftest.c


#include <stdio.h> #include <tiffio.h> int main (int argc, char** argv) { TIFF* tiff; tiff = TIFFOpen (argv[1], "r"); TIFFClose (tiff); return 0; }

Sauvegardez ce fichier sous le nom de tifftest.c. Pour le compiler et le lier libtiff, spcifiez -ltiff sur votre ligne de commande:
% gcc -o tifftest tifftest.c -ltiff

Par dfaut, c'est la version partage de la bibliothque libtiff, situe dans /usr/lib/libtiff.so, qui sera utilise. Comme libtiff utilise libjpeg et libz, les versions partages de ces bibliothque sont galement lies (une bibliothque partage peut pointer vers d'autres bibliothques partages dont elle dpend). Pour vrifier cela, utilisez la commande ldd:
% ldd tifftest libtiff.so.3 => /usr/lib/libtiff.so.3 (0x4001d000) libc.so.6 => /lib/libc.so.6 (0x40060000) libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x40155000) libz.so.1 => /usr/lib/libz.so.1 (0x40174000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) ...

Pour lier ce programme de faon statique, vous devez spcifier les deux autres bibliothques:
% gcc -static -o tifftest tifftest.c -ltiff -ljpeg -lz -lm

De temps en temps, deux bibliothques dpendent mutuellement l'une de l'autre. En d'autres termes, la premire archive fait rfrence des symboles dfinis dans la seconde et vice versa. Cette situation rsulte gnralement d'une mauvaise conception mais existe. Dans ce cas, vous pouvez fournir une mme bibliothque plusieurs fois sur la ligne de commande. L'diteur de liens recherchera les symboles manquants chaque apparition de la bibliothque. Par exemple, cette ligne provoque deux recherches de symboles au sein de libfoo:
% gcc -o app app.o -lfoo -lbar -lfoo

Donc, mme si libfoo.a fait rfrence des symboles de libbar.a et vice versa, le programme sera compil avec succs.

2-3-5 - Avantages et Inconvnients


Maintenant que vous savez tout propos des archives statiques et des bibliothques partages, vous vous demandez probablement lesquelles utiliser. Il y a quelques lments garder l'esprit. Un avantage majeur des bibliothques partages est qu'elles permettent d'conomiser de la place sur le systme o le programme est install. Si vous installez dix programmes et qu'ils utilisent tous la mme bibliothque partage, vous conomiserez beaucoup d'espace utiliser une bibliothque partage. Si vous utilisez une archive statique la place, l'archive est incluse dans les dix programmes. Donc, utiliser des bibliothques partages permet d'conomiser de l'espace disque. Cela rduit galement le temps de tlchargement si votre programme est distribu via le Web.

- 36 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Un avantage li aux bibliothques partages est que les utilisateurs peuvent mettre niveau les bibliothques sans mettre niveau tous les programmes qui en dpendent. Par exemple, supposons que vous criez une bibliothque partage qui gre les connexions HTTP. Beaucoup de programmes peuvent dpendre de cette bibliothque. Si vous dcouvrez un bogue dans celle-ci, vous pouvez mettre niveau la bibliothque. Instantanment, tous les programmes qui en dpendent bnficieront de la correction ; vous n'avez pas repasser par une tape d'dition de liens pour tous les programmes, comme vous le feriez avec une archive statique. Ces avantages pourraient vous faire penser que vous devez toujours utiliser les bibliothques partages. Cependant, il existe des raisons valables pour utiliser les archives statiques. Le fait qu'une mise jour de la bibliothque affecte tous les programmes qui en dpendent peut reprsenter un inconvnient. Par exemple, si vous dveloppez un logiciel critique, il est prfrable de le lier avec une archive statique afin qu'une mise jour des bibliothques sur le systme hte n'affecte pas votre programme (autrement, les utilisateurs pourraient mettre jour les bibliothques, empchant votre programme de fonctionner, puis appeler votre service client en disant que c'est de votre faute !). Si vous n'tes pas sr de pouvoir installer vos bibliothques dans /lib ou /usr/lib, vous devriez dfinitivement rflchir deux fois avant d'utiliser une bibliothque partage (vous ne pourrez pas installer vos bibliothques dans ces rpertoires si votre logiciel est destin pouvoir tre install par des utilisateurs ne disposant pas des droits d'administrateur). De plus, l'astuce de l'option -Wl, -rpath ne fonctionnera pas si vous ne savez pas o seront places les bibliothques en dfinitive. Et demander vos utilisateurs de positionner LD_LIBRARY_PATH leur impose une tape de configuration supplmentaire. Comme chaque utilisateur doit le faire individuellement, il s'agit d'une contrainte additionnelle non ngligeable. Il faut bien peser ces avantages et inconvnients pour chaque programme que vous distribuez.

2-3-6 - Chargement et Dchargement Dynamiques


Il est parfois utile de pouvoir charger du code au moment de l'excution sans lier ce code explicitement. Par exemple, considrons une application qui supporte des plugins , comme un navigateur Web. Le navigateur permet des dveloppeurs tiers de crer des plugins pour fournir des fonctionnalits supplmentaires. Ces dveloppeurs crent des bibliothques partages et les placent un endroit prdfini. Le navigateur charge alors automatiquement le code de ces bibliothques. Cette fonctionnalit est disponible sous Linux en utilisant la fonction dlopen. Vous ouvrez une bibliothque appele libtest.so en appelant dlopen comme ceci:
dlopen ("libtest.so", RTLD_LAZY)

(Le second paramtre est un drapeau qui indique comment lier les symboles de la bibliothque partage. Vous pouvez consulter les pages de manuel de dlopen pour plus d'informations, mais RTLD_LAZY est gnralement l'option conseille). Pour utiliser les fonctions de chargement dynamique, incluez l'entte <dlfcn.h> et liez avec l'option -ldl pour inclure la bibliothque libdl. La valeur de retour de cette fonction est un void * utilis comme rfrence sur la bibliothque partage. Vous pouvez passer cette valeur la fonction dlsym pour obtenir l'adresse d'une fonction charge avec la bibliothque partage. Par exemple, si libtest.so dfinit une fonction appele my_function, vous pourriez l'appeler comme ceci:
void* handle = dlopen ("libtest.so", RTLD_LAZY); void (*test)() = dlsym (handle, "my_function"); (*test)(); dlclose (handle);

L'appel systme dlsym peut galement tre utilis pour obtenir un pointeur vers une variable static de la bibliothque partage.

- 37 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

dlopen et dlsym renvoient toutes deux NULL si l'appel choue. Dans ce cas, vous pouvez appeler dlerror (sans paramtre) pour obtenir un message d'erreur lisible dcrivant le problme. La fonction dlclose dcharge la bibliothque. Techniquement, dlopen ne charge la bibliothque que si elle n'est pas dj charge. Si elle l'est, dlopen se contente d'incrmenter le compteur de rfrences pour la bibliothque. De mme, dlclose dcrmente ce compteur et ne dcharge la bibliothque que s'il a atteint zro. Si vous crivez du code en C++ dans votre bibliothque partage, vous devriez dclarer les fonctions et variables que vous voulez rendre accessible de l'extrieur avec le modificateur extern "C". Par exemple, si la fonction C++ my_function est dans une bibliothque partage et que vous voulez la rendre accessible par dlsym, vous devriez la dclarer comme suit:
extern "C" void my_function ();

Cela vite que le compilateur C++ ne modifie le nom de la fonction ce qui rsulterait en un nom diffrent l'aspect sympathique qui encode des informations supplmentaires sur la fonction. Un compilateur C ne modifie pas les noms; il utilise toujours les noms que vous donnez votre fonction ou variable.

- 38 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

3 - Processus
Une instance d'un programme en cours d'excution est appele un processus. Si vous avez deux fentres de terminal sur votre cran, vous excutez probablement deux fois le mme programme de terminal -- vous avez deux processus de terminal. Chaque fentre de terminal excute probablement un shell; chaque shell en cours d'excution est un processus indpendant. Lorsque vous invoquez un programme depuis le shell, le programme correspondant est excut dans un nouveau processus; le processus shell reprend lorsque ce processus ce termine. Les programmeurs expriments utilisent souvent plusieurs processus cooprant entre eux au sein d'une mme application afin de lui permettre de faire plus d'une chose la fois, pour amliorer sa robustesse et utiliser des programmes dj existants. La plupart des fonctions de manipulation de processus dcrites dans ce chapitre sont similaires celles des autres systmes UNIX. La majorit d'entre elles est dclare au sein de l'entte <unistd.h>; vrifiez la page de manuel de chaque fonction pour vous en assurer.

3-1 - Aperu des Processus


Mme lorsque tout ce que vous faites est tre assis devant votre ordinateur, des processus sont en cours d'excution. Chaque programme utilise un ou plusieurs processus. Commenons par observer les processus dj prsents sur votre ordinateur.

3-1-1 - Identifiants de Processus


Chaque processus d'un systme Linux est identifi par son identifiant de processus unique, quelquefois appel pid (process ID). Les identifiants de processus sont des nombres de 16 bits assigns de faon squentielle par Linux aux processus nouvellement crs. Chaque processus a galement un processus parent (sauf le processus spcial init, dcrit dans la Section 3.4.3, Processus Zombies ). Vous pouvez donc vous reprsenter les processus d'un systme Linux comme un arbre, le processus init tant la racine. L'identifiant de processus parent (parent process ID), ou ppid, est simplement l'identifiant du parent du processus. Lorsque vous faites rfrence aux identifiants de processus au sein d'un programme C ou C++, utilisez toujours le typedef pid_t, dfini dans <sys/types.h>. Un programme peut obtenir l'identifiant du processus au sein duquel il s'excute en utilisant l'appel systme getpid() et l'identifiant de son processus parent avec l'appel systme getppid(). Par exemple, le programme du Listing printpid affiche son identifiant de processus ainsi que celui de son parent. Afficher l'Identifiant de Processus print-pid.c
#include <stdio.h> #include <unistd.h> int main { printf printf return } () ("L'identifiant du processus est %d\n", (int) getpid ()); ("L'identifiant du processus parent est %d\n", (int) getppid ()); 0;

Notez que si vous invoquez ce programme plusieurs fois, un identifiant de processus diffrent est indiqu chaque fois car chaque invocation cre un nouveau processus. Cependant, si vous l'invoquez toujours depuis le mme shell, l'identifiant du processus parent (c'est--dire l'identifiant de processus du shell) est le mme.

3-1-2 - Voir les Processus Actifs


La commande ps affiche les processus en cours d'excution sur votre systme. La version GNU/Linux de ps dispose d'un grand nombre d'options car elle tente d'tre compatible avec les versions

- 39 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

de ps de plusieurs autres variantes d'UNIX. Ces options contrlent les processus lists et les informations les concernant qui sont affiches. Par dfaut, invoquer ps affiche les processus contrls par le terminal ou la fentre de terminal la commande est invoque. Par exemple:
% ps PID TTY TIME CMD 21693 pts/8 00:00:00 bash 21694 pts/8 00:00:00 ps

Cette invocation de ps montre deux processus. Le premier, bash, est le shell s'excutant au sein du terminal. Le second est l'instance de ps en cours d'excution. La premire colonne, PID, indique l'identifiant de chacun des processus. Pour un aperu plus dtaill des programmes en cours d'excution sur votre systme GNU/Linux, invoquez cette commande:
% ps -e -o pid,ppid,command

L'option -e demande ps d'afficher tous processus en cours d'excution sur le systme. L'option -o pid,ppid,command indique ps les informations afficher sur chaque processus -- dans ce cas, l'identifiant de processus, l'identifiant du processus parent et la commande correspondant au processus. Avec l'option -o de la commande ps, vous indiquez les informations sur les processus que vous voulez voir s'afficher sous forme d'une liste de valeurs spares par des virgules. Par exemple, ps -o pid,user,start_time,command affiche l'identifiant de processus, le nom de l'utilisateur propritaire du processus, l'heure de dmarrage du processus et la commande s'excutant au sein du processus. Consultez la page de manuel de ps pour une liste complte des codes. Vous pouvez utiliser les options -f (full listing, listing complet), -l (long listing) ou -j (jobs listing) pour obtenir trois formats de listing prdfinis. Voici les premires et dernires lignes de la sortie de cette commande sur mon systme. Elles peuvent diffrer des vtres, selon les programmes en cours d'excution sur votre systme:
% ps -e -o pid,ppid,command PID PPID COMMAND 1 0 init [5] 2 1 [kflushd] 3 1 [kupdate] ... 21725 21693 xterm 21727 21725 bash 21728 21727 ps -e -o pid,ppid,command

Notez que l'identifiant du processus parent de la commande ps, 21727, est l'identifiant du processus de bash, le shell depuis lequel j'ai invoqu ps. L'identifiant du processus parent de bash est 21725, l'identifiant du processus du programme xterm dans lequel le shell s'excute.

3-1-3 - Tuer un Processus


Vous pouvez tuer un processus en cours d'excution grce la commande kill. Spcifiez simplement sur la ligne de commande l'identifiant du processus tuer.

- 40 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La commande kill envoie un signal(Vous pouvez galement utiliser la commande kill pour envoyer d'autres signaux un processus. Reportez-vous la Section 3.4, Fin de Processus ) SIGTERM, ou de terminaison au processus. Cela termine le processus, moins que le programme en cours d'excution ne gre ou ne masque le signal SIGTERM. Les signaux sont dcrits dans la Section 3.3, Signaux .

3-2 - Crer des Processus


Deux techniques courantes sont utilises pour crer de nouveaux processus. La premire est relativement simple mais doit tre utilise avec modration car elle est peu performante et prsente des risques de scurit considrables. La seconde technique est plus complexe mais offre une flexibilit, une rapidit et une scurit plus grandes.

3-2-1 - Utiliser system


La fonction system de la bibliothque standard propose une manire simple d'excuter une commande depuis un programme, comme si la commande avait t tape dans un shell. En fait, system cre un sous-processus dans lequel s'excute le shell Bourne standard (/bin/sh) et passe la commande ce shell pour qu'il l'excute. Par exemple, le programme du Listing system invoque la commande ls pour afficher le contenu du rpertoire racine, comme si vous aviez saisi ls -l / dans un shell. Utiliser la Fonction system system.c
int main () { int return_value; return_value = system ("ls -l /"); return return_value; }

La fonction system renvoie le code de sortie de la commande shell. Si le shell lui-mme ne peut pas tre lanc, system renvoie 127; si une autre erreur survient, system renvoie -1. Comme la fonction system utilise un shell pour invoquer votre commande elle est soumise aux fonctionnalits, limitations et failles de scurit du shell systme. Vous ne pouvez pas vous reposer sur la disponibilit d'une version spcifique du shell Bourne. Sur beaucoup de systmes UNIX, /bin/sh est en fait un lien symbolique vers un autre shell. Par exemple, sur la plupart des systmes GNU/Linux, /bin/sh pointe vers bash (le Bourne-Again SHell) et des distributions diffrentes de GNU/Linux utilisent diffrentes version de bash. Invoquer un programme avec les privilges root via la fonction system peut donner des rsultats diffrents selon le systme GNU/Linux. Il est donc prfrable d'utiliser la mthode de fork et exec pour crer des processus.

3-2-2 - Utiliser fork et exec


Les API DOS et Windows proposent la famille de fonctions spawn. Ces fonctions prennent en argument le nom du programme excuter et crent une nouvelle instance de processus pour ce programme. Linux ne dispose pas de fonction effectuant tout cela en une seule fois. Au lieu de cela, Linux offre une fonction, fork, qui produit un processus fils qui est l'exacte copie de son processus parent. Linux fournit un autre jeu de fonctions, la famille exec, qui fait en sorte qu'un processus cesse d'tre une instance d'un certain programme et devienne une instance d'un autre. Pour crer un nouveau processus, vous utilisez tout d'abord fork pour crer une copie du processus courant. Puis vous utilisez exec pour transformer un de ces processus en une instance du programme que vous voulez crer.

3-2-2-1 - Appeler fork et exec


Lorsqu'un programme appelle fork, une copie du processus, appele processus fils, est cre. Le processus parent continue d'excuter le programme partir de l'endroit o fork a t appel. Le processus fils excute lui aussi le mme programme partir du mme endroit.
- 41 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Qu'est-ce qui diffrencie les deux processus alors? Tout d'abord, le processus fils est un nouveau processus et dispose donc d'un nouvel identifiant de processus, distinct de celui de l'identifiant de son processus parent. Un moyen pour un programme de savoir s'il fait partie du processus pre ou du processus fils est d'appeler la mthode getpid. Cependant, la fonction fork renvoie des valeurs diffrentes aux processus parent et enfant -- un processus rentre dans le fork et deux en ressortent avec des valeurs de retour diffrentes. La valeur de retour dans le processus pre est l'identifiant du processus fils. La valeur de retour dans le processus fils est zro. Comme aucun processus n'a l'identifiant zro, il est facile pour le programme de dterminer s'il s'excute au sein du processus pre ou du processus fils. Le Listing fork est un exemple d'utilisation de fork pour dupliquer un processus. Notez que le premier bloc de la structure if n'est excut que dans le processus parent alors que la clause else est excute dans le processus fils. Utiliser fork pour Dupliquer un Processus fork.c
int main () { pid_t child_pid; printf ("ID de processus du programme principal : %d\n",(int)getpid ()); child_pid = fork (); if (child_pid != 0) { printf ("je suis le processus parent, ID : %d\n", (int) getpid ()); printf ("Identifiant du processus fils : %d\n", (int) child_pid); } else printf ("je suis le processus fils, ID : %d\n", (int) getpid ()); return 0; }

3-2-2-2 - Utiliser la Famille de exec


Les fonctions exec remplacent le programme en cours d'excution dans un processus par un autre programme. Lorsqu'un programme appelle la fonction exec, le processus cesse immdiatement d'excuter ce programme et commence l'excution d'un autre depuis le dbut, en supposant que l'appel exec se droule correctement. Au sein de la famille de exec existent plusieurs fonctions qui varient lgrement quant aux possibilits qu'elles proposent et la faon de les appeler. Les fonctions qui contiennent la lettre p dans leur nom (execvp et execlp) reoivent un nom de programme qu'elles recherchent dans le path courant; il est ncessaire de passer le chemin d'accs complet du programme aux fonctions qui ne contiennent pas de p. Les fonctions contenant la lettre v dans leur nom (execv, execvp et execve) reoivent une liste d'arguments passer au nouveau programme sous forme d'un tableau de pointeurs vers des chanes termin par NULL. Les fonctions contenant la lettre l (execl, execlp et execle) reoivent la liste d'arguments via le mcanisme du nombre d'arguments variables du langage C. Les fonctions qui contiennent la lettre e dans leur nom (execve et execle) prennent un argument supplmentaire, un tableau de variables d'environnement. L'argument doit tre un tableau de pointeurs vers des chanes termin par NULL. Chaque chane doit tre de la forme "VARIABLE=valeur".

Dans la mesure o exec remplace le programme appelant par un autre, on n'en sort jamais moins que quelque chose ne se droule mal. Les arguments passs un programme sont analogues aux arguments de ligne de commande que vous transmettez un programme lorsque vous le lancez depuis un shell. Ils sont accessibles par le biais des paramtres argc et argv de main. Souvenez-vous que lorsqu'un programme est invoqu depuis le shell, celui-ci place dans le premier lment de la liste d'arguments (argv[0]) le nom du programme, dans le second (argv[1]) le premier paramtre en ligne de commande, etc. Lorsque vous utilisez une fonction exec dans votre programme, vous devriez, vous aussi, passer le nom du programme comme premier lment de la liste d'arguments.

- 42 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

3-2-2-3 - Utiliser fork et exec


Un idiome courant pour l'excution de sous-programme au sein d'un programme est d'effectuer un fork puis d'appeler exec pour le sous-programme. Cela permet au programme appelant de continuer s'excuter au sein du processus parent alors qu'il est remplac par le sous-programme dans le processus fils. Le programme du Listing forkexec, comme celui du Listing system, liste le contenu du rpertoire racine en utilisant la commande ls. Contrairement l'exemple prcdent, cependant, il utilise la commande ls directement, en lui passant les arguments -l et / plutt que de l'invoquer depuis un shell. Utiliser fork et exec fork-exec.c
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> /* Cre un processus fils excutant un nouveau programme. PROGRAM est le nom du programme excuter ; le programme est recherch dans le path. ARG_LIST est une liste termine par NULL de chanes de caractres passer au programme comme liste d'arguments. Renvoie l'identifiant du processus nouvellement cr. */ int spawn (char* program, char** arg_list) { pid_t child_pid; /* Duplique ce processus. */ child_pid = fork (); if (child_pid != 0) /* Nous sommes dans le processus parent. */ return child_pid; else { /* Excute PROGRAM en le recherchant dans le path. */ execvp (program, arg_list); /* On ne sort de la fonction execvp uniquement si une erreur survient. */ fprintf (stderr, "une erreur est survenue au sein de execvp\n"); abort (); } } int main () { /* Liste d'arguments passer la commande "ls". */ char* arg_list[] = { "ls", /* argv[0], le nom du programme. */ "-l", "/", NULL /* La liste d'arguments doit se terminer par NULL. */ }; /* Cre un nouveau processus fils excutant la commande "ls". Ignore l'identifiant du processus fils renvoy. */ spawn ("ls", arg_list); printf ("Fin du programme principal\n"); return 0; }

3-2-3 - Ordonnancement de Processus


Linux ordonnance le processus pre indpendamment du processus fils; il n'y a aucune garantie sur celui qui sera excut en premier ou sur la dure pendant laquelle le premier s'excutera avant que Linux ne l'interrompe et ne passe la main l'autre processus (ou un processus quelconque s'excutant sur le systme). Dans notre cas, lorsque le processus parent se termine, la commande ls peut s'tre excute entirement, partiellement ou pas du tout(Une mthode visant excuter les deux processus de manire squentielle est prsente dans la Section 3.4.1, Attendre la Fin d'un Processus .). Linux garantit que chaque processus finira par s'excuter -- aucun processus ne sera cours de ressources.

- 43 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Vous pouvez indiquer qu'un processus est moins important -- et devrait avoir une priorit infrieure -- en lui assignant une valeur de priorit d'ordonnancement(NdT. le terme original est niceness (gentillesse), ce qui explique que plus la valeur est grande, plus le processus est gentil et donc moins il est prioritaire.) plus grande. Par dfaut, chaque processus a une priorit d'ordonnancement de zro. Une valeur plus leve signifie que le processus a une priorit d'excution plus faible; inversement un processus avec une priorit d'ordonnancement plus faible (c'est-dire, ngative) obtient plus de temps d'excution. Pour lancer un programme avec une priorit d'ordonnancement diffrente de zro, utilisez la commande nice, en spcifiant la valeur de la priorit d'ordonnancement avec l'option -n. Par exemple, voici comment vous pourriez invoquer la commande sort input.txt > output.txt, une opration de tri longue, avec une priorit rduite afin qu'elle ne ralentisse pas trop le systme:
% nice -n 10 sort input.txt > output.txt

Vous pouvez utiliser la commande renice pour changer la priorit d'ordonnancement d'un processus en cours d'excution depuis la ligne de commande. Pour changer la priorit d'ordonnancement d'un processus en cours d'excution par programmation, utilisez la fonction nice. Son argument est une valeur d'ajustement qui est ajoute la valeur de la priorit d'ordonnancement du processus qui l'appelle. Souvenez-vous qu'une valeur positive augmente la priorit d'ordonnancement du processus et donc rduit la priorit d'excution du processus. Notez que seuls les processus disposant des privilges root peuvent lancer un processus avec une priorit d'ordonnancement ngative ou rduire la valeur de la priorit d'ordonnancement d'un processus en cours d'excution. Cela signifie que vous ne pouvez passer des valeurs ngatives aux commandes nice et renice que lorsque vous tes connect en tant que root et seul un processus s'excutant avec les privilges root peut passer une valeur ngative la fonction nice. Cela vite que des utilisateurs ordinaires puissent s'approprier tout le temps d'excution.

3-3 - Signaux
Les signaux sont des mcanismes permettant de manipuler et de communiquer avec des processus sous Linux. Le sujet des signaux est vaste; nous traiterons ici quelques uns des signaux et techniques utilises pour contrler les processus. Un signal est un message spcial envoy un processus. Les signaux sont asynchrones; lorsqu'un processus reoit un signal, il le traite immdiatement, sans mme terminer la fonction ou la ligne de code en cours. Il y a plusieurs douzaines de signaux diffrents, chacun ayant une signification diffrente. Chaque type de signal est caractris par son numro de signal, mais au sein des programmes, on y fait souvent rfrence par un nom. Sous Linux, ils sont dfinis dans /usr/include/bits/signum.h (vous ne devriez pas inclure ce fichier directement dans vos programmes, utilisez plutt <signal.h>). Lorsqu'un processus reoit un signal, il peut agir de diffrentes faons, selon l'action enregistre pour le signal. Pour chaque signal, il existe une action par dfaut, qui dtermine ce qui arrive au processus si le programme ne spcifie pas d'autre comportement. Pour la plupart des signaux, le programme peut indiquer un autre comportement -- soit ignorer le signal, soit appeler un gestionnaire de signal, fonction charge de traiter le signal. Si un gestionnaire de signal est utilis, le programme en cours d'excution est suspendu, le gestionnaire est excut, puis, une fois celuici termin, le programme reprend. Le systme Linux envoie des signaux aux processus en rponse des conditions spcifiques. Par exemple, SIGBUS (erreur de bus), SIGSEGV (erreur de segmentation) et SIGFPE (exception de virgule flottante) peuvent tre envoys un programme essayant d'effectuer une action non autorise. L'action par dfaut pour ces trois signaux est de terminer le processus et de produire un ficher core.

- 44 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Un processus peut galement envoyer des signaux un autre processus. Une utilisation courante de ce mcanisme est de terminer un autre processus en lui envoyant un signal SIGTERM ou SIGKILL(Quelle est la diffrence? Le signal SIGTERM demande au processus de se terminer; le processus peut ignorer la requte en masquant ou ignorant le signal. Le signal SIGKILL tue toujours le processus immdiatement car il est impossible de masquer ou ignorer SIGKILL.). Une autre utilisation courante est d'envoyer une commande un programme en cours d'excution. Deux signaux "dfinis par l'utilisateur" sont rservs cet effet: SIGUSR1 et SIGUSR2. Le signal SIGHUP est galement parfois utilis dans ce but, habituellement pour rveiller un programme inactif ou provoquer une relecture du fichier de configuration. La fonction sigaction peut tre utilise pour paramtrer l'action effectuer en rponse un signal. Le premier paramtre est le numro du signal. Les deux suivants sont des pointeurs vers des structures sigaction; la premire contenant l'action effectuer pour ce numro de signal, alors que la seconde est renseigne avec l'action prcdente. Le champ le plus important, que ce soit dans la premire ou la seconde structure sigaction est sa_handler. Il peut prendre une des trois valeurs suivantes: SIG_DFL, qui correspond l'action par dfaut pour le signal; SIG_IGN, qui indique que le signal doit tre ignor; Un pointeur vers une fonction de gestion de signal. La fonction doit prendre un paramtre, le numro du signal et tre de type void.

Comme les signaux sont asynchrones, le programme principal peut tre dans un tat trs fragile lorsque le signal est trait et donc pendant l'excution du gestionnaire de signal. C'est pourquoi vous devriez viter d'effectuer des oprations d'entres/sorties ou d'appeler la plupart des fonctions systme ou de la bibliothque C depuis un gestionnaire de signal. Un gestionnaire de signal doit effectuer le minimum ncessaire au traitement du signal, puis repasser le contrle au programme principal (ou terminer le programme). Dans la plupart des cas, cela consiste simplement enregistrer que le signal est survenu. Le programme principal vrifie alors priodiquement si un signal a t reu et ragit en consquence. Il est possible qu'un gestionnaire de signal soit interrompu par l'arrive d'un autre signal. Bien que cela puisse sembler tre un cas rare, si cela arrive, il peut tre trs difficile de diagnostiquer et rsoudre le problme (c'est un exemple de conditions de concurrence critique, trait dans le Chapitre 4, Threads , Section 4.4, Synchronisation et Sections Critiques ). C'est pourquoi vous devez tre trs attentif ce que votre programme fait dans un gestionnaire de signal. Mme l'affectation d'une valeur une variable globale peut tre dangereuse car elle peut en fait tre effectue en deux instructions machine ou plus, et un second signal peut survenir, laissant la variable dans un tat corrompu. Si vous utilisez une variable globale pour indiquer la rception d'un signal depuis un gestionnaire de signal, elle doit tre du type spcial sig_atomic_t. Linux garantit que les affectations de valeur des variables de ce type sont effectues en une seule instruction et ne peuvent donc pas tre interrompues. Sous Linux, sig_atomic_t est un int ordinaire; en fait, les affectations de valeur des types de la taille d'un int ou plus petits ou des pointeurs, sont atomiques. Nanmoins, si vous voulez crire un programme portable vers n'importe quel systme UNIX standard, utilisez le type sig_atomic_t. Le squelette de programme du Listing sigusr1, par exemple, utilise une fonction de gestion de signal pour compter le nombre de fois o le programme reoit le signal SIGUSR1, un des signaux utilisables par les applications. Utiliser un Gestionnaire de Signal sigusr1.c
#include <signal.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <unistd.h> sig_atomic_t sigusr1_count = 0; void handler (int signal_number) { ++sigusr1_count; } - 45 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utiliser un Gestionnaire de Signal sigusr1.c

int main () { struct sigaction sa; memset (&sa, 0, sizeof (sa)); sa.sa_handler = &handler; sigaction (SIGUSR1, &sa, NULL); /* Faire quelque chose de long ici. */ /* ... */ printf ("SIGUSR1 a t reu %d fois\n", sigusr1_count); return 0; }

3-4 - Fin de Processus


Dans des conditions normales, un processus peut se terminer de deux faons: soit le programme appelle la fonction exit, soit la fonction main du programme se termine. Chaque processus a un code de sortie: un nombre que le processus renvoie son parent. Le code de sortie est l'argument pass la fonction exit ou la valeur retourne depuis main. Un processus peut galement se terminer de faon anormale, en rponse un signal. Par exemple, les signaux SIGBUS, SIGSEGV et SIGFPE voqus prcdemment provoquent la fin du processus. D'autres signaux sont utiliss pour terminer un processus explicitement. Le signal SIGINT est envoy un processus lorsque l'utilisateur tente d'y mettre fin en saisissant Ctrl+C dans son terminal. Le signal SIGTERM est envoy par la commande kill. L'action par dfaut pour ces deux signaux est de mettre fin au processus. En appelant la fonction abort, un processus s'envoie lui-mme le signal SIGABRT ce qui termine le processus et produit un fichier core. Le signal de terminaison le plus puissant est SIGKILL qui met fin un processus immdiatement et ne peut pas tre bloqu ou gr par un programme. Chacun de ces signaux peut tre envoy en utilisant la commande kill en passant une option supplmentaire sur la ligne de commande; par exemple, pour terminer un programme fonctionnant mal en lui envoyant un SIGKILL, invoquez la commande suivante, o pid est un identifiant de processus:
% kill -KILL pid}

Pour envoyer un signal depuis un programme, utilisez la fonction kill. Le premier paramtre est l'identifiant du processus cible. Le second est le numro du signal; utilisez SIGTERM pour simuler le comportement par dfaut de la commande kill. Par exemple, si child_pid contient l'identifiant du processus fils, vous pouvez utiliser la fonction kill pour terminer un processus fils depuis le pre en l'appelant comme ceci: kill (child_pid, SIGTERM); Incluez les enttes <sys/types.h> et <signal.h> si vous utilisez la fonction kill. Par convention, le code de sortie est utilis pour indiquer si le programme s'est excut correctement. Un code de sortie zro indique une excution correcte, alors qu'un code diffrent de zro indique qu'une erreur est survenue. Dans ce dernier cas, la valeur renvoye peut donner une indication sur la nature de l'erreur. C'est une bonne ide d'adopter cette convention dans vos programmes car certains composants du systme GNU/Linux sont bass dessus. Par exemple, les shells supposent l'utilisation de cette convention lorsque vous connectez plusieurs programmes avec les oprateurs && (et logique) et || (ou logique). C'est pour cela que vous devriez renvoyer zro explicitement depuis votre fonction main, moins qu'une erreur ne survienne. Avec la plupart des shells, il est possible d'obtenir le code de sortie du dernier programme excut en utilisant la variable spciale $? Voici un exemple dans lequel la commande ls est invoque deux fois et son code de sortie est affich aprs chaque invocation. Dans le premier cas, ls se termine correctement et renvoie le code de sortie 0. Dans le second cas, ls rencontre une erreur (car le fichier spcifi sur la ligne de commande n'existe pas) et renvoie donc un code de sortie diffrent de 0.

- 46 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

% ls / bin coda etc lib misc nfs proc boot dev home lost+found mnt opt root % echo $? 0 % ls fichierinexistant ls: fichierinexistant: Aucun fichier ou rpertoire de ce type % echo $? 1

Notez que mme si le paramtre de la fonction exit est de type int et que la fonction main renvoie un int, Linux ne conserve pas les 32 bits du code de sortie. En fait, vous ne devriez utiliser que des codes de sortie entre 0 et 127. Les codes de sortie au dessus de 128 ont une signification spciale -- lorsqu'un processus se termine cause d'un signal, son code de sortie est zro plus le numro du signal.

3-4-1 - Attendre la Fin d'un Processus


Si vous avez saisi et excut l'exemple de fork et exec du Listing forkexec, vous pouvez avoir remarqu que la sortie du programme ls apparat souvent aprs la fin du programme principal. Cela est d au fait que le processus fils, au sein duquel s'excute ls, est ordonnanc indpendamment du processus pre. Comme Linux est un systme d'exploitation multitche, les deux processus semblent s'excuter simultanment et vous ne pas prdire si le programme ls va s'excuter avant ou aprs le processus pre. Dans certaines situations, cependant, il est souhaitable que le processus pre attende la fin d'un ou plusieurs processus fils. Pour cela, il est possible d'utiliser les appels systmes de la famille de wait. Ces fonctions vous permettent d'attendre la fin d'un processus et permettent au processus parent d'obtenir des informations sur la faon dont s'est termin son fils. Il y a quatre appels systme diffrents dans la famille de wait; vous pouvez choisir de rcuprer peu ou beaucoup d'informations sur le processus qui s'est termin et vous pouvez indiquer si vous voulez savoir quel processus fils s'est termin.

3-4-2 - Les Appels Systme wait


La fonction la plus simple s'appelle simplement wait. Elle bloque le processus appelant jusqu' ce qu'un de ses processus fils se termine (ou qu'une erreur survienne). Elle retourne un code de statut via un pointeur sur un entier, partir duquel vous pouvez extraire des informations sur la faon dont s'est termin le processus fils. Par exemple, la macro WEXITSTATUS extrait le code de sortie du processus fils. Vous pouvez utiliser la macro WIFEXITED pour dterminer si un processus s'est termin correctement partir de son code de statut (via la fonction exit ou la sortie de main) ou est mort cause d'un signal non intercept. Dans ce dernier cas, utilisez la macro WTERMSIG pour extraire le numro du signal ayant caus la mort du processus partir du code de statut. Voici une autre version de la fonction main de l'exemple de fork et exec. Cette fois, le processus parent appelle wait pour attendre que le processus fils, dans lequel s'excute la commande ls, se termine.
int main () { int child_status; /* Liste d'arguments passer la commande "ls". */ char* arg_list[] = { "ls", /* argv[0], le nom du programme. */ "-l", "/", NULL /* La liste d'arguments doit se terminer par NULL. */ }; /* Cre un nouveau processus fils excutant la commande "ls". Ignore l'identifiant du processus fils renvoy. */ spawn ("ls", arg_list); - 47 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) /* Attend la fin du processus fils. */ wait (&child_status); if (WIFEXITED (child_status)) printf ("processus fils termin normalement, le code de sortie %d\n", WEXITSTATUS (child_status)); else printf ("processus fils termin anormalement\n"); return 0;

Plusieurs appels systme similaires sont disponibles sous Linux, ils sont plus flexibles ou apportent plus d'informations sur le processus fils se terminant. La fonction waitpid peut tre utilise pour attendre la fin d'un processus fils spcifique au lieu d'attendre n'importe quel processus fils. La fonction wait3 renvoie des statistiques sur l'utilisation du processeur par le processus fils se terminant et la fonction wait4 vous permet de spcifier des options supplmentaires quant au processus dont on attend la fin.

3-4-3 - Processus Zombies


Si un processus fils se termine alors que son pre appelle la fonction wait, le processus fils disparat et son statut de terminaison est transmis son pre via l'appel wait. Mais que se passe-t-il lorsqu'un processus se termine et que son pre n'appelle pas wait? Disparat-il tout simplement? Non, car dans ce cas, les informations concernant la faon dont il s'est termin -- comme le fait qu'il se soit termin normalement ou son code de sortie -- seraient perdues. Au lieu de cela, lorsqu'un processus fils se termine, il devient un processus zombie. Un processus zombie est un processus qui s'est termin mais dont les ressources n'ont pas encore t libres. Il est de la responsabilit du processus pre de librer les ressources occupes par ses fils zombies. La fonction wait le fait, il n'est donc pas ncessaire de savoir si votre processus fils est toujours en cours d'excution avant de l'attendre. Supposons, par exemple, qu'un programme cre un processus fils, fasse un certain nombre d'autres oprations puis appelle wait. Si le processus fils n'est pas encore termin ce moment, le processus parent sera bloqu dans l'appel de wait jusqu' ce que le processus fils se termine. Si le processus fils se termine avant que le pre n'appelle wait, il devient un processus zombie. Lorsque le processus parent appelle wait, le statut de sortie du processus fils est extrait, le processus fils est supprim et l'appel de wait se termine immdiatement. Que se passe-t-il si le pre ne libre pas les ressources de ses fils? Ils restent dans le systme, sous la forme de processus zombies. Le programme du Listing zombie cre un processus fils qui se termine immdiatement, puis s'interrompt pendant une minute, sans jamais librer les ressources du processus fils. Crer un Processus Zombie zombie.c
#include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main () { pid_t child_pid; /* Cre un processus fils. */ child_pid = fork (); if (child_pid > 0) { /* Nous sommes dans le processus parent. Attente d'une minute. */ sleep (60); } else { /* Nous sommes dans le processus fils. Sortie immdiate. */ exit (0); } return 0; }

- 48 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Compilez ce fichier en un excutable appel make-zombie. Excutez-le, et pendant ce temps, listez les processus en cours d'excution sur le systme par le biais de la commande suivante dans une autre fentre:
% ps -e -o pid,ppid,stat,cmd

Elle dresse la liste des identifiants de processus, de processus pres, de leur statut et de la ligne de commande du processus. Observez qu'en plus du processus pre make-zombie, un autre processus make-zombie est affich. Il s'agit du processus fils; notez que l'identifiant de son pre est celui du processus make-zombie principal. Le processus fils est marqu comme <defunct> et son code de statut est Z pour zombie. Que se passe-t-il lorsque le programme principal de make-zombie se termine sans appeler wait? Le processus zombie est-il toujours prsent? Non -- essayez de relancer ps et notez que les deux processus make-zombie ont disparu. Lorsqu'un programme se termine, un processus spcial hrite de ses fils, le programme init, qui s'excute toujours avec un identifiant de processus valant 1 (il s'agit du premier processus lanc lorsque Linux dmarre). Le processus init libre automatiquement les ressources de tout processus zombie dont il hrite.

3-4-4 - Librer les Ressources des Fils de Faon Asynchrone


Si vous utilisez un processus fils uniquement pour appeler exec, il est suffisant d'appeler wait immdiatement dans le processus parent, ce qui le bloquera jusqu' ce que le processus fils se termine. Mais souvent, vous voudrez que le processus pre continue de s'excuter alors qu'un ou plusieurs fils s'excutent en parallle. Comment tre sr de librer les ressources occupes par tous les processus fils qui se sont termins afin de ne pas laisser de processus zombie dans la nature, ce qui consomme des ressources? Une approche envisageable serait que le processus pre appelle priodiquement wait3 et wait4 pour librer les ressources des fils zombies. Appeler wait de cette faon ne fonctionne pas de manire optimale car si aucun fils ne s'est termin, l'appelant sera bloqu jusqu' ce que ce soit le cas. Cependant, wait3 et wait4 prennent un paramtre d'option supplmentaire auquel vous pouvez passer le drapeau WNOHANG. Avec ce drapeau, la fonction s'excute en mode non bloquant -- elle librera les ressources d'un processus fils termin s'il y en a un ou se terminera s'il n'y en a pas. La valeur de retour de l'appel est l'identifiant du processus fils s'tant termin dans le premier cas, zro dans le second. Une solution plus lgante est d'indiquer au processus pre quand un processus fils se termine. Il y a plusieurs faons de le faire en utilisant les mthodes prsentes dans le Chapitre 5, Communication Interprocessus , mais heureusement, Linux le fait pour vous, en utilisant les signaux. Lorsqu'un processus fils se termine, Linux envoie au processus pre le signal SIGCHLD. L'action par dfaut pour ce signal est de ne rien faire, c'est la raison pour laquelle vous pouvez ne jamais l'avoir remarqu auparavant. Donc, une faon lgante de librer les ressources des processus fils est de grer SIGCHLD. Bien sr, lorsque vous librez les ressources du processus fils, il est important de stocker son statut de sortie si cette information est ncessaire, car une fois que les ressources du processus fils ont t libres par wait, cette information n'est plus disponible. Le Listing sigchld montre quoi ressemble un programme utilisant un gestionnaire pour SIGCHLD afin de librer les ressources de ses processus fils. Librer les Ressources des Fils via SIGCHLD sigchld.c
#include <signal.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> sig_atomic_t child_exit_status; void clean_up_child_process (int signal_number) { /* Nettoie le ou les processus fils. */ int status; while(waitpid (-1, &status, WNOHANG)); /* Stocke la statut de sortie du dernier dans une variable globale.

*/

- 49 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Librer les Ressources des Fils via SIGCHLD sigchld.c

child_exit_status = status; } int main () { /* Gre SIGCHLD en appelent clean_up_child_process. */ struct sigaction sigchld_action; memset (&sigchld_action, 0, sizeof (sigchld_action)); sigchld_action.sa_handler = &clean_up_child_process; sigaction (SIGCHLD, &sigchld_action, NULL); /* Faire diverses choses, entre autres crer un processus fils. /* ... */ return 0; }

*/

Notez comment le gestionnaire de signal stocke le statut de sortie du processus fils dans une variable globale, laquelle le programme principal peut accder. Comme la variable reoit une valeur dans un gestionnaire de signal, elle est de type sig_atomic_t.

- 50 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

4 - Threads
Les threads(NdT. Appels aussi processus lgers ., comme les processus,sont un mcanisme permettant un programme) de faire plus d'une chose la fois. Comme les processus, les threads semblent s'excuter en parallle; le noyau Linux les ordonnance de faon asynchrone, interrompant chaque thread de temps en temps pour donner aux autres une chance de s'excuter. Conceptuellement, un thread existe au sein d'un processus. Les threads sont une unit d'excution plus fine que les processus. Lorsque vous invoquez un programme, Linux cre un nouveau processus et, dans ce processus, cre un thread, qui excute le processus de faon squentielle. Ce thread peut en crer d'autres; tous ces threads excutent alors le mme programme au sein du mme processus, mais chaque thread peut excuter une partie diffrente du programme un instant donn. Nous avons vu comment un programme peut crer un processus fils. Celui-ci excute immdiatement le programme de son pre, la mmoire virtuelle, les descripteurs de fichiers, etc. de son pre tant copis. Le processus fils peut modifier sa mmoire, fermer les descripteurs de fichiers sans que cela affecte son pre, et vice versa. Lorsqu'un programme cre un nouveau thread, par contre, rien n'est copi. Le thread crateur et le thread cr partagent tous deux le mme espace mmoire, les mmes descripteurs de fichiers et autres ressources. Si un thread modifie la valeur d'une variable, par exemple, l'autre thread verra la valeur modifie. De mme, si un thread ferme un descripteur de fichier, les autres threads ne peuvent plus lire ou crire dans ce fichier. Comme un processus et tous ses threads ne peuvent excuter qu'un seul programme la fois, si un thread au sein d'un processus appelle une des fonctions exec, tous les autres threads se terminent (le nouveau programme peut, bien sr, crer de nouveaux threads). GNU/Linux implmente l'API de threading standard POSIX (appele aussi pthreads). Toutes les fonctions et types de donnes relatifs aux threads sont dclars dans le fichier d'entte <pthread.h>. Les fonctions de pthread ne font pas partie de la bibliothque standard du C. Elles se trouvent dans libpthread, vous devez donc ajouter -lpthread sur la ligne de commande lors de l'dition de liens de votre programme.

4-1 - Cration de Threads


Chaque thread d'un processus est caractris par un identifiant de thread. Lorsque vous manipulez les identifiants de threads dans des programmes C ou C++, veillez utiliser le type pthread_t. Lors de sa cration, chaque thread excute une fonction de thread. Il s'agit d'une fonction ordinaire contenant le code que doit excuter le thread. Lorsque la fonction se termine, le thread se termine galement. Sous GNU/Linux, les fonctions de thread ne prennent qu'un seul paramtre de type void* et ont un type de retour void*. Ce paramtre est l'argument de thread: GNU/Linux passe sa valeur au thread sans y toucher. Votre programme peut utiliser ce paramtre pour passer des donnes un nouveau thread. De mme, il peut utiliser la valeur de retour pour faire en sorte que le thread renvoie des donnes son crateur lorsqu'il se termine. La fonction pthread_create cre un nouveau thread. Voici les paramtres dont elle a besoin: - Un pointeur vers une variable pthread_t, dans laquelle l'identifiant du nouveau thread sera stock; - Un pointeur vers un objet d'attribut de thread. Cet objet contrle les dtails de l'interaction du thread avec le reste du programme. Si vous passez NULL comme argument de thread, le thread est cr avec les attributs par dfaut. Ceux-ci sont traits dans la Section 4.1.5, Attributs de Threads; - Un pointeur vers la fonction de thread. Il s'agit d'un pointeur de fonction ordinaire de type: void* (*) (void*); - item Une valeur d'argument de thread de type void*. Quoi que vous passiez, l'argument est simplement transmis la fonction de thread lorsque celui-ci commence s'excuter.

- 51 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Un appel pthread_create se termine immdiatement et le thread original continue excuter l'instruction suivant l'appel. Pendant ce temps, le nouveau thread dbute l'excution de la fonction de thread. Linux ordonnance les deux threads de manire asynchrone et votre programme ne doit pas faire d'hypothse sur l'ordre d'excution relatif des instructions dans les deux threads. Le programme du Listing threadcreate cre un thread qui affiche x de faon continue sur la sortie des erreurs. Aprs l'appel de pthread_create, le thread principal affiche des o indfiniment sur la sortie des erreurs. Crer un thread thread-create.c
#include <pthread.h> #include <stdio.h> /* Affiche des x sur stderr. Paramtre inutilis. Ne finit jamais. */ void* print_xs (void* unused) { while (1) fputc ('x', stderr); return NULL; } /* Le programme principal. */ int main () { pthread_t thread_id; /* Cre un nouveau thread. Le nouveau thread excutera la fonction print_xs. */ pthread_create (&thread_id, NULL, &print_xs, NULL); /* Affiche des o en continue sur stderr. */ while (1) fputc ('o', stderr); return 0; }

Compilez ce programme en utilisant la commande suivante:


% cc -o thread-create thread-create.c -lpthread

Essayez de le lancer pour voir ce qui se passe. Notez que le motif form par les x et les o est imprvisible car Linux passe la main alternativement aux deux threads. Dans des circonstances normales, un thread peut se terminer de deux faons. La premire, illustre prcdemment, est de terminer la fonction de thread. La valeur de retour de la fonction de thread est considre comme la valeur de retour du thread. Un thread peut galement se terminer explicitement en appelant pthread_exit. Cette fonction peut tre appele depuis la fonction de thread ou depuis une autre fonction appele directement ou indirectement par la fonction de thread. L'argument de pthread_exit est la valeur de retour du thread.

4-1-1 - Transmettre des Donnes un Thread


L'argument de thread est une mthode pratique pour passer des donnes un thread. Comme son type est void*, cependant, vous ne pouvez pas passer beaucoup de donnes directement en l'utilisant. Au lieu de cela, utilisez l'argument de thread pour passer un pointeur vers une structure ou un tableau de donnes. Une technique couramment utilise est de dfinir une structure de donnes pour chaque argument de thread, qui contient les paramtres attendus par la fonction de thread. En utilisant l'argument de thread, il est facile de rutiliser la mme fonction pour diffrents threads. Ils excutent alors tous les mmes traitements mais sur des donnes diffrentes. Le programme du Listing threadcreate2 est similaire l'exemple prcdent. Celui-ci cre deux nouveaux threads, l'un affiche des x et l'autre des o. Au lieu de les afficher indfiniment, cependant, chaque thread affiche un nombre prdtermin de caractres puis se termine en sortant de la fonction de thread. La mme fonction de
- 52 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

thread, char_print, est utilise par les deux threads mais chacun est configur diffremment en utilisant une struct char_print_parms. Crer Deux Threads thread-create2.c
#include <pthread.h> #include <stdio.h> /* Paramtres de la fonction print. */ struct char_print_parms { /* Caractre afficher. */ char character; /* Nombre de fois o il doit tre affich. */ int count; }; /* Affiche un certain nombre de caractres sur stderr, selon le contenu de PARAMETERS, qui est un pointeur vers une struct char_print_parms. */ void* char_print (void* parameters) { /* Effectue un transtypage du pointeur void vers le bon type. */ struct char_print_parms* p = (struct char_print_parms*) parameters; int i; for (i = 0; i < p->count; ++i) fputc (p->character, stderr); return NULL; } /* Programme principal. */ int main () { pthread_t thread1_id; pthread_t thread2_id; struct char_print_parms thread1_args; struct char_print_parms thread2_args; /* Cre un nouveau thread affichant 30 000 x. */ thread1_args.character = 'x'; thread1_args.count = 30000; pthread_create (&thread1_id, NULL, &char_print, &thread1_args); /* Cre un nouveau thread affichant 20 000 'o'. */ thread2_args.character = ?o?; thread2_args.count = 20000; pthread_create (&thread2_id, NULL, &char_print, &thread2_args); return 0; }

Mais attendez! Le programme du Listing threadcreate2 est srieusement bogu. Le thread principal (qui excute la fonction main) cre les structures passes en paramtre aux threads (thread1_args et thread2_args) comme des variables locales puis transmet des pointeurs sur ces structures aux threads qu'il cre. Qu'est-ce qui empche Linux d'ordonnancer les trois threads de faon ce que main termine son excution avant que l'un des deux autres threads s'excutent? Rien! Mais si cela se produit, la mmoire contenant les structures de paramtres des threads sera libre alors que les deux autres threads tentent d'y accder.

4-1-2 - Synchroniser des Threads


Une solution possible est de forcer main attendre la fin des deux autres threads. Ce dont nous avons besoin est une fonction similaire wait qui attende la fin d'un thread au lieu de celle d'un processus. Cette fonction est pthread_join, qui prend deux arguments: l'identifiant du thread attendre et un pointeur vers une variable void* qui recevra la valeur de retour du thread s'tant termin. Si la valeur de retour du thread ne vous est pas utile, passez NULL comme second argument. Le Listing threadcreate2a montre la fonction main sans le bogue du Listing threadcreate2. Dans cette version, main ne se termine pas jusqu' ce que les deux threads affichant des o et des x se soient eux-mme termins, afin qu'ils n'utilisent plus les structures contenant les arguments.

- 53 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Fonction main de thread-create2.c corrige thread-create2a.c


int main () { pthread_t thread1_id; pthread_t thread2_id; struct char_print_parms thread1_args; struct char_print_parms thread2_args; /* Cre un nouveau thread affichant 30 000 x. */ thread1_args.character = ?x?; thread1_args.count = 30000; pthread_create (&thread1_id, NULL, &char_print, &thread1_args); /* Cre un nouveau thread affichant 20 000 o. */ thread2_args.character = ?o?; thread2_args.count = 20000; pthread_create (&thread2_id, NULL, &char_print, &thread2_args); /* S'assure que le premier thread est termin. */ pthread_join (thread1_id, NULL); /* S'assure que le second thread est termin. */ pthread_join (thread2_id, NULL); /* Nous pouvons maintenant quitter en toute scurit. */ return 0; }

Morale de l'histoire: assurez vous que toute donne que vous passez un thread par rfrence n'est pas libre, mme par un thread diffrent, moins que vous ne soyez sr que le thread a fini de l'utiliser. Cela s'applique aux variables locales, qui sont libres lorsqu'elles sont hors de porte, ainsi qu'aux variables alloues dans le tas, que vous librez en appelant free (ou en utilisant delete en C++).

4-1-3 - Valeurs de Retour des Threads


Si le second argument que vous passez pthread_join n'est pas NULL, la valeur de retour du thread sera stocke l'emplacement point par cet argument. La valeur de retour du thread, comme l'argument de thread, est de type void*. Si vous voulez renvoyer un simple int ou un autre petit nombre, vous pouvez le faire facilement est convertissant la valeur en void* puis en le reconvertissant vers le type adquat aprs l'appel de pthread_join(Notez que cette faon de faire n'est pas portable et qu'il est de votre responsabilit de vous assurer que la valeur peut tre convertie en toute scurit vers et depuis void* sans perte de bit.). Le programme du Listing primes calcule le n<sup>ime</sup> nombre premier dans un thread distinct. Ce thread renvoie le numro du nombre premier demand via sa valeur de retour de thread. Le thread principal, pendant ce temps, est libre d'excuter d'autres traitements. Notez que l'algorithme de divisions successives utilis dans compute_prime est quelque peu inefficace; consultez un livre sur les algorithmes numriques si vous avez besoin de calculer beaucoup de nombres premiers dans vos programmes. Calcule des Nombres Premiers dans un Thread primes.c
#include <pthread.h> #include <stdio.h> /* Calcules des nombres premiers successifs (trs inefficace). Renvoie le Nime nombre premier o N est la valeur pointe par *ARG. */ void* compute_prime (void* arg) { int candidate = 2; int n = *((int*) arg); while (1) { int factor; int is_prime = 1; /* Teste si le nombre est premier par divisions successives. */ for (factor = 2; factor < candidate; ++factor) if (candidate % factor == 0) { is_prime = 0; break; } /* Est-ce le nombre premier que nous cherchons ? */

- 54 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Calcule des Nombres Premiers dans un Thread primes.c

} int main () { pthread_t thread; int which_prime = 5000; int prime; /* Dmarre le thread de calcul jusqu'au 5 000me nombre premier. */ pthread_create (&thread, NULL, &compute_prime, &which_prime); /* Faire autre chose ici... */ /* Attend la fin du thread de calcul et rcupre le rsultat. */ pthread_join (thread, (void*) &prime); /* Affiche le nombre premier calcul. */ printf("Le %dme nombre premier est %d.\n", which_prime, prime); return 0; }

} return NULL;

if (is_prime) { if (--n == 0) /* Renvoie le nombre premier dsir via la valeur de retour du thread. */ return (void*) candidate; } ++candidate;

4-1-4 - Plus d'Informations sur les Identifiants de Thread


De temps autre, il peut tre utile pour une portion de code de savoir quel thread l'excute. La fonction pthread_self renvoie l'identifiant du thread depuis lequel elle a t appele. Cet identifiant de thread peut tre compar un autre en utilisant la fonction pthread_equal. Ces fonctions peuvent tre utiles pour dterminer si un identifiant de thread particulier correspond au thread courant. Par exemple, un thread ne doit jamais appeler pthread_join pour s'attendre lui-mme (dans ce cas, pthread_join renverrait le code d'erreur EDEADLK). Pour le vrifier avant l'appel, utilisez ce type de code :
if (!pthread_equal (pthread_self (), other_thread)) pthread_join (other_thread, NULL);

4-1-5 - Attributs de Thread


Les attributs de thread proposent un mcanisme pour contrler finement le comportement de threads individuels. Souvenez-vous que pthread_create accepte un argument qui est un pointeur vers un objet d'attributs de thread. Si vous passez un pointeur nul, les attributs par dfaut sont utiliss pour configurer le nouveau thread. Cependant, vous pouvez crer et personnaliser un objet d'attributs de threads pour spcifier vos propres valeurs. Pour indiquer des attributs de thread personnaliss, vous devez suivre ces tapes : - Crez un objet pthread_attr_t. La faon la plus simple de le faire est de dclarer une variable automatique de ce type. - Appelez pthread_attr_init en lui passant un pointeur vers cet objet. Cela initialise les attributs leur valeur par dfaut. - Modifiez l'objet d'attributs pour qu'ils contiennent les valeurs dsires. - Passez un pointeur vers l'objet cr lors de l'appel pthread_create. - Appelez pthread_attr_destroy pour librer l'objet d'attributs. La variable pthread_attr_t n'est pas libre en ellemme; elle peut tre rinitialise avec pthread_attr_init.

- 55 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Un mme objet d'attributs de thread peut tre utilis pour dmarrer plusieurs threads. Il n'est pas ncessaire de conserver l'objet d'attributs de thread une fois qu'ils ont t crs. Pour la plupart des applications GNU/Linux, un seul attribut de thread a gnralement de l'intrt (les autres attributs disponibles sont principalement destins la programmation en temps rel). Cet attribut est l'tat de dtachement (detach state) du thread. Un thread peut tre un thread joignable (par dfaut) ou un thread dtach. Les ressources d'un thread joignable, comme pour un processus, ne sont pas automatiquement libres par GNU/Linux lorsqu'il se termine. Au lieu de cela, l'tat de sortie du thread reste dans le systme (un peu comme pour un processus zombie) jusqu' ce qu'un autre thread appelle pthread_join pour rcuprer cette valeur de retour. Alors seulement, les ressources sont libres. Les ressources d'un thread dtach, au contraire, sont automatiquement libres lorsqu'il se termine. Cela impliquer qu'un autre thread ne peut attendre qu'il se termine avec pthread_join ou obtenir sa valeur de retour. Pour paramtrer l'tat de dtachement dans un objet d'attributs de thread, utilisez pthread_attr_setdetachstate. Le premier argument est un pointeur vers l'objet d'attributs de thread et le second est l'tat dsir. Comme l'tat joignable est la valeur par dfaut, il n'est ncessaire d'effectuer cet appel que pour crer des threads dtachs; passez PTHREAD_CREATE_DETACHED comme second argument. Le code du Listing detached cre un thread dtach en activant l'attribut de dtachement de thread: Programme Squelette Crant un Thread Dtach detached.c
#include <pthread.h> void* thread_function (void* thread_arg) { /* Effectuer les traitements ici... */ } int main () { pthread_attr_t attr; pthread_t thread; pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); pthread_create (&thread, &attr, &thread_function, NULL); pthread_attr_destroy (&attr); /* Effectuer les traitements ici... */ /* Pas besoin d'attendre le deuxime thread. */ return 0; }

Mme si un thread est cr dans un tat joignable, il peut tre transform plus tard en un thread dtach. Pour cela, appelez pthread_detach. Une fois qu'un thread est dtach, il ne peut plus tre rendu joignable.

4-2 - Annulation de Thread


Dans des circonstances normales, un thread se termine soit en arrivant la fin de la fonction de thread, soit en appelant la fonction pthread_exit. Cependant, il est possible pour un thread de demander la fin d'un autre thread. Cela s'appelle annuler un thread. Pour annuler un thread, appelez pthread_cancel, en lui passant l'identifiant du thread annuler. Un thread annul peut tre joint ultrieurement; en fait, vous devriez joindre un thread annul pour librer ses ressources, moins que le thread ne soit dtach (consultez la Section 4.1.5, Attributs de Thread ). La valeur de retour d'un thread annul est la valeur spciale PTHREAD_CANCELED. Souvent un thread peut tre en train d'excuter un code qui doit tre excut totalement ou pas du tout. Par exemple, il peut allouer des ressources, les utiliser, puis les librer. Si le thread est annul au milieu de ce code, il peut ne pas avoir l'opportunit de librer les ressources et elles seront perdues. Pour viter ce cas de figure, un thread peut contrler si et quand il peut tre annul.

- 56 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Un thread peut se comporter de trois faons face l'annulation. Il peut tre annulable de faon asynchrone. C'est--dire qu'il peut tre annul n'importe quel moment de son excution. Il peut tre annulable de faon synchrone. Le thread peut tre annul, mais pas n'importe quand pendant son excution. Au lieu de cela, les requtes d'annulation sont mises en file d'attente et le thread n'est annul que lorsqu'il atteint un certain point de son excution. Il peut tre impossible annuler. Les tentatives d'annulation sont ignores silencieusement.

Lors de sa cration, un thread est annulable de faon synchrone.

4-2-1 - Threads Synchrones et Asynchrones


Un thread annulable de faon asynchrone peut tre annul n'importe quel point de son excution. Un thread annulable de faon synchrone, par contre, ne peut tre annul qu' des endroits prcis de son excution. Ces endroits sont appels points d'annulation. Le thread mettra les requtes d'annulation en attente jusqu' ce qu'il atteigne le point d'annulation suivant. Pour rendre un thread annulable de faon asynchrone, utilisez pthread_setcanceltype. Cela n'affecte que le thread effectuant l'appel. Le premier argument doit tre PTHREAD_CANCEL_ASYNCHRONOUS pour rendre le thread annulable de faon asynchrone, ou PTHREAD_CANCEL_DEFERRED pour repasser immdiatement en tat d'annulation synchrone. Le second argument, s'il n'est pas NULL, est un pointeur vers une variable qui recevra le type d'annulation prcdemment supporte par le thread. Cet appel, par exemple, rend le thread appelant annulable de faon asynchrone.
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

A quoi ressemble un point d'annulation et o doit-il tre plac? La faon la plus directe de crer un point d'annulation est d'appeler pthread_testcancel. Cette fonction ne fait rien part traiter une annulation en attente dans un thread annulable de faon synchrone. Vous devriez appeler pthread_testcancel priodiquement durant des calculs longs au sein d'une fonction de thread, aux endroits o le thread peut tre annul sans perte de ressources ou autres effets de bord. Un certain nombre d'autres fonctions sont galement des points d'annulation implicites. Elles sont listes sur la page de manuel de pthread_cancel. Notez que d'autres fonctions peuvent y faire appel en interne et donc constituer des points d'annulation implicites.

4-2-2 - Sections Critiques Non-Annulables


Un thread peut dsactiver son annulation avec la fonction pthread_setcancelstate. Comme pthread_setcanceltype, elle affecte le thread appelant. Le premier argument est PTHREAD_CANCEL_DISABLE pour dsactiver l'annulation, ou PTHREAD_CANCEL_ENABLE pour ractiver l'annulation. Le second argument, s'il n'est pas NULL, pointe vers une variable qui recevra l'tat prcdent de l'annulation. Cet appel, par exemple, dsactive l'annulation du thread appelant.
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);

L'utilisation de pthread_setcancelstate vous permet d'implmenter des sections critiques. Une section critique est une squence de code qui doit tre excute entirement ou pas du tout; en d'autres termes, si un thread commence l'excution d'une section critique, il doit atteindre la fin de la section critique sans tre annul. Par exemple, supposons que vous criviez une routine pour un programme bancaire qui transfre de l'argent d'un compte un autre. Pour cela, vous devez ajouter la valeur au solde d'un compte et la soustraire du solde de
- 57 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

l'autre. Si le thread excutant votre routine est annul entre les deux oprations, le programme aura augment de faon incorrecte l'argent dtenu par la banque en ne terminant pas la transaction. Pour viter cela, placez les deux oprations au sein d'une section critique. Vous pourriez implmenter le transfert avec une fonction comme process_transaction prsente dans le Listing criticalsection. Cette fonction dsactive l'annulation de thread pour dmarrer une section critique avant toute modification de solde. Transaction avec Section Critique critical-section.c
#include <pthread.h> #include <stdio.h> #include <string.h> /* Tableau des soldes de comptes, index par numro de compte. */ float* account_balances; /* Transfre DOLLARS depuis le compte FROM_ACCT vers TO_ACCT. Renvoie 0 si la transaction s'est bien droule ou 1 si le solde de FROM_ACCT est trop faible. */ int process_transaction (int from_acct, int to_acct, float dollars) { int old_cancel_state; /* Vrifie le solde de FROM_ACCT. */ if (account_balances[from_acct] < dollars) return 1; /* Dbut de la section critique. */ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cancel_state); /* Transfre l'argent. */ account_balances[to_acct] += dollars; account_balances[from_acct] -= dollars; /* Fin de la section critique. */ pthread_setcancelstate (old_cancel_state, NULL); return 0; }

Notez qu'il est important de restaurer l'ancien tat de l'annulation la fin de la section critique plutt que de le positionner systmatiquement PTHREAD_CANCEL_ENABLE. Cela vous permet d'appeler la fonction process_transaction en toute scurit depuis une autre section critique ? dans ce cas, votre fonction remettrait l'tat de l'annulation la mme valeur que lorsqu'elle a dbut.

4-2-3 - Quand Utiliser l'Annulation de Thread?


En gnral, ce n'est pas une bonne ide d'utiliser l'annulation de thread pour en terminer l'excution, except dans des circonstances anormales. En temps normal, il est mieux d'indiquer au thread qu'il doit se terminer, puis d'attendre qu'il se termine de lui-mme. Nous reparlerons des techniques de communication avec les threads plus loin dans ce chapitre et dans le Chapitre 5, Communication Interprocessus .

4-3 - Donnes Propres un Thread


Contrairement aux processus, tous les threads d'un mme programme partagent le mme espace d'adressage. Cela signifie que si un thread modifie une valeur en mmoire (par exemple, une variable globale), cette modification est visible dans tous les autres threads. Cela permet plusieurs threads de manipuler les mmes donnes sans utiliser les mcanismes de communication interprocessus (dcrits au Chapitre 5). Nanmoins, chaque thread a sa propre pile d'appel. Cela permet chacun d'excuter un code diffrent et d'utiliser des sous-routines la faon classique. Comme dans un programme monothread, chaque invocation de sous-routine dans chaque thread a son propre jeu de variables locales, qui sont stockes dans la pile de ce thread. Cependant, il peut parfois tre souhaitable de dupliquer certaines variables afin que chaque thread dispose de sa propre copie. GNU/Linux supporte cette fonctionnalit avec une zone de donnes propres au thread. Les variables stockes dans cette zone sont dupliques pour chaque thread et chacun peut modifier sa copie sans affecter les
- 58 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

autres. Comme tous les threads partagent le mme espace mmoire, il n'est pas possible d'accder aux donnes propres un thread via des rfrences classiques. GNU/Linux fournit des fonction ddies pour placer des valeurs dans la zone de donnes propres au thread et les rcuprer. Vous pouvez crer autant d'objets de donnes propres au thread que vous le dsirez, chacune tant de type void*. Chaque objet est rfrenc par une cl. Pour crer une nouvelle cl, et donc un nouvel objet de donnes pour chaque thread, utilisez pthread_key_create. Le premier argument est un pointeur vers une variable pthread_key_t. Cette cl peut tre utilise par chaque thread pour accder sa propre copie de l'objet de donnes correspondant. Le second argument de pthread_key_create est une fonction de libration des ressources. Si vous passez un pointeur sur une fonction, GNU/Linux appelle celle-ci automatiquement lorsque chaque thread se termine en lui passant la valeur de la donne propre au thread correspondant la cl. Ce mcanisme est trs pratique car la fonction de libration des ressources est appele mme si le thread est annul un moment quelconque de son excution. Si la valeur propre au thread est nulle, la fonction de libration de ressources n'est pas appele. Si vous n'avez pas besoin de fonction de libration de ressources, vous pouvez passer NULL au lieu d'un pointeur de fonction. Une fois que vous avez cr une cl, chaque thread peut positionner la valeur correspondant cette cl en appelant pthread_setspecific. Le premier argument est la cl, et le second est la valeur propre au thread stocker sous forme de void*. Pour obtenir un objet de donnes propre au thread, appelez pthread_getspecific, en lui passant la cl comme argument. Supposons, par exemple, que votre application partage une tche entre plusieurs threads. des fins d'audit, chaque thread doit avoir un fichier journal spar dans lequel des messages d'avancement pour ce thread sont enregistrs. La zone de donnes propres au thread est un endroit pratique pour stocker le pointeur sur le fichier journal dans chaque thread. Le Listing tsd prsente une implmentation d'un tel mcanisme. La fonction main de cet exemple cre une cl pour stocker le pointeur vers le fichier propre chaque thread puis la stocke au sein de la variable globale thread_lock_key. Comme il s'agit d'une variable globale, elle est partage par tous les threads. Lorsqu'un thread commence excuter sa fonction, il ouvre un fichier journal et stocke le pointeur de fichier sous cette cl. Plus tard, n'importe lequel de ces thread peut appeler write_to_thread_log pour crire un message dans le fichier journal propre au thread. Cette fonction obtient le pointeur de fichier du fichier log du thread depuis les donnes propres au thread et y crit le message. Fichiers Logs par Thread avec Donnes Propres au Thread tsd.c
#include <malloc.h> #include <pthread.h> #include <stdio.h> /* Cl utilise pour associer un pointeur de fichier chaque thread. */ static pthread_key_t thread_log_key; /* crit MESSAGE vers le fichier journal du thread courant. */ void write_to_thread_log (const char* message) { FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key); fprintf (thread_log, "%s\n", message); } /* Ferme le pointeur vers le fichier journal THREAD_LOG. */ void close_thread_log (void* thread_log){ fclose ((FILE*) thread_log); } void* thread_function (void* args) { char thread_log_filename[20]; FILE* thread_log; /* Gnre le nom de fichier pour le fichier journal de ce thread. */ sprintf (thread_log_filename, "thread%d.log", (int) pthread_self ()); /* Ouvre le fichier journal. */ thread_log = fopen (thread_log_filename, "w"); /* Stocke le pointeur de fichier dans les donnes propres au thread sous la cl thread_log_key. */ pthread_setspecific (thread_log_key, thread_log); write_to_thread_log ("Dmarrage du Thread."); /* Placer les traitements ici... */ - 59 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Fichiers Logs par Thread avec Donnes Propres au Thread tsd.c

return NULL; } int main () { int i; pthread_t threads[5]; /* Cre une cl pour associer les pointeurs de fichier journal dans les donnes propres au thread. Utilise close_thread_log pour librer les pointeurs de fichiers. */ pthread_key_create (&thread_log_key, close_thread_log); /* Cre des threads pour effectuer les traitements. */ for (i = 0; i < 5; ++i) pthread_create (&(threads[i]), NULL, thread_function, NULL); /* Attend la fin de tous les threads. */ for (i = 0; i < 5; ++i) pthread_join (threads[i], NULL); return 0; }

Remarquez que thread_function n'a pas besoin de fermer le fichier journal. En effet, lorsque la cl du fichier a t cre, close_thread_log a t spcifie comme fonction de libration de ressources pour cette cl. Lorsqu'un thread se termine, GNU/Linux appelle cette fonction, en lui passant la valeur propre au thread correspondant la cl du fichier journal. Cette fonction prend soin de fermer le fichier journal. Gestionnaires de Libration de Ressources Les fonctions de libration de ressources associes aux cls des donnes spcifiques au thread peuvent tre trs pratiques pour s'assurer que les ressources ne sont pas perdues lorsqu'un thread se termine ou est annul. Quelquefois, cependant, il est utile de pouvoir spcifier des fonctions de libration de ressources sans avoir crer un nouvel objet de donnes propre au thread qui sera dupliqu pour chaque thread. Pour ce faire, GNU/Linux propose des gestionnaires de libration de ressources. Un gestionnaire de libration de ressources est tout simplement une fonction qui doit tre appele lorsqu'un thread se termine. Le gestionnaire prend un seul paramtre void* et la valeur de l'argument est spcifie lorsque le gestionnaire est enregistr ? cela facilite l'utilisation du mme gestionnaire pour librer plusieurs instances de ressources. Un gestionnaire de libration de ressources est une mesure temporaire, utilis pour librer une ressource uniquement si le thread se termine ou est annul au lieu de terminer l'excution d'une certaine portion de code. Dans des circonstances normales, lorsqu'un thread ne se termine pas et n'est pas annul, la ressource doit tre libre explicitement et le gestionnaire de ressources supprim. Pour enregistrer un gestionnaire de libration de ressources, appelez pthread_cleanup_push, en lui passant un pointeur vers la fonction de libration de ressources et la valeur de son argument void*. L'appel pthread_cleanup_push doit tre contrebalanc avec un appel pthread_cleanup_pop qui supprime le gestionnaire de libration de ressources. Dans une optique de simplification, pthread_cleanup_pop prend un indicateur int en argument; s'il est diffrent de zro, l'action de libration de ressources est excut lors de la suppression. L'extrait de programme du Listing cleanup montre comment vous devriez utiliser un gestionnaire de libration de ressources pour vous assurer qu'un tampon allou dynamiquement est libr si le thread se termine. Extrait de Programme Montrant l'Utilisation d'un Gestionnaire de Libration de Ressources cleanup.c
#include <malloc.h> #include <pthread.h> /* Alloue un tampon temporaire. */ void* allocate_buffer (size_t size) { return malloc (size); } /* Libre un tampon temporaire. */ void deallocate_buffer (void* buffer) - 60 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Extrait de Programme Montrant l'Utilisation d'un Gestionnaire de Libration de Ressources cleanup.c


{ free (buffer); } void do_some_work () { /* Alloue un tampon temporaire. */ void* temp_buffer = allocate_buffer (1024); /* Enregistre un gestionnaire de libration de ressources pour ce tampon pour le librer si le thread se termine ou est annul. */ pthread_cleanup_push (deallocate_buffer, temp_buffer); /* Placer ici des traitements qui pourraient appeler pthread_exit ou tre annuls... */ /* Supprime le gestionnaire de libration de ressources. Comme nous passons une valeur diffrente de zro, la libration est effectue par l'appel de deallocate_buffer. */ pthread_cleanup_pop (1); }

Comme l'argument de pthread_cleanup_pop est diffrent de zro dans ce cas, la fonction de libration de ressources deallocate_buffer est appele automatiquement et il n'est pas ncessaire de le faire explicitement. Dans ce cas simple, nous aurions pu utiliser la fonction de la bibliothque standard free comme fonction de libration de ressources au lieu de deallocate_buffer.

4-3-1 - Libration de Ressources de Thread en C++


Les programmeurs C++ sont habitus avoir des fonctions de libration de ressources gratuitement en plaant les actions adquates au sein de destructeurs. Lorsque les objets sont hors de porte, soit cause de la fin d'un bloc, soit parce qu'une exception est lance, le C++ s'assure que les destructeurs soient appels pour les variables automatiques qui en ont un. Cela offre un mcanisme pratique pour s'assurer que le code de libration de ressources est appel quelle que soit la faon dont le bloc se termine. Si un thread appelle pthread_exit, cependant, le C++ ne garantit pas que les destructeurs soient appels pour chacune des variables automatiques situes sur la pile du thread. Une faon intelligente d'obtenir le mme comportement est d'invoquer pthread_exit au niveau de la fonction de thread en lanant une exception spciale. Le programme du Listing cxxexit dmontre cela. Avec cette technique, une fonction indique son envie de quitter le thread en lanant une ThreadExitException au lieu d'appeler pthread_exit directement. Comme l'exception est intercepte au niveau de la fonction de thread, toutes les variables locales situes sur la pile du thread seront dtruites proprement lorsque l'exception est remonte. Implmenter une Sortie de Thread en C++ cxx-exit.cpp
#include <pthread.h> class ThreadExitException { public: /* Cre une exception signalant la fin d'un thread avec RETURN_VALUE. */ ThreadExitException (void* return_value) : thread_return_value_ (return_value) { } /* Quitte le thread en utilisant la valeur de retour fournie dans le constructeur. */ void* DoThreadExit () { pthread_exit (thread_return_value_); } private: /* Valeur de retour utilise lors de la sortie du thread. */ void* thread_return_value_; }; void do_some_work () { while (1) { - 61 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Implmenter une Sortie de Thread en C++ cxx-exit.cpp

} } void* thread_function (void*) { try { do_some_work (); } catch (ThreadExitException ex) { /* Une fonction a signal que l'on devait quitter le thread. */ ex.DoThreadExit (); } return NULL; }

/* Placer le code utile ici... */ if (should_exit_thread_immediately ()) throw ThreadExitException (/* thread?s return value = */ NULL);

4-4 - Synchronisation et Sections Critiques


Programmer en utilisant les threads demande beaucoup de rigueur car la plupart des programmes utilisant les threads sont des programmes concurrents. En particulier, il n'y a aucun moyen de connatre la faon dont seront ordonnancs les threads les uns par rapport aux autres. Un thread peut s'excuter pendant longtemps ou le systme peut basculer d'un thread un autre trs rapidement. Sur un systme avec plusieurs processeurs, le systme peut mme ordonnancer plusieurs threads afin qu'ils s'excutent physiquement en mme temps. Dboguer un programme qui utilise les threads est compliqu car vous ne pouvez pas toujours reproduire facilement le comportement ayant caus le problme. Vous pouvez lancer le programme une premire fois sans qu'aucun problme ne survienne et la fois suivante, il plante. Il n'y a aucun moyen de faire en sorte que le systme ordonnance les threads de la mme faon d'une fois sur l'autre. La cause la plus vicieuse de plantage des programmes utilisant les threads est lorsque ceux-ci tentent d'accder aux mmes donnes. Comme nous l'avons dit prcdemment, il s'agit d'un des aspects les plus puissants des threads mais cela peut galement tre dangereux. Si un thread n'a pas termin la mise jour d'une structure de donnes lorsqu'un autre thread y accde, il s'en suit une situation imprvisible. Souvent, les programmes bogus qui utilisent les threads contiennent une portion de code qui ne fonctionne que si un thread a la main plus souvent ? ou plus tt ? qu'un autre. Ces bogues sont appels conditions de concurrence critique; les threads sont en concurrence pour modifier la mme structure de donnes.

4-4-1 - Conditions de Concurrence Critique


Supposons que votre programme ait une srie de tches en attente traites par plusieurs threads concurrents. La file d'attente des tches est reprsente par une liste chane d'objets struct job. Aprs que chaque thread a fini une opration, il vrifie la file pour voir si une nouvelle tche est disponible. Si job_queue n'est pas NULL, le thread supprime la tte de la liste chane et fait pointer job_queue vers la prochaine tche de la liste. La fonction de thread qui traite les tches de la liste pourrait ressembler au Listing jobqueue1. Fonction de Thread Traitant une File de Tches job-queue1.c
#include <malloc.h> struct job { /* Champ de chanage. */ struct job* next; /* Autres champs dcrivant la tche... */ }; /* Liste chane de tches en attente. */ struct job* job_queue; - 62 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Fonction de Thread Traitant une File de Tches job-queue1.c

/* Traite les tches jusqu' ce que la file soit vide. */ void* thread_function (void* arg) { while (job_queue != NULL) { /* Rcupre la tche suivante. */ struct job* next_job = job_queue; /* Supprime cette tche de la liste. */ job_queue = job_queue->next; /* Traite la tche. */ process_job (next_job); /* Libration des ressources. */ free (next_job); } return NULL; }

Supposons maintenant que deux threads finissent une tche peu prs au mme moment, mais qu'il ne reste qu'une seule tche dans la liste. Le premier thread regarde si job_queue est nul; comme ce n'est pas le cas, le thread entre dans la boucle et stocke le pointeur vers la tche dans next_job. ce moment, Linux interrompt le thread et passe la main au second. Le second thread vrifie galement job_queue, comme elle n'est pas NULL, affecte la mme valeur que le premier next_job. Par une malheureuse concidence, nous avons deux threads excutant la mme tche. Pire, un des thread va positionner job_queue NULL pour supprimer l'objet de la liste. Lorsque l'autre valuera job_queue->next, il en rsultera une erreur de segmentation. C'est un exemple de condition de concurrence critique. En d' heureuses circonstances, cet ordonnancement particulier des threads ne surviendra jamais et la condition de concurrence critique ne sera jamais rvle. Dans des circonstances diffrentes, par exemple dans le cas d'un systme trs charg (ou sur le nouveau serveur multiprocesseur d'un client important!) le bogue peut apparatre. Pour liminer les conditions de concurrence critique, vous devez trouver un moyen de rendre les oprations atomiques. Une opration atomique est indivisible et impossible interrompre; une fois qu'elle a dbut, elle ne sera pas suspendue ou interrompue avant d'tre termine, et aucune autre opration ne sera accomplie pendant ce temps. Dans notre exemple, nous voulons vrifier la valeur de job_queue et si elle n'est pas NULL, supprimer la premire tche, tout cela en une seule opration atomique.

4-4-2 - Mutexes
La solution pour notre problme de concurrence critique au niveau de la file de tches est de n'autoriser qu'un seul thread accder la file. Une fois que le thread commence observer la file d'attente, aucun autre thread ne doit pouvoir y accder jusqu' ce que le premier ait dcid s'il doit traiter une tche, et si c'est le cas, avant qu'il n'ait supprim la tche de la liste. L'implmentation de ce mcanisme requiert l'aide du systme d'exploitation. GNU/Linux propose des mutexes, raccourcis de MUTual EXclusion locks (verrous d'exclusion mutuelle). Un mutex est un verrouillage spcial qu'un seul thread peut utiliser la fois. Si un thread verrouille un mutex puis qu'un second tente de verrouiller le mme mutex, ce dernier est bloqu ou suspendu. Le second thread est dbloqu uniquement lorsque le premier dverrouille le mutex ? ce qui permet la reprise de l'excution. GNU/Linux assure qu'il n'y aura pas de condition de concurrence critique entre deux threads tentant de verrouiller un mutex; seul un thread obtiendra le verrouillage et tous les autres seront bloqus. On peut faire l'analogie entre un mutex et une porte de toilettes. Lorsque quelqu'un entre dans les toilettes et verrouille la porte, si une personne veut entrer dans les toilettes alors qu'ils sont occups, elle sera oblige d'attendre dehors jusqu' ce que l'occupant sorte. Pour crer un mutex, crez une variable pthread_mutex_t et passez pthread_mutex_init un pointeur sur cette variable. Le second argument de pthread_mutex_init est un pointeur vers un objet d'attributs de mutex. Comme pour pthread_create, si le pointeur est nul, ce sont les valeurs par dfaut des attributs qui sont utilises. La variable mutex ne doit tre initialise qu'une seule fois. Cet extrait de code illustre la dclaration et l'initialisation d'une variable mutex:

- 63 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

pthread_mutex_t mutex; pthread_mutex_init (&mutex, NULL);

Une faon plus simple de crer un mutex avec les attributs par dfaut est de l'initialiser avec la valeur spciale PTHREAD_MUTEX_INITIALIZER. Aucun appel pthread_mutex_init n'est alors ncessaire. Cette mthode est particulirement pratique pour les variables globales (et les membres static en C++). L'extrait de code prcdent pourrait tre crit comme suit:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Un thread peut essayer de verrouiller un mutex en appelant pthread_mutex_lock dessus. Si le mutex tait dverrouill, il devient verrouill et la fonction se termine immdiatement. Si le mutex tait verrouill par un autre thread, pthread_mutex_lock bloque l'excution et ne se termine que lorsque le mutex est dverrouill par l'autre thread. Lorsque le mutex est dverrouill, seul un des threads suspendus (choisi alatoirement) est dbloqu et autoris accder au mutex; les autres threads restent bloqus. Un appel pthread_mutex_unlock dverrouille un mutex. Cette fonction doit toujours tre appele par le thread qui a verrouill le mutex. Listing jobqueue montre une autre version de la file de tches. Dsormais, celle-ci est protge par un mutex. Avant d'accder la file (que ce soit pour une lecture ou une criture), chaque thread commence par verrouiller le mutex. Ce n'est que lorsque la squence de vrification de la file et de suppression de la tche est termine que le mutex est dbloqu. Cela vite l'apparition des conditions de concurrence ciritique cites prcdemment. Fonction de Thread de File de Tches, Protge par un Mutex job-queue.c
#include <malloc.h> #include <pthread.h> struct job { /* Pointeur vers la tche suivante. */ struct job* next; }; /* Liste chane des tches en attente. */ struct job* job_queue; /* Mutex protgeant la file de tches. */ pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER; /* Traite les tches en attente jusqu' ce que la file soit vide. */ void* thread_function (void* arg) { while (1) { struct job* next_job; /* Verrouille le mutex de la file de tches. */ pthread_mutex_lock (&job_queue_mutex); /* Il est maintenant sans danger de vrifier si la file est vide. */ if (job_queue == NULL) next_job = NULL; else { /* Rcupre la tche suivante. */ next_job = job_queue; /* Supprime cette tche de la liste. */ job_queue = job_queue->next; } /* Dverrouille le mutex de la file de tches car nous en avons fini avec la file pour l'instant. */ pthread_mutex_unlock (&job_queue_mutex); /* La file tait vide ? Si c'est la cas, le thread se termine. */ if (next_job == NULL) break; /* Traite la tche. */ process_job (next_job); /* Libration des ressources. */ free (next_job); } return NULL; }

- 64 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Tous les accs job_queue, le pointeur de donnes partag, ont lieu entre l'appel pthread_mutex_lock et celui pthread_mutex_unlock. L'accs un objet dcrivant une tche, stock dans next_job, a lieu en dehors de cette zone uniquement une fois que l'objet a t supprim de la liste et a donc t rendu inaccessible aux autres threads. Notez que si la file est vide (c'est--dire que job_queue est NULL), nous ne quittons pas la boucle immdiatement car dans ce cas le mutex resterait verrouill empchant l'accs la file de tche par un quelconque autre thread. Au lieu de cela, nous notons que c'est le cas en mettant next_job NULL et en ne quittant la boucle qu'aprs avoir dverrouill le mutex. L'utilisation de mutex pour verrouiller job_queue n'est pas automatique; c'est vous d'ajouter le code pour verrouiller le mutex avant d'accder la variable et le dverrouiller ensuite. Par exemple, une fonction d'ajout de tche la file pourrait ressembler ce qui suit:
void enqueue_job (struct job* new_job) { pthread_mutex_lock (&job_queue_mutex); new_job->next = job_queue; job_queue = new_job; pthread_mutex_unlock (&job_queue_mutex); }

4-4-3 - Interblocage de Mutexes


Les mutexes offrent un mcanisme permettant un thread de bloquer l'excution d'un autre. Cela ouvre la possibilit l'apparition d'une nouvelle classe de bogues appels interblocages (deadlocks). Un interblocage survient lorsque un ou plusieurs threads sont bloqus en attendant quelque chose qui n'aura jamais lieu. Un type simple d'interblocage peut survenir lorsqu'un mme thread tente de verrouiller un mutex deux fois d'affil. Ce qui se passe dans un tel cas dpend du type de mutex utilis. Trois types de mutexes existent: Le verrouillage d'un mutex rapide (le type par dfaut) provoquera un interblocage. Une tentative de verrouillage sur le mutex est bloquante jusqu' ce que le mutex soit dverrouill. Mais comme le thread est bloqu sur un mutex qu'il a lui-mme verrouill, le verrou ne pourra jamais tre supprim. Le verrouillage d'un mutex rcursif ne cause pas d'interblocage. Un mutex rcursif peut tre verrouill plusieurs fois par le mme thread en toute scurit. Le mutex se souvient combien de fois pthread_mutex_lock a t appel par le thread qui dtient le verrou; ce thread doit effectuer autant d'appels pthread_mutex_unlock pour que le mutex soit effectivement dverrouill et qu'un autre thread puisse y accder. GNU/Linux dtectera et signalera un double verrouillage sur un mutex vrification d'erreur qui causerait en temps normal un interblocage. Le second appel pthread_mutex_lock renverra le code d'erreur EDEADLK.

Par dfaut, un mutex GNU/Linux est de type rapide. Pour crer un mutex d'un autre type, commencez par crer un objet d'attributs de mutex en dclarant une variable de type pthread_mutexattr_t et appelez pthread_mutexattr_init en lui passant un pointeur dessus. Puis dfinissez le type de mutex en appelant pthread_mutexattr_setkind_np; son premier argument est un pointeur vers l'objet d'attributs de mutex et le second est PTHREAD_MUTEX_RECURSIVE_NP pour un mutex rcursif ou PTHREAD_MUTEX_ERRORCHECK_NP pour un mutex vrification d'erreurs. Passez un pointeur vers l'objet d'attributs de mutex pthread_mutex_init pour crer un mutex du type dsir, puis dtruisez l'objet d'attributs avec pthread_mutexattr_destroy. Le code suivant illustre la cration d'un mutex vrification d'erreurs, par exemple:
pthread_mutexattr_t attr; pthread_mutex_t mutex; pthread_mutexattr_init (&attr); pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_ERRORCHECK_NP); pthread_mutex_init (&mutex, &attr); pthread_mutexattr_destroy (&attr);

- 65 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Comme le suggre le suffixe np, les mutexes rcursifs et vrification d'erreurs sont spcifiques GNU/Linux et ne sont pas portables. Ainsi, il est gnralement dconseill de les utiliser dans vos programmes (les mutexes vrification d'erreurs peuvent cependant tre utiles lors du dbogage).

4-4-4 - Vrification de Mutex non Bloquante


De temps en temps, il peut tre utile de tester si un mutex est verrouill sans tre bloqu s'il l'est. Par exemple, un thread peut avoir besoin de verrouiller un mutex mais devoir faire autre chose au lieu de se bloquer si le mutex est dj verrouill. Comme pthread_mutex_lock ne se termine pas avant le dverrouillage du mutex, une autre fonction est ncessaire. GNU/Linux fournit pthread_mutex_trylock pour ce genre de choses. Si vous appelez pthread_mutex_trylock sur un mutex dverrouill, vous verrouillerez le mutex comme si vous aviez appel pthread_mutex_lock et pthread_mutex_trylock renverra zro. Par contre, si le mutex est dj verrouill par un autre mutex, pthread_mutex_trylock ne sera pas bloquante. Au lieu de cela, elle se terminera en renvoyant le code d'erreur EBUSY. Le verrou sur le mutex dtenu par l'autre thread n'est pas affect. Vous pouvez ressayer plus tard d'obtenir un verrou.

4-4-5 - Smaphores pour les Threads


Dans l'exemple prcdent, dans lequel plusieurs threads traitent les tches d'une file, la fonction principale des threads traite la tche suivante jusqu' ce qu'il n'en reste plus, ce moment, le thread se termine. Ce schma fonctionne si toutes les tches sont mises dans la file au pralable ou si de nouvelles tches sont ajoutes au moins aussi vite que la vitesse de traitement des threads. Cependant, si les threads fonctionnent trop rapidement, la file de tches se videra et les threads se termineront. Si de nouvelles tches sont mises dans la file plus tard, il ne reste plus de thread pour les traiter. Ce que nous devrions faire est de mettre en place un mcanisme permettant de bloquer les threads lorsque la file se vide et ce jusqu' ce que de nouvelles tches soient disponibles. L'utilisation de smaphores permet de faire ce genre de choses. Un smaphore est un compteur qui peut tre utilis pour synchroniser plusieurs threads. Comme avec les mutexes, GNU/Linux garantit que la vrification ou la modification de la valeur d'un smaphore peut tre accomplie en toute scurit, sans risque de concurrence critique. Chaque smaphore dispose d'une valeur de compteur, qui est un entier positif ou nul. Un smaphore supporte deux oprations de base: Une opration d'attente (wait), qui dcrmente la valeur du smaphore d'une unit. Si la valeur est dj zro, l'opration est bloquante jusqu' ce que la valeur du smaphore redevienne positive (en raison d'une opration de la part d'un autre thread). Lorsque la valeur du smaphore devient positive, elle est dcrmente d'une unit et l'opration d'attente se termine. Une opration de rveil (post) qui incrmente la valeur du smaphore d'une unit. Si le smaphore tait prcdemment zro et que d'autres threads taient en attente sur ce mme smaphore, un de ces threads est dbloqu et son attente se termine (ce qui ramne la valeur du smaphore zro).

Notez que GNU/Linux fournit deux implmentations lgrement diffrentes des smaphores. Celle que nous dcrivons ici est l'implmentation POSIX standard. Utilisez cette implmentation lors de la ommunication entre threads. L'autre implmentation, utilise pour la communication entre les processus, est dcrite dans la Section 5.2, Smaphores pour les Processus . Si vous utilisez les smaphores, incluez <semaphore.h>. Un smaphore est reprsent par une variable sem_t. Avant de l'utiliser, vous devez l'initialiser par le biais de la fonction sem_init, laquelle vous passez un pointeur sur la variable sem_t. Le second paramtre doit tre zro(Une valeur diffrente de zro indiquerait que le smaphore peut tre partag entre les processus, ce qui n'est pas support sous GNU/Linux pour ce type de smaphore.), et le troisime paramtre est la valeur initiale du smaphore. Si vous n'avez plus besoin d'un smaphore, il est conseill de librer les ressources qu'il occupe avec sem_destroy. Pour vous mettre en attente sur un smaphore, utilisez sem_wait. Pour effectuer une opration de rveil, utilisez sem_post. Une fonction permettant une mise en attente non bloquante, sem_trywait, est galement fournie. Elle est
- 66 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

similaire pthread_mutex_trylock ? si l'attente devait bloquer en raison d'un smaphore zro, la fonction se termine immdiatement, avec le code d'erreur EAGAIN, au lieu de bloquer le thread. GNU/Linux fournit galement une fonction permettant d'obtenir la valeur courante d'un smaphore, sem_getvalue, qui place la valeur dans la variable int pointe par son second argument. Vous ne devriez cependant pas utiliser la valeur du smaphore que vous obtenez partir de cette fonction pour dcider d'une opration d'attente ou de rveil sur un smaphore. Utiliser ce genre de mthode peut conduire des conditions de concurrence critique: un autre thread peut changer la valeur du smaphore entre l'appel de sem_getvalue et l'appel une autre fonction de manipulation des smaphores. Utilisez plutt les fonctions d'oprations d'attente et de rveil atomiques. Pour revenir notre exemple de file d'attente de tches, nous pouvons utiliser un smaphore pour compter le nombre de tches en attente dans la file. Le Listing jobqueue3 contrle la file avec un smaphore. La fonction enqueue_job ajoute une nouvelle tche la file d'attente. File de Tches Contrle par un Smaphore job-queue3.c
#include <malloc.h> #include <pthread.h> #include <semaphore.h> struct job { /* Champ de chanage. */ struct job* next; /* Autres champs dcrivant la tche... */ }; /* Liste chane des tches en attente. */ struct job* job_queue; /* Mutex protgeant job_queue. */ pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER; /* Smaphore comptant le nombre de tches dans la file. */ sem_t job_queue_count; /* Initialisation de la file de tches. */ void initialize_job_queue () { /* La file est initialement vide. */ job_queue = NULL; /* Initialise le smaphore avec le nombre de tches dans la file. Sa valeur initiale est zro. */ sem_init (&job_queue_count, 0, 0); } /* Traite les tches en attente jusqu' ce que la file soit vide. */ void* thread_function (void* arg) { while (1) { struct job* next_job; /* Se met en attente sur le smaphore de la file de tches. Si sa valeur est positive, indiquant que la file n'est pas vide, dcrmente le total d'une unit. Si la file est vide, bloque jusqu' ce qu'une nouvelle tche soit mise en attente. */ sem_wait (&job_queue_count); /* Verrouille le mutex sur la file de tches. */ pthread_mutex_lock (&job_queue_mutex); /* cause du smaphore, nous savons que la file n'est pas vide. Rcupre donc la prochaine tche disponible. */ next_job = job_queue; /* Supprime la tche de la liste. */ job_queue = job_queue->next; /* Dverrouille le mutex de la file d'attente car nous en avons fini avec celle-ci pour le moment. */ pthread_mutex_unlock (&job_queue_mutex); /* Traite la tche. */ process_job (next_job); /* Libration des ressources. */ free (next_job); } return NULL; } /* Ajoute une nouvelle tche en tte de la file. */ void enqueue_job (/* Passez les donnes sur la tche ici */) - 67 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

File de Tches Contrle par un Smaphore job-queue3.c


{ struct job* new_job; /* Alloue un nouvel objet job. */ new_job = (struct job*) malloc (sizeof (struct job)); /* Positionner les autres champs de la struct job ici... */ /* Verrouille le mutex de la file de tches avant d'y accder. */ pthread_mutex_lock (&job_queue_mutex); /* Ajoute la nouvelle tche en tte de file. */ new_job->next = job_queue; job_queue = new_job; /* Envoie un signal de rveil smaphore pour indiquer qu'une nouvelle tche est disponible. Si des threads sont bloqus en attente sur le smaphore, l'un d'eux sera dbloqu et pourra donc traiter la tche. */ sem_post (&job_queue_count); /* Dverrouille le mutex de la file de tches. */ pthread_mutex_unlock (&job_queue_mutex);

Avant de prlever une tche en tte de file, chaque thread se mettra en attente sur le smaphore. Si la valeur de celui-ci est zro, indiquant que la file est vide, le thread sera tout simplement bloqu jusqu' ce que le smaphore devienne positif, indiquant que la tche a t ajoute la file. La fonction enqueue_job ajoute une tche la file. Comme thread_function, elle doit verrouiller le mutex de la file avant de la modifier. Aprs avoir ajout la tche la file, elle envoie un signal de rveil au smaphore, indiquant qu'une nouvelle tche est disponible. Dans la version du Listing jobqueue3, les threads qui traitent les tches ne se terminent jamais; si aucune tche n'est disponible pendant un certain temps, ils sont simplement bloqus dans sem_wait.

4-4-6 - Variables de Condition


Nous avons montr comment utiliser un mutex pour protger une variable contre les accs simultans de deux threads et comment utiliser les smaphores pour implmenter un compteur partag. Une variable de condition est un troisime dispositif de synchronisation que fournit GNU/Linux ; avec ce genre de mcanisme, vous pouvez implmenter des conditions d'excution plus complexes pour le thread. Supposons que vous criviez une fonction de thread qui excute une boucle infinie, accomplissant une tche chaque itration. La boucle du thread, cependant, a besoin d'tre contrle par un indicateur: la boucle ne s'excute que lorsqu'il est actif; dans le cas contraire, la boucle est mise en pause. Le Listing spincondvar montre comment vous pourriez l'implmenter au moyen d'une simple boucle. chaque itration, la fonction de thread vrifie que l'indicateur est actif. Comme plusieurs threads accdent l'indicateur, il est protg par un mutex. Cette implmentation peut tre correcte mais n'est pas efficace. La fonction de thread utilisera du temps processeur, que l'indicateur soit actif ou non, vrifier et revrifier cet indicateur, verrouillant et dverrouillant le mutex chaque fois. Ce dont vous avez rellement besoin, est un moyen de mettre le thread en pause lorsque l'indicateur n'est pas actif, jusqu' ce qu'un certain changement survienne qui pourrait provoquer l'activation de l'indicateur. Implmentation Simple de Variable de Condition spin-condvar.c
#include <pthread.h> int thread_flag; pthread_mutex_t thread_flag_mutex; void initialize_flag () { pthread_mutex_init (&thread_flag_mutex, NULL); thread_flag = 0; } /* Appelle do_work de faon rpte tant que l'indicateur est actif ; sinon, tourne dans la boucle. */ void* thread_function (void* thread_arg) { - 68 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Implmentation Simple de Variable de Condition spin-condvar.c


while (1) { int flag_is_set; /* Protge l'indicateur avec un mutex. */ pthread_mutex_lock (&thread_flag_mutex); flag_is_set = thread_flag; pthread_mutex_unlock (&thread_flag_mutex); if (flag_is_set) do_work (); /* Rien faire sinon, part boucler. */ } return NULL;

} /* Positionne la valeur de l'indicateur de thread FLAG_VALUE. void set_thread_flag (int flag_value) { /* Protge l'indicateur avec un verrouillage de mutex. */ pthread_mutex_lock (&thread_flag_mutex); thread_flag = flag_value; pthread_mutex_unlock (&thread_flag_mutex); }

*/

Une variable de condition vous permet de spcifier une condition qui lorsqu'elle est remplie autorise l'excution du thread et inversement, une condition qui lorsqu'elle est remplie bloque le thread. Du moment que tous les threads susceptibles de modifier la condition utilisent la variable de condition correctement, Linux garantit que les threads bloqus cause de la condition seront dbloqus lorsque la condition change. Comme pour les smaphores, un thread peut se mettre en attente sur une variable de condition. Si le thread A est en attente sur une variable de condition, il est bloqu jusqu' ce qu'un autre thread, le thread B, valide la mme variable de condition. Contrairement un smaphore, une variable de condition ne dispose pas de compteur ou de mmoire; le thread A doit se mettre en attente sur la variable de condition avant que le thread B ne la valide. Si le thread B valide la condition avant que le thread A ne fasse un wait dessus, la validation est perdue, et le thread A reste bloqu jusqu' ce qu'un autre thread ne valide la variable de condition son tour. Voici comment vous utiliseriez une variable de condition pour rendre l'exemple prcdent plus efficace: La boucle dans thread_function vrifie l'indicateur. S'il n'est pas actif, le thread se met en attente sur la variable de condition. La fonction set_thread_flag valide la variable de condition aprs avoir chang la valeur de l'indicateur. De cette faon, si thread_function est bloque sur la variable de condition, elle sera dbloque et vrifiera la condition nouveau.

Il y a un problme avec cette faon de faire: il y a une concurrence critique entre la vrification de la valeur de l'indicateur et la validation ou l'attente sur la variable de condition. Supposons que thread_function ait vrifi l'indicateur et ait dtermin qu'il n'tait pas actif. ce moment, l'ordonnancer de Linux suspend ce thread et relance le principal. Par hasard, le thread principal est dans set_thread_flag. Il active l'indicateur puis valide la variable de condition. Comme aucun thread n'est en attente sur la variable de condition ce moment (souvenez-vous que thread_function a t suspendue avant de se mettre en attente sur la variable de condition), la validation est perdue. Maintenant, lorsque Linux relance l'autre thread, il commence attendre la variable de condition et peut tre bloqu pour toujours. Pour rsoudre ce problme, nous avons besoin de verrouiller l'indicateur et la variable de condition avec un seul mutex. Heureusement, GNU/Linux dispose exactement de ce mcanisme. Chaque variable de condition doit tre utilise en conjugaison avec un mutex, pour viter ce type de concurrence critique. Avec ce principe, la fonction de thread suit les tapes suivantes: La boucle dans thread_function verrouille le mutex et lit la valeur de l'indicateur. Si l'indicateur est actif, elle dverrouille le mutex et excute la fonction de traitement. Si l'indicateur n'est pas actif, elle dverrouille le mutex de faon atomique et se met en attente sur la variable de condition.

- 69 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La fonctionnalit critique utilise se trouve dans l'tape 3, GNU/Linux vous permet de dverrouiller le mutex et de vous mettre en attente sur la variable de condition de faon atomique, sans qu'un autre thread puisse intervenir. Cela limine le risque qu'un autre thread ne change la valeur de l'indicateur et ne valide la variable de condition entre le test de l'indicateur et la mise en attente sur la variable de condition de thread_function. Une variable de condition est reprsente par une instance de pthread_cond_t. Souvenez-vous que chaque variable de condition doit tre accompagne d'un mutex. Voici les fonctionsqui manipulent les variables de condition: pthread_cond_init initialise une variable de condition. Le premier argument est un pointeur vers une variable pthead_cond_t. Le second argument, un pointeur vers un objet d'attributs de variable de condition, est ignor par GNU/Linux. Le mutex doit tre initialis part, comme indiqu dans la Section 4.4.2, Mutexes . pthread_cond_signal valide une variable de condition. Un seul des threads bloqus sur la variable de condition est dbloqu. Si aucun thread n'est bloqu sur la variable de condition, le signal est ignor. L'argument est un pointeur vers la variable pthread_cond_t. Un appel similaire, pthread_cond_broadcast, dbloque tous les threads bloqus sur une variable de condition, au lieu d'un seul pthread_cond_wait bloque l'appelant jusqu' ce que la variable de condition soit valide. L'argument est un pointeur vers la variable pthread_cond_t. Le second argument est un pointeur vers la variable pthread_mutex_t. Lorsque pthread_cond_wait est appele, le mutex doit dj tre verrouill par le thread appelant. Cette fonction dverrouille automatiquement le mutex et se met en attente sur la variable de condition. Lorsque la variable de condition est valide et que le thread appelant est dbloqu, pthread_cond_wait racquiert automatiquement un verrou sur le mutex.

Lorsque votre programme effectue une action qui pourrait modifier la condition que vous protgez avec la variable de condition, il doit suivre les tapes suivante (dans notre exemple, la condition est l'tat de l'indicateur du thread, donc ces tapes doivent tre suivies chaque fois que l'indicateur est modifi): Verrouiller le mutex accompagnant la variable de condition. Effectuer l'action qui pourrait modifier la condition (dans notre exemple, activer l'indicateur). Valider ou effectuer un broadcast sur la variable de condition, selon le comportement dsir. Dverrouiller le mutex accompagnant la variable de condition.

Le Listing condvar reprend l'exemple prcdent, en utilisant une variable de condition pour protger l'indicateur. Notez qu'au sein de thread_function, un verrou est pos sur le mutex avant de vrifier la valeur de thread_flag. Ce verrou est automatiquement libr par pthread_cond_wait avant qu'il ne se bloque et automatiquement racquis ensuite. Notez galement que set_thread_flag verrouille le mutex avant de dfinir la valeur de thread_flag et de valider le mutex. Contrler un Thread avec une Variable de Condition condvar.c
#include <pthread.h> int thread_flag; pthread_cond_t thread_flag_cv; pthread_mutex_t thread_flag_mutex; void initialize_flag () { /* Initialise le mutex et la variable de condition. */ pthread_mutex_init (&thread_flag_mutex, NULL); pthread_cond_init (&thread_flag_cv, NULL); /* Initialise la valeur de l'indicateur. */ thread_flag = 0; } /* Appelle do_work de faon rpte tant que l'indicateur est actif ; bloque si l'indicateur n'est pas actif. */ void* thread_function (void* thread_arg) { /* Boucle infinie. */ while (1) { /* Verrouille le mutex avant d'accder la valeur de l'indicateur. */ pthread_mutex_lock (&thread_flag_mutex); while (!thread_flag) /* L'indicateur est inactif. Attend la validation de la variable de

- 70 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Contrler un Thread avec une Variable de Condition condvar.c

} /* Dfinit la valeur de l'indicateur FLAG_VALUE. */ void set_thread_flag (int flag_value) { /* Verrouille le mutex avant d'accder la valeur de l'indicateur. */ pthread_mutex_lock (&thread_flag_mutex); /* Dfinit la valeur de l'indicateur, puis valide la condition, si jamais thread_function est bloque en attente de l'activation de l'indicateur. Cependant, thread_function ne peut pas rellement tester la valeur de l'indicateur tant que le mutex n'est pas dverrouill. */ thread_flag = flag_value; pthread_cond_signal (&thread_flag_cv); /* Dverrouille le mutex. */ pthread_mutex_unlock (&thread_flag_mutex); }

} return NULL;

condition, indiquant que la valeur de l'indicateur a chang. Lorsque la validation a lieu et que le thread se dbloque, boucle et teste nouveau l'indicateur. */ pthread_cond_wait (&thread_flag_cv, &thread_flag_mutex); /* Lorsque nous arrivons ici, nous savons que l'indicateur est actif. Dverrouille le mutex. */ pthread_mutex_unlock (&thread_flag_mutex); /* Actions utiles. */ do_work ();

La condition protge par une variable de condition peut tre d'une complexit quelconque. Cependant, avant d'effectuer une opration qui pourrait modifier la condition, un verrouillage du mutex doit tre demand, aprs quoi la variable de condition doit tre valide. Une variable de condition peut aussi tre utilise sans condition, simplement pour bloquer un thread jusqu' ce qu'un autre thread le rveille . Un smaphore peut galement tre utilis pour ce faire. La principale diffrence est qu'un smaphore se souvient de l'appel de rveil, mme si aucun thread n'tait bloqu en attente ce moment, alors qu'une variable de condition ignore les appels de rveil, moins qu'un thread ne soit bloqu en attente ce moment. Qui plus est, un smaphore ne ractive qu'un thread par signal de rveil; avec pthread_cond_broadcast, un nombre quelconque et inconnu de threads bloqus peuvent tre relancs en une fois.

4-4-7 - Interblocage avec Deux Threads ou Plus


Des interblocages peuvent survenir lorsque deux threads (ou plus) sont bloqus, chacun attendant une validation de condition que seul l'autre peut effectuer. Par exemple, si le thread A est bloqu sur une variable de condition attendant que le thread B la valide et le thread B est bloqu sur une variable de condition attendant que le thread A la valide, un interblocage survient car aucun thread ne pourra jamais valider la variable attendue par l'autre. Vous devez tre attentif afin d'viter l'apparition de telles situations car elles sont assez difficiles dtecter. Une situation courante menant un interblocage survient lorsque plusieurs threads tentent de verrouiller le mme ensemble d'objets. Par exemple, considrons un programme dans lequel deux threads diffrents, excutant deux fonctions de thread distinctes, ont besoin de verrouiller les deux mmes mutexes. Supposons que le thread A verrouille le mutex 1, puis le mutex 2 et que le thread B verrouille le mutex 2 avant le mutex 1. Avec un scnario d'ordonnancement pessimiste, Linux pourrait donner la main au thread A suffisamment longtemps pour qu'il verrouille le mutex 1 puis donne la main au thread B qui verrouille immdiatement le mutex 2. Dsormais, aucun thread ne peut plus avancer cas chacun est bloqu sur un mutex que l'autre maintient verrouill. C'est un exemple de problme d'interblocage gnral qui peut impliquer non seulement des objets de synchronisation, comme les mutex, mais galement d'autres ressources, comme des verrous sur des fichiers ou des priphriques. Le problme survient lorsque plusieurs threads tentent de verrouiller le mme ensemble de ressources dans des ordres diffrents. La solution est de s'assurer que tous les threads qui verrouillent plus d'une ressource le font dans le mme ordre.

- 71 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

4-5 - Implmentation des Threads sous GNU/Linux


L'implmentation des threads POSIX sous GNU/Linux diffre de l'implmentation des threads sous beaucoup de systmes de type UNIX sur un point important: sous GNU/Linux, les threads sont implments comme des processus. Lorsque vous appelez pthread_create pour crer un nouveau thread, Linux cre un nouveau processus qui excute ce thread. Cependant, ce processus n'est pas identique ceux crs au moyen de fork; en particulier, il partage son espace d'adressage et ses ressources avec le processus original au lieu d'en recevoir des copies. Le programme thread-pid du Listing threadpid le dmontre. Le programme cre un thread; le thread original et le nouveau appellent tous deux la fonction getpid et affichent leurs identifiants de processus respectifs, puis bouclent indfiniment. Affiche les Identifiants de Processus des Threads thread-pid.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> void* thread_function (void* arg) { fprintf (stderr, "L'identifiant du thread fils est %d\n", (int) getpid ()); /* Boucle indfiniment. */ while (1); return NULL; } int main () { pthread_t thread; fprintf(stderr, "L'identifiant du thread principal est %d\n",(int)getpid()); pthread_create (&thread, NULL, &thread_function, NULL); /* Boucle indfiniment. */ while (1); return 0; }

Lancez le programme en arrire-plan puis invoquez ps x pour afficher vos processus en cours d'excution. N'oubliez pas de tuer le programme thread-pid ensuite ? il consomme beaucoup de temps processeur pour rien. Voici quoi la sortie pourrait ressembler:
% cc thread-pid.c -o thread-pid -lpthread % ./thread-pid & [1] 14608 main thread pid is 14608 child thread pid is 14610 % ps x PID TTY STAT TIME COMMAND 14042 pts/9 S 0:00 bash 14608 pts/9 R 0:01 ./thread-pid 14609 pts/9 S 0:00 ./thread-pid 14610 pts/9 R 0:01 ./thread-pid 14611 pts/9 R 0:00 ps x % kill 14608 [1]+ Terminated ./thread-pid

Les lignes dbutant par [1] viennent du shell. Lorsque vous excutez un programme en arrire-plan, le shell lui assigne un numro de tche ? dans ce cas, 1 ? et affiche l'identifiant de processus du programme. Si une tche en arrire-plan se termine, le shell le signale lorsque vous invoquez une nouvelle commande. Remarquez qu'il y a trois processus excutant le programme thread-pid. Le premier, avec le pid 14608, est le thread principal du programme ; le troisime, avec le pid 14610, est le thread que nous avons cr pour excuter thread_function.

- 72 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Qu'en est-il du second thread, avec le pid 14609 ? Il s'agit du thread de gestion (manager thread) qui fait partie de l'implmentation interne des threads sous GNU/Linux. Le thread de contrle est cr la premire fois qu'un programme appelle pthread_create pour crer un nouveau thread.

4-5-1 - Gestion de Signaux


Supposons qu'un programme multithread reoive un signal. Dans quel thread est invoqu le gestionnaire de signal? Le comportement de l'interaction entre les threads et les signaux varie d'un type d'UNIX l'autre. Sous GNU/Linux, ce comportement est dict par le fait que les threads sont implments comme des processus. Comme chaque thread est un processus distinct, et comme un signal est dlivr un processus particulier, il n'y a pas d'ambigut au niveau du thread qui va recevoir le signal. Typiquement, les signaux envoys de l'extrieur du programme sont envoys au processus correspondant au thread principal du programme. Par exemple, si un processus se divise et que le processus fils excute un programme multithread, le processus pre conservera l'identifiant de processus du thread principal du programme du processus fils et l'utilisera pour envoyer des signaux son fils. Il s'agit gnralement d'un bonne convention que vous devriez suivre lorsque vous envoyez des signaux un programme multithread. Notez que cet aspect de l'implmentation de pthreads sous GNU/Linux va l'encontre du standard de threads POSIX. Ne vous reposez pas sur ce comportement au sein de programmes destins tre portables. Au sein d'un programme multithread, il est possible pour un thread d'envoyer un signal un autre thread bien dfini. Utilisez la fonction pthread_kill pour cela. Son premier paramtre est l'identifiant du thread et le second, le numro du signal.

4-5-2 - L'appel Systme clone


Bien que les threads GNU/Linux crs dans le mme programme soient implments comme des processus spars, ils partagent leur espace mmoire virtuel et leurs autres ressources. Un processus fils cr avec fork, cependant, copie ces objets. Comment est cr le premier type de processus ? L'appel systme clone de Linux est une forme hybride entre fork et pthread_create qui permet l'appelant de spcifier les ressources partages entre lui et le nouveau processus. clone ncessite galement que vous spcifiiez la rgion mmoire utilise pour la pile d'excution du nouveau processus. Bien que nous mentionnions clone pour satisfaire la curiosit du lecteur, cet appel systme ne doit pas tre utilis au sein de programmes. Utilisez fork pour crer de nouveau processus ou pthread_create pour crer des threads.

4-6 - Comparaison Processus/Threads


Pour certains programmes tirant partie du paralllisme, le choix entre processus et threads peut tre difficile. Voici quelques pistes pour vous aider dterminer le modle de paralllisme qui convient le mieux votre programme: Tous les threads d'un programme doivent excuter le mme code. Un processus fils, au contraire, peut excuter un programme diffrent en utilisant une fonction exec. Un thread peut endommager les donnes d'autres threads du mme processus car les threads partagent le mme espace mmoire et leurs ressources. Par exemple, une criture sauvage en mmoire via un pointeur non initialis au sein d'un thread peut corrompre la mmoire d'un autre thread. Un processus corrompu par contre, ne peut pas agir de cette faon car chaque processus dispose de sa propre copie de l'espace mmoire du programme. Copier le contenu de la mmoire pour un nouveau processus a un cot en performances par rapport la cration d'un nouveau thread. Cependant, la copie n'est effectue que lorsque la mmoire est modifie, donc ce cot est minime si le processus fils ne fait que lire la mmoire. Les threads devraient tre utiliss pour les programmes qui ont besoin d'un paralllisme finement contrl. Par exemple, si un problme peut tre dcompos en plusieurs tches presque identiques, les threads

- 73 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

peuvent tre un bon choix. Les processus devraient tre utiliss pour des programmes ayant besoin d'un paralllisme plus grossier. Le partage de donnes entre des threads est trivial car ceux-ci partagent le mme espace mmoire (cependant, il faut faire trs attention viter les conditions de concurrence critique, comme expliqu plus haut). Le partage de donnes entre des processus ncessite l'utilisation de mcanismes IPC, comme expliqu dans le Chapitre 5. Cela peut tre plus complexe mais diminue les risques que les processus souffrent de bugs lis au paralllisme.

- 74 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5 - Communication Interprocessus
Le Chapitre processus, Processus traitait de la cration de processus et montrait comment il est possible d'obtenir le code de sortie d'un processus fils. Il s'agit de la forme la plus simple de communication entre deux processus, mais en aucun cas de la plus puissante. Les mcanismes prsents au Chapitre processus ne fournissent aucun moyen au processus parent pour communiquer avec le fils except via les arguments de ligne de commande et les variables d'environnement, ni aucun moyen pour le processus fils de communiquer avec son pre, except par le biais de son code de sortie. Aucun de ces mcanismes ne permet de communiquer avec le processus fils pendant son excution, ni n'autorise une communication entre les processus en dehors de la relation pre-fils. Ce chapitre prsente des moyens de communication interprocessus qui dpassent ces limitations. Nous prsenterons diffrentes faons de communiquer entre pre et fils, entre des processus sans liens et mme entre des processus s'excutant sur des machines distinctes. La communication interprocessus (interprocess communication, IPC) consiste transfrer des donnes entre les processus. Par exemple, un navigateur Internet peut demander une page un serveur, qui envoie alors les donnes HTML. Ce transfert utilise des sockets dans une connexion similaire celle du tlphone. Dans un autre exemple, vous pourriez vouloir imprimer les noms des fichiers d'un rpertoire en utilisant une commande du type ls | lpr. Le shell cre un processus ls et un processus lpr distincts et connecte les deux au moyen d'un tube (ou pipe) reprsent par le symbole "|". Un tube permet une communication sens unique entre deux processus. Le processus ls crit les donnes dans le tube et le processus lpr les lit partir du tube. Dans ce chapitre, nous traiterons de cinq types de communication interprocessus: La mmoire partage permet aux processus de communiquer simplement en lisant ou crivant dans un emplacement mmoire prdfini. La mmoire mappe est similaire la mmoire partage, except qu'elle est associe un fichier. Les tubes permettent une communication squentiel d'un processus l'autre. Les files FIFO sont similaires aux tubes except que des processus sans lien peuvent communiquer car le tube reoit un nom dans le systme de fichiers. Les sockets permettent la communication entre des processus sans lien, pouvant se trouver sur des machines distinctes.

Ces types d'IPC diffrent selon les critres suivants: Ils restreignent ou non la communication des processus lis (processus ayant un anctre commun), des processus partageant le mme systme de fichiers ou tout ordinateur connect un rseau. Un processus communiquant n'est limit qu' la lecture ou qu' l'criture de donnes. Le nombre de processus pouvant communiquer. Les processus qui communiquent sont synchroniss par l'IPC ? par exemple, un processus lecteur s'interrompt jusqu' ce qu'il y ait des donnes lire.

Dans ce chapitre, nous ne traiteront pas des IPC ne permettant qu'une communication limite un certain nombre de fois, comme communiquer en utilisant la valeur de sortie du fils.

5-1 - Mmoire Partage


Une des mthodes de communication interprocessus les plus simples est d'utiliser la mmoire partage. La mmoire partage permet deux processus ou plus d'accder la mme zone mmoire comme s'ils avaient appel malloc et avaient obtenu des pointeurs vers le mme espace mmoire. Lorsqu'un processus modifie la mmoire, tous les autres processus voient la modification.

- 75 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-1-1 - Communication Locale Rapide


La mmoire partage est la forme de communication interprocessus la plus rapide car tous les processus partagent la mme mmoire. L'accs cette mmoire partage est aussi rapide que l'accs la mmoire non partage du processus et ne ncessite pas d'appel systme ni d'entre dans le noyau. Elle vite galement les copies de donnes inutiles. Comme le noyau ne coordonne pas les accs la mmoire partage, vous devez mettre en place votre propre synchronisation. Par exemple, un processus ne doit pas effectuer de lecture avant que des donnes aient t crites et deux processus ne doivent pas crire au mme emplacement en mme temps. Une stratgie courante pour viter ces conditions de concurrence est d'utiliser des smaphores, ce dont nous parlerons dans la prochaine section. Nos programmes d'illustration, cependant, ne montrent qu'un seul processus accdant la mmoire, afin de se concentrer sur les mcanismes de la mmoire partage et viter d'obscurcir le code avec la logique de synchronisation.

5-1-2 - Le Modle Mmoire


Pour utiliser un segment de mmoire partage, un processus doit allouer le segment. Puis, chaque processus dsirant accder au segment doit l'attacher. Aprs avoir fini d'utiliser le segment, chaque processus le dtache. un moment ou un autre, un processus doit librer le segment. La comprhension du modle mmoire de Linux aide expliquer le processus d'allocation et d'attachement. Sous Linux, la mmoire virtuelle de chaque processus est divise en pages. Chaque processus conserve une correspondance entre ses adresses mmoire et ces pages de mmoire virtuelle, qui contiennent rellement les donnes. Mme si chaque processus dispose de ses propres adresses, plusieurs tables de correspondance peuvent pointer vers la mme page, permettant le partage de mmoire. Les pages mmoires sont examines de plus prs dans la Section 8.8, La Famille mlock: Verrouiller la Mmoire Physique , du Chapitre 8, Appels Systme Linux . L'allocation d'un nouveau segment de mmoire partage provoque la cration de nouvelles pages de mmoire virtuelle. Comme tous les processus dsirent accder au mme segment de mmoire partage, seul un processus doit allouer un nouveau segment de mmoire partage. Allouer un segment existant ne cre pas de nouvelles pages mais renvoie l'identifiant des pages existantes. Pour qu'un processus puisse utiliser un segment de mmoire partage, il doit l'attacher, ce qui ajoute les correspondances entre sa mmoire virtuelle et les pages partages du segment. Lorsqu'il en a termin avec le segment, ces correspondances sont supprimes. Lorsque plus aucun processus n'a besoin d'accder ces segments de mmoire partage, un processus exactement doit librer les pages de mmoire virtuelle. Tous les segments de mmoire partage sont allous sous forme de multiples entiers de la taille de page du systme, qui est le nombre d'octets dans une page mmoire. Sur les systmes Linux, la taille de page est de 4 Ko mais vous devriez vous baser sur la valeur renvoye par getpagesize.

5-1-3 - Allocation
Un processus alloue un segment de mmoire partage en utilisant shmget ( SHared Memory GET , obtention de mmoire partage). Son premier paramtre est une cl entire qui indique le segment crer. Des processus sans lien peuvent accder au mme segment partag en spcifiant la mme valeur de cl. Malheureusement, d'autres processus pourraient avoir choisi la mme valeur de cl fix, ce qui provoquerait un conflit. Utiliser la constante spciale IPC_PRIVATE comme valeur de cl garantit qu'un nouveau segment mmoire est cr. Le second paramtre indique le nombre d'octets du segment. Comme les segments sont allous en utilisant des pages, le nombre d'octets effectivement allous est arrondi au multiple de la taille de page suprieur. Le troisime paramtre est un ou binaire entre des indicateurs dcrivant les options demandes shmget. Voici ces indicateurs:

- 76 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

IPC_CREAT ? Cet indicateur demande la cration d'un nouveau segment. Cela permet la cration d'un nouveau segment tout en spcifiant une valeur de cl. IPC_EXCL ? Cet indicateur, toujours utilis avec IPC_CREAT, provoque l'chec de shmget si la cl de segment spcifie existe dj. Donc, cela permet au processus appelant d'avoir un segment exclusif . Si cette option n'est pas prcise et que la cl d'un segment existant est utilis, shmget renvoie le segment existant au lieu d'en crer un nouveau. Indicateurs de mode ? Cette valeur est constitue de 9~bits indiquant les permissions du propritaire, du groupe et des autres utilisateurs pour contrler l'accs au segment. Les bits d'excution sont ignors. Une faon simple de spcifier les permissions est d'utiliser les constantes dfinies dans <sys/stat.h> et documentes dans la page de manuel de section 2 de stat(Ces bits de permissions sont les mmes que ceux utiliss pour les fichiers. Ils sont dcrits dans la Section 10.3, Permissions du Systme de Fichiers .). Par exemple, S_IRUSR et S_IWUSR spcifient des permissions de lecture et criture pour le propritaire du segment de mmoire partage et S_IROTH et S_IWOTH spcifient des permissions de lecture et criture pour les autres utilisateurs.

L'appel suivant shmget cre un nouveau segment de mmoire partage (ou accde un segment existant, si shm_key est dj utilis) qui peut tre lu et crit par son propritaire mais pas par les autres utilisateurs.
int segment_id = shmget (shm_key, getpagesize (), IPC_CREAT | S_IRUSR | S_IWUSR);

Si l'appel se passe bien, shmget renvoie un identifiant de segment. Si le segment de mmoire partage existe dj, les permissions d'accs sont vrifies et le systme s'assure que le segment n'est pas destin tre dtruit.

5-1-4 - Attachement et Dtachement


Pour rendre le segment de mmoire partage disponible, un procesus doit utiliser shmat ( SHared Memory ATtach , attachement de mmoire partage) en lui passant l'identifiant du segment de mmoire partage SHMID renvoy par shmget. Le second argument est un pointeur qui indique o vous voulez que le segment soit mis en correspondance dans l'espace d'adressage de votre processus ; si vous passez NULL, Linux slectionnera une adresse disponible. Le troisime argument est un indicateur, qui peut prendre une des valeurs suivantes: SHM_RND indique que l'adresse spcifie par le second paramtre doit tre arrondie un multiple infrieur de la taille de page. Si vous n'utilisez pas cet indicateur, vous devez aligner le second argument de shmat sur un multiple de page vous-mme. SHM_RDONLY indique que le segment sera uniquement lu, pas crit.

Si l'appel se droule correctement, il renvoie l'adresse du segment partag attach. Les processus fils crs par des appels fork hritent des segments partags attachs; il peuvent les dtacher s'ils le souhaitent. Lorsque vous en avez fini avec un segment de mmoire partage, le segment doit tre dtach en utilisant shmdt ( SHared Memory DeTach , Dtachement de Mmoire Partage) et lui passant l'adresse renvoye par shmat. Si le segment n'a pas t libr et qu'il s'agissait du dernier processus l'utilisant, il est supprim. Les appels exit et toute fonction de la famille d'exec dtachent automatiquement les segments.

5-1-5 - Contrler et Librer la Mmoire Partage


L'appel shmctl ( SHared Memory ConTroL , contrle de la mmoire partage) renvoie des informations sur un segment de mmoire partage et peut le modifier. Le premier paramtre est l'identifiant d'un segment de mmoire partage. Pour obtenir des informations sur un segment de mmoire partage, passez IPC_STAT comme second argument et un pointeur vers une struct shmid_ds.

- 77 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour supprimer un segment, passez IPC_RMID comme second argument et NULL comme troisime argument. Le segment est supprim lorsque le dernier processus qui l'a attach le dtache. Chaque segment de mmoire partage devrait tre explicitement libr en utilisant shmctl lorsque vous en avez termin avec lui, afin d'viter de dpasser la limite du nombre total de segments de mmoire partage dfinie par le systme. L'invocation de exit et exec dtache les segments mmoire mais ne les libre pas. Consultez la page de manuel de shmctl pour une description des autres oprations que vous pouvez effectuer sur les segments de mmoire partage.

5-1-6 - Programme Exemple


Le programme du Listing shm illustre l'utilisation de la mmoire partage. Utilisation de la Mmoire Partage shm.c
#include <stdio.h> #include <sys/shm.h> #include <sys/stat.h> int main () { int segment_id; char* shared_memory; struct shmid_ds shmbuffer; int segment_size; const int shared_segment_size = 0x6400; /* Alloue le segment de mmoire partage. */ segment_id = shmget (IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); /* Attache le segment de mmoire partage. */ shared_memory = (char*) shmat (segment_id, 0, 0); printf ("mmoire partage attache l'adresse %p\n", shared_memory); /* Dtermine la taille du segment. */ shmctl (segment_id, IPC_STAT, &shmbuffer); segment_size = shmbuffer.shm_segsz; printf ("taille du segment : %d\n", segment_size); /* crit une chane dans le segment de mmoire partage. */ sprintf (shared_memory, "Hello, world."); /* Dtache le segment de mmoire partage. */ shmdt (shared_memory); /* Rattache le segment de mmoire partage une adresse diffrente. */ shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0); printf ("mmoire partage rattache l'adresse %p\n", shared_memory); /* Affiche la chane de la mmoire partage. */ printf ("%s\n", shared_memory); /* Dtache le segment de mmoire partage. */ shmdt (shared_memory); /* Libre le segment de mmoire partage. shmctl (segment_id, IPC_RMID, 0); } return 0; */

5-1-7 - Dbogage
La commande ipcs donne des informations sur les possibilits de communication interprocessus, y compris les segments de mmoire partage. Utilisez l'option -m pour obtenir des informations sur la mmoire partage. Par exemple, ce code illustre le fait qu'un segment de mmoire partage, numrot 1627649, est utilis:
% ipcs -m

- 78 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) ??? Segments de mmoire partage ???? cl shmid propritaire perms 0x00000000 1627649 user 640

octets nattch tat 25600 0

Si ce segment de mmoire avait t oubli par erreur par un programme, vous pouvez utiliser la commande ipcrm pour le supprimer.
% ipcrm shm 1627649

5-1-8 - Avantages et Inconvnients


Les segments de mmoire partage permettent une communication bidirectionnelle rapide entre n'importe quel nombre de processus. Chaque utilisateur peut la fois lire et crire, mais un programme doit dfinir et suivre un protocole pour viter les conditions de concurrence critique comme craser des informations avant qu'elles ne soient lues. Malheureusement, Linux ne garantit pas strictement l'accs exclusif mme si vous crez un nouveau segment partag avec IPC_PRIVATE. De plus, lorsque plusieurs processus utilisent un segment de mmoire partage, ils doivent s'arranger pour utiliser la mme cl.

5-2 - Smaphores de Processus


Nous l'avons voqu dans la section prcdente, les processus doivent se coordonner pour accder la mmoire partage. Comme nous l'avons dit dans la Section semthreads, Smaphores pour les Threads , du Chapitre threads, Threads , les smaphores sont des compteurs qui permettent la synchronisation de plusieurs threads. Linux fournit une implmentation alternative distincte de smaphores qui peuvent tre utiliss pour synchroniser les processus (appels smaphores de processus ou parfois smaphores System~V). Les smaphores de processus sont instancis, utiliss et librs comme les segments de mmoire partage. Bien qu'un seul smaphore soit suffisant pour quasiment toutes les utilisations, les smaphores de processus sont regroups en ensembles. Tout au long de cette section, nous prsentons les appels systme relatifs aux smaphores de processus, en montrant comment implmenter des smaphores binaires isols les utilisant.

5-2-1 - Instanciation et Libration


Les appels semget et semctl instancient et librent des smaphores, ils sont analogues shmget et shmctl pour la mmoire partage. Invoquez semget avec une cl correspondant un ensemble de smaphores, le nombre de smaphores dans l'ensemble et des indicateurs de permissions, comme pour shmget; la valeur de retour est un identifiant d'ensemble de smaphores. Vous pouvez obtenir l'identifiant d'un ensemble de smaphores existant en passant la bonne valeur de cl; dans ce cas, le nombre de smaphores peut tre zro. Les smaphores continuent exister mme aprs que tous les processus les utilisant sont termins. Le dernier processus utiliser un ensemble de smaphores doit le supprimer explicitement afin de s'assurer que le systme d'exploitation ne tombe pas court de smaphores. Pour cela, invoquez semctl avec l'identifiant de l'ensemble de smaphores, le nombre de smaphores qu'il contient, IPC_RMID en troisime argument et n'importe quelle valeur d'union semun comme quatrime argument (qui est ignor). L'identifiant d'utilisateur effectif du processus appelant doit correspondre celui de l'instanciateur du smaphore (ou l'appelant doit avoir les droits root). Contrairement aux segments de mmoire partage, la suppression d'un jeu de smaphores provoque sa libration immdiate par Linux. Le Listing semalldeall prsente les fonctions d'allocation et de libration d'un smaphore binaire. Allouer et Librer un Smaphore Binaire sem_all_deall.c

- 79 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Allouer et Librer un Smaphore Binaire sem_all_deall.c


#include <sys/ipc.h> #include <sys/sem.h> #include <sys/types.h>

/* Nous devons dfinir l'union semun nous-mmes. */ union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *%%__%%buf; }; /* Obtient l'identifiant d'un smaphore binaire, l'alloue si ncessaire. */ int binary_semaphore_allocation (key_t key, int sem_flags) { return semget (key, 1, sem_flags); } /* Libre un smaphore binaire. Tous les utilisateurs doivent avoir fini de s'en servir. Renvoie -1 en cas d'chec. */ int binary_semaphore_deallocate (int semid) { union semun ignored_argument; return semctl (semid, 1, IPC_RMID, ignored_argument); }

5-2-2 - Initialisation des Smaphores


L'instanciation et l'initialisation des smaphores sont deux oprations distinctes. Pour initialiser un smaphore, utilisez semctl en lui passant zro comme second argument et SETALL comme troisime paramtre. Pour le quatrime argument, vous devez crer un objet union semun et faire pointer son champ array vers un tableau de valeurs unsigned short. Chaque valeur est utilise pour initialiser un smaphore dans l'ensemble. Le Listing seminit prsente une fonction initialisant un smaphore binaire. Initialiser un Smaphore Binaire sem_init.c
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> /* Nous devons dfinir l'union semun nous-mmes. */ union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *%%__%%buf; }; /* Initialise un smaphore binaire avec une valeur de 1. */ int binary_semaphore_initialize (int semid) { union semun argument; unsigned short values[1]; values[0] = 1; argument.array = values; return semctl (semid, 0, SETALL, argument); }

- 80 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-2-3 - Oprations d'Attente et de Rveil


Chaque smaphore contient une valeur positive ou nulle et supporte des oprations d'attente et de rveil. L'appel systme semop implmente les deux oprations. Son premier paramtre est l'identifiant d'un ensemble de smaphores. Son second paramtre est un tableau d'lments struct sembuf qui dfinit les oprations que vous voulez accomplir. Le troisime paramtre est la taille de ce tableau. Les champs de la struct sembuf sont les suivants: sem_num est le numro du smaphore dans l'ensemble sur lequel est effectue l'opration. sem_op est un entier spcifiant l'opration accomplir. Si sem_op est un entier positif, ce chiffre est ajout la valeur du smaphore immdiatement. Si sem_op est un nombre ngatif, la valeur absolue de ce chiffre est soustraite de la valeur du smaphore. Si cela devait rendre la valeur du smaphore ngative, l'appel est bloquant jusqu' ce que la valeur du smaphore atteigne la valeur absolue de sem_op (par le biais d'incrmentations effectues par d'autres processus). Si sem_op est zro, l'opration est bloquante jusqu' ce que la valeur atteigne zro. sem_flg est un indicateur. Positionnez-le IPC_NOWAIT pour viter que l'opration ne soit bloquante; au lieu de cela, l'appel semop choue si elle devait l'tre. Si vous le positionnez SEM_UNDO, Linux annule automatiquement l'opration sur le smaphore lorsque le processus se termine.

Le Listing sempv illustre les oprations d'attente et de rveil pour un smaphore binaire. Attente et Rveil pour un Smaphore Binaire sem_pv.c
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> /* Se met en attente sur un smaphore binaire. Bloque jusqu' ce que la valeur du smaphore soit positive, puis le dcrmente d'une unit. */ int binary_semaphore_wait (int semid) { struct sembuf operations[1]; /* Utilise le premier (et unique) smaphore. */ operations[0].sem_num = 0; /* Dcrmente d'une unit. */ operations[0].sem_op = -1; /* Autorise l'annulation. */ operations[0].sem_flg = SEM_UNDO; } return semop (semid, operations, 1);

/* Envoie un signal de rveil un smaphore binaire : incrmente sa valeur d'une unit. Sort de la fonction immdiatement. */ int binary_semaphore_post (int semid) { struct sembuf operations[1]; /* Utilise le premier (et unique) smaphore. */ operations[0].sem_num = 0; /* Incrmente d'une unit. */ operations[0].sem_op = 1; /* Autorise l'annulation. */ operations[0].sem_flg = SEM_UNDO; } return semop (semid, operations, 1);

Passer l'indicateur SEM_UNDO permet de traiter le problme de la fin d'un processus alors qu'il dispose de ressources alloues via un smaphore. Lorsqu'un processus se termine, volontairement ou non, la valeur du smaphore est automatiquement ajuste pour annuler les actions du processus sur le smaphore. Par exemple, si un processus qui a dcrment le smaphore est tu, la valeur du smaphore est incrmente.

- 81 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-2-4 - Dbogage des Smaphores


Utilisez la commande ipcs -s pour afficher des informations sur les ensembles de smaphores existants. Utilisez la commande ipcrm sem pour supprimer un ensemble de smaphores depuis la ligne de commande. Par exemple, pour supprimer l'ensemble de smaphores ayant l'identifiant 5790517, utilisez cette commande:
% ipcrm sem 5790517

5-3 - Mmoire Mappe


La mmoire mappe permet diffrents processus de communiquer via un fichier partag. Bien que vous puissiez concevoir l'utilisation de mmoire mappe comme tant celle d'un segment de mmoire partage avec un nom, vous devez tre conscient qu'il existe des diffrences techniques. La mmoire mappe peut tre utilise pour la communication interprocessus ou comme un moyen pratique d'accder au contenu d'un fichier. La mmoire mappe cre une correspondance entre un fichier et la mmoire d'un processus. Linux divise le fichier en fragments de la taille d'une page puis les copie dans des pages de mmoire virtuelle afin qu'elles puissent tre disponibles au sein de l'espace d'adressage d'un processus. Donc le processus peut lire le contenu du fichier par le biais d'accs mmoire classiques. Celapermet un accs rapide aux fichiers. Vous pouvez vous reprsenter la mmoire mappe comme l'allocation d'un tampon contenant la totalit d'un fichier, la lecture du fichier dans le tampon, puis (si le tampon est modifi) l'criture de celui-ci dans le fichier. Linux gre les oprations de lecture et d'criture votre place. Il existe d'autres utilisations des fichiers de mmoire mappe que la communication interprocessus. Quelques unes d'entre elles sont traites dans la Section 5.3.5, Autres utilisations de mmap.

5-3-1 - Mapper un Fichier Ordinaire


Pour mettre en correspondance un fichier ordinaire avec la mmoire d'un processus, utilisez l'appel mmap ( Memory MAPped , Mmoire mappe, prononcez em-map ). Le premier argument est l'adresse laquelle vous dsirez que Linux mette le fichier en correspondance au sein de l'espace d'adressage de votre processus; la valeur NULL permet Linux de choisir une adresse de dpart disponible. Le second argument est la longueur de l'espace de correspondance en octets. Le troisime argument dfinit la protection de l'intervalle d'adresses mis en correspondance. La protection consiste en un ou binaire entre PROT_READ, PROT_WRITE et PROT_EXEC, correspondant aux permissions de lecture, d'criture et d'excution, respectivement. Le quatrime argument est un drapeau spcifiant des options supplmentaires. Le cinquime argument est un descripteur de fichier pointant vers le fichier mettre en correspondance, ouvert en lecture. Le dernier argument est le dplacement, par rapport au dbut du fichier, partir duquel commencer la mise en correspondance. Vous pouvez mapper tout ou partie du fichier en mmoire en choisissant le dplacement et la longueur de faon approprie. Le drapeau est un ou binaire entre ces contraintes: MAP_FIXED ? Si vous utilisez ce drapeau, Linux utilise l'adresse que vous demandez pour mapper le fichier plutt que de la considrer comme une indication. Cette adresse doit tre aligne sur une page. MAP_PRIVATE ? Les critures en mmoire ne doivent pas tre rpercutes sur le fichier mis en correspondance, mais sur une copie prive du fichier. Aucun autre processus ne voit ces critures. Ce mode ne doit pas tre utilis avec MAP_SHARED. MAP_SHARED ? Les critures sont immdiatement rpercutes sur le fichier mis en correspondance. Utilisez ce mode lorsque vous utilisez la mmoire mappe pour l'IPC. Ce mode ne doit pas tre utilis avec MAP_PRIVATE.

- 82 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si l'appel se droule avec succs, la fonction renvoie un pointeur vers le dbut de la mmoire. S'il choue, la fonction renvoie MAP_FAILED.

5-3-2 - Programmes Exemples


Examinons deux programmes pour illustrer l'utilisation des rgions de mmoire mappe pour lire et crire dans des fichiers. Le premier programme, le Listing mmapwrite, gnre un nombre alatoire et l'crit dans un fichier mapp en mmoire. Le second programme, le Listing mmapread, lit le nombre, l'affiche et le remplace par le double de sa valeur. Tous deux prennent en argument de ligne de commande le nom du fichier mettre en correspondance avec la mmoire. crit un Nombre Alatoire dans un Fichier Mapp mmap-write.c
#include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <time.h> #include <unistd.h> #define FILE_LENGTH 0x100 /* Renvoie un nombre alatoire compris dans l'intervalle [low,high]. int random_range (unsigned const low, unsigned const high) { unsigned const range = high - low + 1; return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0)); } int main (int argc, char* const argv[]) { int fd; void* file_memory; /* Initialise le gnrateur de nombres alatoires. srand (time (NULL)); */

*/

/* Prpare un fichier suffisamment long pour contenir le nombre. */ fd = open (argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); lseek (fd, FILE_LENGTH+1, SEEK_SET); write (fd, "", 1); lseek (fd, 0, SEEK_SET); /* Met en correspondance le fichier et la mmoire. */ file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0); close (fd); /* Ecrit un entier alatoire dans la zone mise en correspondance. */ sprintf((char*) file_memory, "%d\n", random_range (-100, 100)); /* Libre la mmoire (facultatif car le programme se termine). */ munmap (file_memory, FILE_LENGTH); return 0;

Le programme mmap-write ouvre le fichier, le crant s'il n'existe pas. Le second argument de open indique que le fichier est ouvert en lecture et criture. Comme nous ne connaissons pas la taille du fichier, nous utilisons lseek pour nous assurer qu'il est suffisamment grand pour stocker un entier puis nous nous replaons au dbut du fichier. Le programme met en correspondance le fichier et la mmoire puis ferme le fichier car il n'est plus utile. Il crit ensuite un entier alatoire dans la mmoire mappe, et donc dans le fichier, puis libre la mmoire. L'appel munmap n'est pas ncessaire car Linux supprimerait automatiquement la mise en correspondance la fin du programme. Lit un Entier Depuis un Fichier mis en correspondance avec la Mmoire et le Multiplie par Deux mmap-read.c

- 83 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Lit un Entier Depuis un Fichier mis en correspondance avec la Mmoire et le Multiplie par Deux mmap-read.c
#include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #define FILE_LENGTH 0x100

int main (int argc, char* const argv[]) { int fd; void* file_memory; int integer; /* Ouvre le fichier. */ fd = open (argv[1], O_RDWR, S_IRUSR /* Met en correspondance le fichier file_memory = mmap (0, FILE_LENGTH, MAP_SHARED, fd, close (fd); | S_IWUSR); et la mmoire. */ PROT_READ | PROT_WRITE, 0);

/* Lit l'entier, l'affiche et le multiplie par deux. */ sscanf (file_memory, "%d", &integer); printf ("valeur : %d\n", integer); sprintf ((char*) file_memory, "%d\n", 2 * integer); /* Libre la mmoire (facultatif car le programme se termine). */ munmap (file_memory, FILE_LENGTH); } return 0;

Le programme mmap-read lit le nombre partir du fichier puis y crit son double. Tout d'abord, il ouvre le fichier et le met en correspondance en lecture/criture. Comme nous pouvons supposer que le fichier est suffisamment grand pour stocker un entier non sign, nous n'avons pas besoin d'utiliser lseek, comme dans le programme prcdent. Le programme lit la valeur partir de la mmoire en utilisant sscanf puis formate et crit le double de la valeur en utilisant sprintf. Voici un exemple de l'excution de ces programmes d'exemple. Il utilise le fichier /tmp/integer-file.
% ./mmap-write /tmp/integer-file % cat /tmp/integer-file 42 % ./mmap-read /tmp/integer-file valeur : 42 % cat /tmp/integer-file 84

Remarquez que le texte 42 a t crit dans le fichier sur le disque sans jamais appeler write et t lu par la suite sans appeler read. Notez que ces programmes de dmonstration crivent et lisent l'entier sous forme de chane (en utilisant sprintf et sscanf) dans un but d'exemple uniquement ? il n'y a aucune raison pour que le contenu d'un fichier mis en correspondance avec la mmoire soit au format texte. Vous pouvez lire et crire de faon binaire dans un fichier mis en correspondance avec la mmoire.

5-3-3 - Accs Partag un Fichier


Des processus distincts peuvent communiquer en utilisant des rgions de la mmoire mises en correspondance avec le mme fichier. Passez le drapeau MAP_SHARED afin que toute criture dans une telle rgion soit immdiatement rpercute sur le disque et visible par les autres processus. Si vous n'indiquez pas ce drapeau, Linux pourrait placer les donnes crites dans un tampon avant de les transfrer dans le fichier.

- 84 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Une alternative est de forcer Linux intgrer les changements effectus en mmoire dans le fichier en appelant msync. Ses deux premiers paramtres dfinissent une rgion de la mmoire mise en correspondance avec un fichier, comme pour munmap. Le troisime paramtre peut prendre les valeurs suivantes: MS_ASYNC ? La mise jour est planifie mais pas ncessairement excute avant la fin de la fonction. MS_SYNC ? La mise jour est immdiate; l'appel msync est bloquant jusqu' ce qu'elle soit termine. MS_SYNC et MS_ASYNC ne peuvent tre utiliss simultanment. MS_INVALIDATE ? Toutes les autres mises en correspondance avec le fichier sont invalides afin de prendre en compte les modifications.

Par exemple, pour purger un fichier partag mis en correspondance l'adresse mem_addr et d'une longueur de mem_length octets, effectuez cet appel:
msync (mem_addr, mem_length, MS_SYNC | MS_INVALIDATE);

Comme pour les segments de mmoire partage, les utilisateurs de rgions de mmoire mises en correspondance avec un fichier doivent tablir et suivre un protocole afin d'viter les conditions de concurrence critique. Par exemple, un smaphore peut tre utilis pour viter que plus d'un processus n'accde la rgion de la mmoire en mme temps. Vous pouvez galement utiliser fcntl pour placer un verrou en lecture ou en criture sur le fichier, comme le dcrit la Section 8.3, fcntl : Verrous et Autres Oprations sur les Fichiers , du Chapitre 8.

5-3-4 - Mises en Correspondance Prives


Passer MAP_PRIVATE mmap cre une rgion en mode copie l'criture. Toute criture dans la rgion n'est rpercute que dans la mmoire du processus; les autres processus qui utilisent une mise en correspondance sur le mme fichier ne voient pas les modifications. Au lieu d'crire directement sur une page partage par tous les processus, le processus crit sur une copie prive de la page. Toutes les lectures et criture ultrieures du processus utilisent cette page.

5-3-5 - Autres Utilisations de mmap


L'appel mmap peut tre utilis dans d'autres buts que la communication interprocessus. Une utilisation courante est de le substituer read et write. Par exemple, plutt que de charger explicitement le contenu d'un fichier en mmoire, un programme peut mettre le fichier en correspondance avec la mmoire et l'analyser via des lectures en mmoire. Pour certains programmes, cette faon de faire est plus pratique et peut galement tre plus rapide que des oprations d'entres/sorties explicites sur le fichier. Une technique puissante utilise par certains programmes consiste fabriquer des structures de donnes (des instances struct ordinaires, par exemple) dans un fichier mis en correspondance avec la mmoire. Lors d'une invocation ultrieure, le programme remet le fichier en correspondance avec la mmoire et les structures de donnes retrouvent leur tat prcdent. Notez cependant que les pointeurs de ces structures de donnes seront invalides moins qu'ils ne pointent vers des adresses au sein de la mme rgion de mmoire et que le fichier soit bien mis en correspondance lamme adresse mmoire qu'initialement. Une autre technique utile est de mettre le fichier spcial /dev/zero en correspondance avec la mmoire. Ce fichier, dcrit dans la Section 6.5.2, /dev/zero , du Chapitre 6, Priphriques , se comporte comme s'il tait un fichier de taille infinie rempli d'octets zro. Un programme ayant besoin d'une source d'octets zro peut appeler mmap pour le fichier /dev/zero. Les critures sur /dev/zero sont ignores, donc la mmoire mise en correspondance peut tre utilise pour n'importe quelle opration. Les distributeurs de mmoire personnaliss utilisent souvent /dev/zero pour obtenir des portions de mmoire prinitialises.

- 85 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-4 - Tubes
Un tube est un dispositif de communication qui permet une communication sens unique. Les donnes crites sur l' extrmit d'criture du tube sont lues depuis l' extrmit de lecture . Les tubes sont des dispositifs squentiels; les donnes sont toujours lues dans l'ordre o elles ont t crites. Typiquement, un tube est utilis pour la communication entre deux threads d'un mme processus ou entre processus pre et fils. Dans un shell, le symbole | cre un tube. Par exemple, cette commande provoque la cration par le shell de deux processus fils, l'un pour ls et l'autre pour less:
% ls | less

Le shell cre galement un tube connectant la sortie standard du processus ls avec l'entre standard de less. Les noms des fichiers lists par ls sont envoys less dans le mme ordre que s'ils taient envoys directement au terminal. La capacit d'un tube est limite. Si le processus crivain crit plus vite que la vitesse laquelle le processus lecteur consomme les donnes, et si le tube ne peut pas contenir de donnes supplmentaires, le processus crivain est bloqu jusqu' ce qu'il y ait nouveau de la place dans le tube. Si le lecteur essaie de lire mais qu'il n'y a plus de donnes disponibles, il est bloqu jusqu' ce que ce ne soit plus le cas. Ainsi, le tube synchronise automatiquement les deux processus.

5-4-1 - Crer des Tubes


Pour crer un tube, appelez la fonction pipe. Passez lui un tableau de deux entiers. L'appel pipe stocke le descripteur de fichier en lecture l'indice zro et le descripteur de fichier en criture l'indice un. Par exemple, examinons ce code:
int pipe_fds[2]; int read_fd; int write_fd; pipe (pipe_fds); read_fd = pipe_fds[0]; write_fd = pipe_fds[1];

Les donnes crites via le descripteur write_fd peuvent tre relues via read_fd.

5-4-2 - Communication entre Processus Pre et Fils


Un appel pipe cre des descripteurs de fichiers qui ne sont valide qu'au sein du processus appelant et de ses fils. Les descripteurs de fichiers d'un processus ne peuvent tre transmis des processus qui ne lui sont pas lis; cependant, lorsqu'un processus appelle fork, les descripteurs de fichiers sont copis dans le nouveau processus. Ainsi, les tubes ne peuvent connecter que des processus lis. Dans le programme du Listing pipe, un fork cre un nouveau processus fils. Le fils hrite des descripteurs de fichiers du tube. Le pre crit une chane dans le tube et le fils la lit. Le programme exemple convertit ces descripteurs de fichiers en flux FILE* en utilisant fdopen. Comme nous utilisons des flux plutt que des descripteurs de fichiers, nous pouvons utiliser des fonctions d'entres/sorties de la bibliothque standard du C de plus haut niveau, comme printf et fgets.

- 86 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Utiliser un Tube pour Communiquer avec un Processus Fils pipe.c


#include <stdlib.h> #include <stdio.h> #include <unistd.h> /* crit COUNT fois entre chaque. */ MESSAGE vers STREAM, avec une pause d'une seconde

void writer (const char* message, int count, FILE* stream) { for (; count > 0; ?count) { /* crit le message vers le flux et purge immdiatement. */ fprintf (stream, "%s\n", message); fflush (stream); /* S'arrte un instant. */ sleep (1); } } /* Lit des chanes alatoires depuis le flux aussi longtemps que possible. */ void reader (FILE* stream) { char buffer[1024]; /* Lit jusqu' ce que l'on atteigne la fin du flux. fgets lit jusqu' ce qu'une nouvelle ligne ou une fin de fichier survienne. */ while (!feof (stream) && !ferror (stream) && fgets (buffer, sizeof (buffer), stream) != NULL) fputs (buffer, stdout); } int main () { int fds[2]; pid_t pid; /* Cre un tube. Les descripteurs de fichiers pour les deux bouts du tube sont placs dans fds. */ pipe (fds); /* Cre un processus fils. */ pid = fork (); if (pid == (pid_t) 0) { FILE* stream; /* Nous sommes dans le processus fils. On ferme notre copie de l'extrmit en criture du descripteur de fichiers. */ close (fds[1]); /* Convertit le descripteur de fichier de lecture en objet FILE et lit partir de celui-ci. */ stream = fdopen (fds[0], "r"); reader (stream); close (fds[0]); } else { /* Nous sommes dans le processus parent. */ FILE* stream; /* Ferme notre copie de l'extrmit en lecture du descripteur de fichier. */ close (fds[0]); /* Convertit le descripteur de fichier d'criture en objet FILE et y crit des donnes. */ stream = fdopen (fds[1], "w"); writer ("Coucou.", 5, stream); close (fds[1]); } } return 0;

- 87 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Au dbut de la fonction main, fds est dclar comme tant un tableau de deux entiers. L'appel pipe cre un tube et place les descripteurs en lecture et en criture dans ce tableau. Le programme cre alors un processus fils. Aprs avoir ferm l'extrmit en lecture du tube, le processus pre commence crire des chanes dans le tube. Aprs avoir ferm l'extrmit en criture du tube, le processus fils lit les chanes depuis le tube. Notez qu'aprs l'criture dans la fonction writer, le pre purge le tube en appelant fflush. Dans le cas contraire, les donnes pourraient ne pas tre envoyes dans le tube immdiatement. Lorsque vous invoquez la commande ls | less, deux divisions de processus ont lieu: une pour le processus fils ls et une pour le processus fils less. Ces deux processus hritent des descripteurs de fichier du tube afin qu'il puissent communiquer en l'utilisant. Pour faire communiquer des processus sans lien, utilisez plutt des FIFO, comme le dcrit la Section 5.4.5, FIFO.

5-4-3 - Rediriger les Flux d'Entre, de Sortie et d'Erreur Standards


Souvent, vous aurez besoin de crer un processus fils et de dfinir l'extrmit d'un tube comme son entre ou sa sortie standard. Grce la fonction dup2, vous pouvez substituer un descripteur de fichier un autre. Par exemple, pour rediriger l'entre standard vers un descripteur de fichier fd, utilisez ce code:
dup2 (fd, STDIN_FILENO);

La constante symbolique STDIN_FILENO reprsente le descripteur de fichier de l'entre standard, qui a la valeur~0. L'appel ferme l'entre standard puis la rouvre comme une copie de fd de faon ce que les deux puissent tre utiliss indiffremment. Les descripteurs de fichiers substitus partagent la mme position dans le fichier et le mme ensemble d'indicateurs de statut de fichier. Ainsi, les caractres lus partir de fd ne sont pas relus partir de l'entre standard. Le programme du Listing dup2 utilise dup2 pour envoyer des donne d'un tube vers la commande sort(sort lit des lignes de texte depuis l'entre standard, les trie par ordre alphabtique et les affiche sur la sortie standard.). Une fois le tube cr, le programme se divise. Le processus parent envoie quelques chanes vers le tube. Le processus fils connecte le descripteur de fichier en lecture du tube son entre standard en utilisant dup2. Il excute ensuite le programme sort. Rediriger la Sortie d'un Tube avec dup2 dup2.c
#include #include #include #include <stdio.h> <sys/types.h> <sys/wait.h> <unistd.h>

int main () { int fds[2]; pid_t pid; /* Cre un tube. Les descripteurs de fichiers des deux extrmits du tube sont places dans fds. */ pipe (fds); /* Cre un processus fils. */ pid = fork (); if (pid == (pid_t) 0) { /* Nous sommes dans le processus fils. On ferme notre copie du descripteur de fichier en criture. */ close (fds[1]); /* Connexion de l'extrmit en lecture l'entre standard. */ dup2 (fds[0], STDIN_FILENO); /* Remplace le processus fils par le programme "sort". */ execlp ("sort", "sort", 0); } else {

- 88 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Rediriger la Sortie d'un Tube avec dup2 dup2.c

} }

/* Processus pre. */ FILE* stream; /* Ferme notre copie de l'extrmit en lecture du descripteur. */ close (fds[0]); /* Convertit le descripteur de fichier en criture en objet FILE, et y crit. */ stream = fdopen (fds[1], "w"); fprintf (stream, "C'est un test.\n"); fprintf (stream, "Coucou.\n"); fprintf (stream, "Mon chien a des puces.\n"); fprintf (stream, "Ce programme est excellent.\n"); fprintf (stream, "Un poisson, deux poissons.\n"); fflush (stream); close (fds[1]); /* Attend la fin du processus fils. */ waitpid (pid, NULL, 0);

return 0;

5-4-4 - popen et pclose


Une utilisation courante des tubes est d'envoyer ou de recevoir des donnes depuis un programme en cours d'excution dans un sous-processus. Les fonctions popen et pclose facilitent cette pratique en liminant le besoin d'appeler pipe, fork, dup2, exec et fdopen. Comparez le Listing popen, qui utilise popen et pclose, l'exemple prcdent (Listing dup2). Exemple d'Utilisation de popen popen.c
#include <stdio.h> #include <unistd.h> int main () { FILE* stream = popen ("sort", "w"); fprintf (stream, "C'est un test.\n"); fprintf (stream, "Coucou.\n"); fprintf (stream, "Mon chien a des puces.\n"); fprintf (stream, "Ce programme est excellent.\n"); fprintf (stream, "Un poisson, deux poissons.\n"); return pclose (stream); }

L'appel popen cre un processus fils excutant la commande sort, ce qui remplace les appels pipe, fork, dup2 et execlp. Le second argument, "w", indique que ce processus dsire crire au processus fils. La valeur de retour de popen est l'extrmit d'un tube; l'autre extrmit est connecte l'entre standard du processus fils. Une fois que le processus pre a termin d'crire, pclose ferme le flux du processus fils, attend la fin du processus et renvoie son code de retour. Le premier argument de popen est excut comme s'il s'agissait d'une commande shell, dans un processus excutant /bin/sh. Le shell recherche les programmes excuter en utilisant la variable d'environnement PATH de la faon habituelle. Si le deuxime argument est "r", la fonction renvoie le flux de sortie standard du processus fils afin que le pre puisse lire la sortie. Si le second argument est "w", la fonction renvoie le flux d'entre standard du processus fils afin que le pre puisse envoyer des donnes. Si une erreur survient, popen renvoie un pointeur nul. Appelez pclose pour fermer un flux renvoyer par popen. Aprs avoir ferm le flux indiqu, pclose attend la fin du processus fils.

- 89 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-4-5 - FIFO
Une file premier entr, premier sorti (first-in, first-out, FIFO) est un tube qui dispose d'un nom dans le systme de fichiers. Tout processus peut ouvrir ou fermer la FIFO; les processus raccords aux extrmits du tube n'ont pas avoir de lien de parent. Les FIFO sont galement appels canaux nomms. Vous pouvez crer une FIFO via la commande mkfifo. Indiquez l'emplacement o elle doit tre cre sur la ligne de commande. Par exemple, crez une FIFO dans /tmp/fifo en invoquant ces commandes:
% mkfifo /tmp/fifo % ls -l /tmp/fifo prw-rw-rw1 samuel users 0 Jan 16 14:04 /tmp/fifo

Le premier caractre affich par ls est p ce qui indique que le fichier est en fait une FIFO (canal nomm, named pipe). Dans une fentre, lisez des donnes depuis la FIFO en invoquant cette commande:
% cat < /tmp/fifo

Dans une deuxime fentre, crivez dans la FIFO en invoquant cela:


% cat > /tmp/fifo

Puis, saisissez du texte. chaque fois que vous appuyez sur Entre, la ligne de texte est envoy dans la FIFO et apparat dans la premire fentre. Fermez la FIFO en appuyant sur Ctrl+D dans la seconde fentre. Supprimez la FIFO avec cette commande:
% rm /tmp/fifo

5-4-5-1 - Crer une FIFO


Pour crer une FIFO par programmation, utilisez la fonction mkfifo. Le premier argument est l'emplacement o crer la FIFO; le second paramtre spcifie les permissions du propritaire du tube, de son groupe et des autres utilisateurs, comme le dcrit le Chapitre 10, Scurit , Section 10.3, Permissions du Systme de Fichiers . Comme un tube doit avoir un lecteur et un crivain, les permissions doivent comprendre des autorisations en lecture et en criture. Si le tube ne peut pas tre cr (par exemple, si un fichier possdant le mme nom existe dj), mkfifo renvoie -1. Incluez <sys/types.h> et <sys/stat.h> si vous appelez mkfifo.

5-4-5-2 - Accder une FIFO


L'accs une FIFO se fait de la mme faon que pour un fichier ordinaire. Pour communiquer via une FIFO, un programme doit l'ouvrir en criture. Il est possible d'utiliser des fonction d'E/S de bas niveau (open, write, read, close, etc. listes dans l'Appendice B, E/S de Bas Niveau ) ou des fonctions d'E/S de la bibliothque C (fopen, fprintf, fscanf, fclose, etc.). Par exemple, pour crire un tampon de donnes dans une FIFO en utilisant des routines de bas niveau, vous pourriez procder comme suit:
int fd = open (fifo_path, O_WRONLY); write (fd, data, data_length); close (fd);

- 90 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour lire une chane depuis la FIFO en utilisant les fonctions d'E/S de la bibliothque C, vous pourriez utiliser ce code:
FILE* fifo = fopen (fifo_path, "r"); fscanf (fifo, "%s", buffer); fclose (fifo);

Une FIFO peut avoir plusieurs lecteurs ou plusieurs crivains. Les octets de chaque crivain sont crits de faon atomique pour une taille infrieure PIPE_BUF (4Ko sous Linux). Les paquets d'crivains en accs simultans peuvent tre entrelacs. Les mmes rgles s'appliquent des lectures concurrentes.

5-4-5-3 - Diffrences avec les Canaux Nomms Windows


Les tubes des systmes d'exploitation Win32 sont trs similaires aux tubes Linux (consultez la documentation de la bibliothque Win32 pour plus de dtails techniques). Les principales diffrences concernent les canaux nomms, qui, sous Win32, fonctionnent plus comme des sockets. Les canaux nomms Win32 peuvent connecter des processus d'ordinateurs distincts connects via un rseau. Sous Linux, ce sont les sockets qui sont utiliss pour ce faire. De plus, Win32 permet de multiples connexions de lecteur crivain sur un mme canal nomm sans que les donnes ne soient entrelaces et les tubes peuvent tre utiliss pour une communication double sens(Notez que seul Windows NT permet la cration de canaux nomms; les programmes pour Windows 9x ne peuvent crer que des connexions client.).

5-5 - Sockets
Un socket est un dispositif de communication bidirectionnel pouvant tre utilis pour communiquer avec un autre processus sur la mme machine ou avec un processus s'excutant sur d'autres machines. Les sockets sont la seule forme de communication interprocessus dont nous traiterons dans ce chapitre qui permet la communication entre processus de diffrentes machines. Les programmes Internet comme Telnet, rlogin, FTP, talk et le World Wide Web utilisent des sockets. Par exemple, vous pouvez obtenir une page depuis un serveur Web en utilisant le programme Telnet car tous deux utilisent des sockets pour la communication via le rseau(Habituellement, vous utilisez telnet pour vous connecter un serveur Telnet pour une identification distance. Mais vous pouvez galement utiliser telnet pour vous connecter un autre type de serveur et lui envoyer des commandes directement.). Pour ouvrir une connexion vers un serveur Web dont l'adresse est www.codesourcery.com, utilisez la commande telnet www.codesourcery.com 80. La constante magique 80 demande une connexion au serveur Web de http://www.codesourcery.com plutt qu' un autre processus. Essayez d'entrer GET / une fois la connexion tablie. Cela envoie un message au serveur Web travers le socket, celui-ci rpond en envoyant la source HTML de la page d'accueil puis en fermant la connexion ? par exemple:
% telnet www.codesourcery.com 80 Trying 206.168.99.1... Connected to merlin.codesourcery.com (206.168.99.1). Escape character is "^]". GET / <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> ...

5-5-1 - Concepts relatifs aux Sockets


Lorsque vous crez un socket, vous devez indiquer trois paramtres: le style de communication, l'espace de nommage et le protocole. Un style de communication contrle la faon dont le socket traite les donnes transmises et dfinit le nombre d'interlocuteurs. Lorsque des donnes sont envoyes via le socket, elles sont dcoupes en morceaux appels
- 91 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

paquets. Le style de communication dtermine comment sont grs ces paquets et comment ils sont envoys de l'metteur vers le destinataire. Le style connexion garantit la remise de tous les paquets dans leur ordre d'mission. Si des paquets sont perdus ou mlangs cause de problmes dans le rseau, le destinataire demande automatiquement leur retransmission l'metteur. Un socket de type connexion ressemble un appel tlphonique: les adresses de l'metteur et du destinataire sont fixes au dbut de la communication lorsque la connexion est tablie. Le style datagramme ne garantit pas la remise ou l'ordre d'arrive des paquets. Des paquets peuvent tre perdus ou mlangs cause de problmes dans le rseau. Chaque paquet doit tre associ sa destination et il n'y a aucune garantie quant sa remise. Le systme ne garantit que le meilleur effort (best effort), des paquets peuvent donc tre perdus ou tre remis dans un ordre diffrent de leur mission. Un socket de style datagramme se comporte plus comme une lettre postale. L'metteur spcifie l'adresse du rcepteur pour chaque message.

L'espace de nommage d'un socket spcifie comment les adresses de socket sont crites. Une adresse de socket identifie l'extrmit d'une connexion par socket. Par exemple, les adresses de socket dans l'espace de nommage local sont des noms de fichiers ordinaires. Dans l'espace de nommage Internet , une adresse de socket est compose de l'adresse Internet (galement appele adresse IP) d'un hte connect au rseau et d'un numro de port. Le numro de port permet de faire la distinction entre plusieurs sockets sur le mme hte. Un protocole spcifie comment les donnes sont transmises. Parmi ces protocoles, on peut citer TCP/IP; les deux protocoles principaux utiliss pour Internet, le protocole rseau AppleTalk; et le protocole de communication locale d'UNIX. Toutes les combinaisons de styles, d'espace de nommage et de protocoles ne sont pas supportes.

5-5-2 - Appels Systme


Les sockets sont plus flexibles que les techniques de communication cites prcdemment. Voici les appels systmes utiles lors de l'utilisation de sockets: socket ? Cre un socket. close ? Dtruit un socket. connect ? Cre une connexion entre deux sockets. bind ? Associe un socket serveur une adresse. listen ? Configure un socket afin qu'il accepte les connexions. accept ? Accepte une connexion et cre un nouveau socket pour celle-ci.

Les sockets sont reprsents par des descripteurs de fichiers.

5-5-2-1 - Crer et Dtruire des Sockets


Les fonctions socket et close crent et dtruisent des sockets, respectivement. Lorsque vous crez un socket, spcifiez ses trois proprits: espace de nommage, style de communication et protocole. Pour le paramtre d'espace de nommage, utilisez les constantes commenant par PF_ (abrviation de protocol family , famille de protocoles). Par exemple, PF_LOCAL ou PF_UNIX spcifient l'espace de nommage local et PF_INET correspond l'espace de nommage Internet. Pour le paramtre du style de communication, utilisez les constantes commenant par SOCK_. SOCK_STREAM demande un socket de style connexion, SOCK_DGRAM un socket de style datagramme. Le troisime paramtre, le protocole, spcifie le mcanisme de bas niveau utilis pour transmettre et recevoir des donnes. Chaque protocole est valide pour une combinaison d'espace de nommage et de type de communication particulire. Comme il y a souvent un protocole adapt pour chaque paire, indiquer 0 slectionne gnralement le bon protocole. Si l'appel socket se droule correctement, il renvoie un descripteur de fichier pour le socket. Vous pouvez lire ou crire sur un socket en utilisant read, write, etc. comme avec les autres descripteurs de fichiers. Lorsque vous en avez fini avec le socket, appelez close pour le supprimer.

- 92 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

5-5-2-2 - Appeler connect


Pour crer une connexion entre deux sockets, le client appelle connect, en lui passant l'adresse d'un socket serveur auquel se connecter. Un client est un processus initiant une connexion et un serveur est un processus en attente de connexions. Le client appelle connect pour initier une connexion depuis un socket local, dont le descripteur est pass en premier argument, vers le socket serveur spcifi par le deuxime argument. Le troisime argument est la longueur, en octets, de la structure d'adresse pointe par le second argument. Les formats d'adresse de sockets diffrent selon l'espace de nommage du socket.

5-5-2-3 - Envoyer des Informations


Toutes les techniques valides pour crire dans un descripteur de fichier le sont galement pour crire dans un socket. Reportez-vous l'Appendice B pour une prsentation des fonctions d'E/S de bas niveau Linux et de certains des problmes ayant trait leur utilisation. La fonction send, qui est spcifique aux descripteur de fichiers socket, est une alternative write avec quelques choix supplmentaires; reportez-vous sa page de manuel pour plus d'informations.

5-5-3 - Serveurs
Le cycle de vie d'un serveur consiste crer un socket de type connexion, le lier une adresse, appeler listen pour permettre au socket d'accepter des connexions, appeler accept rgulirement pour accepter les connexions entrantes, puis fermer le socket. Les donnes ne sont pas lues et crites directement via le socket serveur; au lieu de cela, chaque fois qu'un programme accepte une nouvelle connexion, Linux cre un socket spar utilis pour le transfert de donnes via cette connexion. Dans cette section, nous introduirons bind, listen et accept. Une adresse doit tre lie au socket serveur en utilisant bind afin que les clients puissent le trouver. Son premier argument est le descripteur de fichier du socket. Le second argument est un pointeur vers une structure d'adresse de socket; son format dpend de la famille d'adresse du socket. Le troisime argument est la longueur de la structure d'adresse en octets. Une fois qu'une adresse est lie un socket de type connexion, il doit invoquer listen pour indiquer qu'il agit en tant que serveur. Son premier argument est le descripteur de fichier du socket. Le second spcifie combien de connexions peuvent tre mise en file d'attente. Si la file est pleine, les connexions supplmentaires seront refuses. Cela ne limite pas le nombre total de connexions qu'un serveur peut grer; cela limite simplement le nombre de clients tentant de se connecter qui n'ont pas encore t accepts. Un serveur accepte une demande de connexion d'un client en invoquant accept. Son premier argument est le descripteur de fichier du socket. Le second pointe vers une structure d'adresse de socket, qui sera renseigne avec l'adresse de socket du client. Le troisime argument est la longueur, en octets, de la structure d'adresse de socket. Le serveur peut utiliser l'adresse du client pour dterminer s'il dsire rellement communiquer avec le client. L'appel accept cre un nouveau socket pour communiquer avec le client et renvoie le descripteur de fichier correspondant. Le socket serveur original continue accepter de nouvelles connexions de clients. Pour lire des donnes depuis un socket sans le supprimer de la file d'attente, utilisez recv. Cette fonction prend les mmes arguments que read ainsi qu'un argument FLAGS supplmentaire. Passer la valeur MSG_PEEK permet de lire les donnes sans les supprimer de la file d'attente.

5-5-4 - Sockets Locaux


Les sockets mettant en relation des processus situs sur le mme ordinateur peuvent utiliser l'espace de nommage local reprsent par les constantes PF_LOCAL et PF_UNIX. Ils sont appels sockets locaux ou sockets de domaine UNIX. Leur adresse de socket, un nom de fichier, n'est utilise que lors de la cration de connexions. Le nom du socket est spcifi dans une struct sockaddr_un. Vous devez positionner le champ sun_family AF_LOCAL, qui reprsente un espace de nommage local. Le champ sun_path spcifie le nom de fichier utiliser et peut faire au plus 108 octets de long. La longueur relle de la struct sockaddr_un doit tre calcule en utilisant la macro SUN_LEN. Tout nom de fichier peut tre utilis, mais le processus doit avoir des autorisations d'criture sur le rpertoire, qui permettent l'ajout de fichiers. Pour se connecter un socket, un processus doit avoir des droits

- 93 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

en lecture sur le fichier. Mme si diffrents ordinateurs peuvent partager le mme systme de fichier, seuls des processus s'excutant sur le mme ordinateur peuvent communiquer via les sockets de l'espace de nommage local. Le seul protocole permis pour l'espace de nommage local est 0. Comme il est stock dans un systme de fichiers, un socket local est affich comme un fichier. Par exemple, remarquez le s du dbut:
% ls -l /tmp/socket srwxrwx?x 1 user group 0 Nov 13 19:18 /tmp/socket

Appelez unlink pour supprimer un socket local lorsque vous ne l'utilisez plus.

5-5-5 - Un Exemple Utilisant les Sockets Locaux


Nous allons illustrer l'utilisation des sockets au moyen de deux programmes. Le programme serveur, Listing socketserver, cre un socket dans l'espace de nommage local et attend des connexions. Lorsqu'il reoit une connexion, il lit des messages au format texte depuis celle-ci et les affiche jusqu' ce qu'elle soit ferme. Si l'un de ces messages est quit , le programme serveur supprime le socket et se termine. Le programme socket-server prend en argument de ligne de commande le chemin vers le socket. Serveur Utilisant un Socket Local socket-server.c
#include #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <sys/socket.h> <sys/un.h> <unistd.h>

/* Lit du texte depuis le socket et l'affiche. Continue jusqu' ce que le socket soit ferm. Renvoie une valeur diffrente de zro si le client a envoy un message "quit", zro sinon. */ int server (int client_socket) { while (1) { int length; char* text; /* Commence par lire la longueur du message texte depuis le socket. Si read renvoie zro, le client a ferm la connexion. */ if (read (client_socket, &length, sizeof (length)) == 0) return 0; /* Alloue un tampon pour contenir le texte. */ text = (char*) malloc (length); /* Lit le texte et l'affiche. */ read (client_socket, text, length); printf ("%s\n", text); /* Si le client a envoy le message "quit", c'est fini. if (!strcmp (text, "quit")) { /* Libre le tampon. */ free (text); return 1; } /* Libre le tampon. */ free (text);

*/

int main (int argc, char* const argv[]) { const char* const socket_name = argv[1]; - 94 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Serveur Utilisant un Socket Local socket-server.c


int socket_fd; struct sockaddr_un name; int client_sent_quit_message;

/* Cre le socket. */ socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0); /* Indique qu'il s'agit d'un serveur. */ name.sun_family = AF_LOCAL; strcpy (name.sun_path, socket_name); bind (socket_fd, (struct sockaddr *) &name, SUN_LEN (&name)); /* Se met en attente de connexions. */ listen (socket_fd, 5); /* Accepte les connexions de faon rpte, lance un server() pour traiter chaque client. Continue jusqu' ce qu'un client envoie un message "quit". */ do { struct sockaddr_un client_name; socklen_t client_name_len; int client_socket_fd; /* Accepte une connexion. */ client_socket_fd = accept (socket_fd, (struct sockaddr *) &client_name, &client_name_len); /* Traite la connexion. */ client_sent_quit_message = server (client_socket_fd); /* Ferme notre extrmit. */ close (client_socket_fd);

} while (!client_sent_quit_message); /* Supprime le fichier socket. */ close (socket_fd); unlink (socket_name); } return 0;

Le programme client, Listing socketclient, se connecte un socket local et envoie un message. Le chemin vers le socket et le message sont indiqus sur la ligne de commande. Client Utilisant un Socket Local socket-client.c
#include #include #include #include #include <stdio.h> <string.h> <sys/socket.h> <sys/un.h> <unistd.h>

/* crit TEXT vers le socket indiqu par le descripteur SOCKET_FD. */ void write_text (int socket_fd, const char* text) { /* crit le nombre d'octets de la chane, y compris l'octet nul de fin. */ int length = strlen (text) + 1; write (socket_fd, &length, sizeof (length)); /* crit la chane. */ write (socket_fd, text, length); } int main (int argc, char* const argv[]) { const char* const socket_name = argv[1]; const char* const message = argv[2]; int socket_fd; struct sockaddr_un name; /* Cre le socket. */ - 95 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Client Utilisant un Socket Local socket-client.c

socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0); /* Stocke le nom du serveur dans l'adresse du socket. */ name.sun_family = AF_LOCAL; strcpy (name.sun_path, socket_name); /* Se connecte au socket. */ connect (socket_fd, (struct sockaddr*) &name, SUN_LEN (&name)); /* crit le texte de la ligne de commande vers le socket. */ write_text (socket_fd, message); close (socket_fd); return 0;

Avant que le client n'envoie le message, il envoie sa longueur contenue dans la variable entire length. De mme, le serveur lit la longueur du texte en lisant une variable entire depuis le socket. Cela permet au serveur d'allouer un tampon de taille adquate pour contenir le message avant de le lire. Pour tester cet exemple, dmarrez le programme serveur dans une fentre. Indiquez le chemin vers le socket ? par exemple, /tmp/socket.
% ./socket-server /tmp/socket

Dans une autre fentre, lancez plusieurs fois le client en spcifiant le mme socket ainsi que des messages envoyer au client:
% ./socket-client /tmp/socket "Coucou." % ./socket-client /tmp/socket "Ceci est un test."

Le programme serveur reoit et affiche les messages. Pour fermer le serveur, envoyez le message quit depuis un client:
% ./socket-client /tmp/socket "quit"

Le programme serveur se termine alors.

5-5-6 - Sockets Internet


Les sockets de domaine UNIX ne peuvent tre utiliss que pour communiquer entre deux processus s'excutant sur le mme ordinateur. Les sockets Internet, par contre, peuvent tre utiliss pour connecter des processus s'excutant sur des machines distinctes connectes par un rseau. Les sockets connectant des processus via Internet utilisent l'espace de nommage Internet reprsent par PF_INET. Le protocole le plus courant est TCP/IP. L'Internet Protocol (IP), un protocole de bas niveau transporte des paquets sur Internet, en les fragmentant et les rassemblant si ncessaire. Il ne garantit que le meilleur effort de remise, des paquets peuvent donc disparatre ou tre mlangs durant le transport. Tous les ordinateurs participants sont dfinis en utilisant une adresse IP unique. Le Transmission Control Protocol (TCP), qui s'appuie sur IP, fournit un transport fiable orient connexion. Il permet d'tablir des connexions semblables aux connexions tlphoniques entre des ordinateurs et assure que les donnes sont remises de faon fiable et dans l'ordre. Comme il est plus facile de se souvenir de noms que de numros, le Domain Name Service (DNS, Service de Nom de Domaine) associe un nom comme www.codesourcery.com l'adresse IP d'un ordinateur. Le DNS est implment par une hirarchie mondiale de serveurs de noms, mais vous n'avez pas besoin de comprendre les protocoles employs par le DNS pour utiliser des noms d'htes Internet dans vos programmes.

- 96 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Les adresses de sockets Internet comprennent deux parties: un numro de machine et un numro de port. Ces informations sont stockes dans une variable de type struct sockaddr_in. Positionnez le champ sin_family AF_INET pour indiquer qu'il s'agit d'une adresse de l'espace de nommage Internet. Le champ sin_addr stocke l'adresse Internet de la machine cible sous forme d'une adresse IP entire sur 32 bits. Un numro de port permet de faire la distinction entre plusieurs sockets d'une machine donne. Comme des machines distinctes peuvent stocker les octets de valeurs multioctets dans des ordres diffrents, utilisez htons pour reprsenter le numro de port dans l'ordre des octets dfini par le rseau. Consultez la page de manuel de ip pour plus d'informations. Pour convertir des noms d'htes, des adresses en notation pointe (comme 10.0.0.1) ou des noms DNS (comme www.codesourcery.com) adresse IP sur 32 bits, vous pouvez utiliser gethostbyname. Cette fonction renvoie un pointeur vers une structure struct hostent; le champ h_addr contient l'IP de l'hte. Reportez-vous au programme exemple du Listing socketinet. Le Listing socketinet illustre l'utilisation de sockets de domaine Internet. Le programme rapatrie la page d'accueil du serveur Web dont le nom est pass sur la ligne de commande. Lecture partir d'un Serveur WWW socket-inet.c
#include #include #include #include #include #include #include <stdlib.h> <stdio.h> <netinet/in.h> <netdb.h> <sys/socket.h> <unistd.h> <string.h>

/* Affiche le contenu de la page d'accueil du serveur correspondant au socket. */ void get_home_page (int socket_fd) { char buffer[10000]; ssize_t number_characters_read; /* Envoie la commande HTTP GET pour la page d'accueil. */ sprintf (buffer, "GET /\n"); write (socket_fd, buffer, strlen (buffer)); /* Lit partir du socket. L'appel read peut ne pas renvoyer toutes les donnes en une seule fois, on continue de lire jusqu' ce qu'il n'y ait plus rien. */ while (1) { number_characters_read = read (socket_fd, buffer, 10000); if (number_characters_read == 0) return; /* Ecrit les donnes vers la sortie standard. */ fwrite (buffer, sizeof (char), number_characters_read, stdout); }

int main (int argc, char* const argv[]) { int socket_fd; struct sockaddr_in name; struct hostent* hostinfo; /* Cre le socket. */ socket_fd = socket (PF_INET, SOCK_STREAM, 0); /* Place le nom du serveur dans l'adresse du socket. */ name.sin_family = AF_INET; /* Convertit la chane en adresse IP sur 32 bits. */ hostinfo = gethostbyname (argv[1]); if (hostinfo == NULL) return 1; else name.sin_addr = *((struct in_addr *) hostinfo->h_addr); /* Les serveurs Web utilisent le port 80. */ name.sin_port = htons (80); - 97 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Lecture partir d'un Serveur WWW socket-inet.c


/* Se connecte au serveur Web. */ if (connect (socket_fd, (struct sockaddr*) &name, sizeof (struct sockaddr_in)) == -1) { perror ("connect"); return 1; } /* Rcupre la page d'accueil du serveur. */ get_home_page (socket_fd); return 0;

Ce programme lit le nom du serveur Web partir de la ligne de commande (pas l'URL ? c'est--dire sans le http: %%%% ). Il appelle gethostbyname pour traduire le nom d'hte en adresse IP numrique puis connecte un socket de type connexion (TCP) au port 80 de cet hte. Les serveurs Web parlent l'Hypertext Transport Protocol// (HTTP), donc le programme met la commande HTTP GET et le serveur rpond en envoyant le texte correspondant la page d'accueil. Par convention, les serveurs Web attendent des connexions sur le port 80. La plupart des services rseau Internet sont associs un numro de port standard. Par exemple, les serveurs Web scuriss utilisant SSL attendent les connexions sur le port 443 et les serveurs de mail (qui parlent SMTP) utilisent le port 25. Sur les systmes GNU/Linux, les associations entre les noms des services, les protocoles et les numros de port standards sont lists dans le fichier /etc/services. La premire colonne est le nom du protocole ou du service. La seconde colonne indique le numro de port et le type de connexion: tcp pour le mode orient connexion et udp pour le mode datagramme. Si vous implmentez des services rseau personnaliss utilisant des sockets Internet, utilisez des numros de ports suprieurs 1024. Par exemple, pour rapatrier la page d'accueil du site Web www.codesourcery.com, invoquez la commande suivante:
% ./socket-inet www.codesourcery.com <html> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> ...

5-5-7 - Couples de Sockets


Comme nous l'avons vu prcdemment, la fonction pipe cre deux descripteurs de fichiers chacune tant l'extrmit d'un tube. Les tubes sont limits car les descripteurs de fichiers doivent tre utiliss par des processus lis et car la communication est unidirectionnelle. La fonction socketpair cre deux descripteurs de fichiers pour deux sockets connects sur le mme ordinateur. Ces descripteurs de fichiers permettent une communication double sens entre des processus lis. Ses trois premiers paramtres sont les mmes que pour l'appel socket: ils indiquent le domaine, le type de connexion et le protocole. Le dernier paramtre est un tableau de deux entiers, qui sera renseign avec les descripteurs de fichiers des deux sockets, comme pour pipe. Lorsque vous appelez socketpair, vous devez utiliser PF_LOCAL comme domaine.

- 98 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

6 - Priphriques
Linux, comme la plupart des systmes d'exploitation, interagit avec les priphriques matriels via des composants logiciels modulaires appels pilotes de priphriques. Un pilote masque les particularits des protocoles de communication utiliss par un dispositif matriel au systme d'exploitation et lui permet d'interagir avec le priphrique par le biais d'une interface standardise. Sous Linux, les pilotes de priphriques font partie du noyau et peuvent tre intgrs de faon statique celui-ci ou chargs la demande sous forme de modules. Les pilotes de priphriques s'excutent comme s'ils faisaient partie du noyau et ne sont pas accessibles directement aux processus utilisateur. Cependant, Linux propose un mcanisme ces processus pour communiquer avec un pilote ? et par l mme avec le dispositif matriel ? via des objets semblables aux fichiers. Ces objets apparaissent dans le systme de fichiers et des applications peuvent les ouvrir, les lire et y crire pratiquement comme s'il s'agissait de fichiers normaux. Vos programmes peuvent donc communiquer avec des dispositifs matriels via des objets semblables aux fichiers soit en utilisant les oprations d'E/ S de bas niveau de Linux (consultez l'Appendice B, E/S de Bas Niveau ), soit les oprations de la bibliothque d'E/S standard du C. Linux fournit galement plusieurs objets semblables des fichiers qui communiquent directement avec le noyau plutt qu'avec des pilotes de priphriques. Ils ne sont pas lis des dispositifs matriels; au lieu de cela, ils fournissent diffrents types de comportements spcialiss qui peuvent tre utiles aux applications et aux programmes systmes. Les techniques prsentes dans ce chapitre fournissent un accs direct aux pilotes de priphriques s'excutant au sein du noyau Linux, et travers eux aux dispositifs matriels connects au systme. Utilisez ces techniques avec prudence car une mauvaise manipulation peut altrer ou endommager le systme GNU/Linux. Lisez notamment le cadre Danger des Priphriques Blocs .

6-1 - Types de Priphriques


Les fichiers de priphriques ne sont pas des fichiers ordinaires ? ils ne reprsentent pas des zones de donnes au sein d'un systme de fichiers sur disque. Au lieu de cela, les donnes lues ou crites sur un fichier de priphrique sont transmises au pilote de priphrique correspondant et, par son intermdiaire, au matriel sous-jacent. Les fichiers de priphriques se divisent en deux types: Un priphrique caractre reprsente un dispositif matriel qui lit ou crit en srie un flux d'octets. Les ports srie et parallle, les lecteurs de cassettes, les terminaux et les cartes son sont des exemples de priphriques caractres. Un priphrique bloc reprsente un dispositif matriel qui lit ou crit des donnes sous forme de blocs de taille fixe. Contrairement aux priphriques caractre, un priphrique bloc fournit un accs direct aux donnes stockes sur le priphrique. Un lecteur de disque est un exemple de priphrique bloc.

Les programmes traditionnels n'utiliseront jamais de priphriques blocs. Bien qu'un lecteur de disque soit reprsent comme un priphrique matriel, le contenu de chaque partition contient habituellement un systme de fichiers mont sur l'arborescence racine de GNU/Linux. Seul le code du noyau qui implmente le systme de fichiers a besoin d'accder au priphrique bloc directement; les programmes d'application accdent au contenu du disque via des fichiers et des rpertoires normaux. Nanmoins, les applications utilisent quelquefois les priphriques caractre. Nous traiterons de plusieurs d'entre eux dans les sections suivantes. Les priphriques bloc offrent un accs direct aux donnes du lecteur de disque. Bien que la plupart des systmes GNU/Linux soient configurs pour interdire aux processus non root d'accder directement ces priphriques, un processus root peut causer des dommages svres en changeant le contenu du disque. En crivant sur un priphrique bloc correspondant un disque, un programme peut modifier ou dtruire les informations
- 99 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

de contrle du systme de fichier et mme la table des partitions d'un disque et son secteur de dmarrage, rendant ainsi le lecteur, ou mme tout le systme, inutilisable. Accdez toujours ces priphriques avec la plus grande prudence.

6-2 - Numros de Priphrique


Linux identifie les priphriques au moyen de deux nombres: le numro de priphrique majeur et le numro de priphrique mineur. Le numro de priphrique majeur indique quel pilote correspond le priphrique. Les correspondances entre les numros de priphrique majeurs et les pilotes sont fixes et dfinies dans les sources du noyau Linux. Notez qu'un mme numro de priphrique majeur peut correspondre deux pilotes diffrents, l'un tant un priphrique caractre et l'autre un priphrique bloc. Les numros de priphrique mineurs permettent de distinguer plusieurs priphriques ou composants contrls par le mme pilote. Par exemple, le priphrique de numro majeur~3 correspond au contrleur IDE primaire du systme. Un contrleur IDE peut tre connect deux priphriques (lecteur de disque, cassette ou CD-ROM); le priphrique matre a le numro mineur~0 et le priphrique esclave a le numro mineur~64. les partitions du priphrique matre (s'il supporte les partitions) ont les numros 1, 2, 3, etc. Les partitions du priphrique esclave sont reprsentes par les numros de priphrique mineurs 65, 66, 67, etc. Les numros de priphrique majeurs sont rpertoris dans la documentation des sources du noyau Linux. Sur beaucoup de distributions GNU/Linux, ils sont dcrits dans le fichier /usr/src/linux/Documentation/devices.txt. Le fichier spcial /proc/devices dresse la liste des numros de priphrique majeurs correspondant aux pilotes de priphriques actuellement chargs dans le noyau (consultez le Chapitre 7, le Systme de Fichiers /proc pour plus d'informations sur les entres du systme de fichiers /proc).

6-3 - Fichiers de Priphriques


Un fichier de priphrique ressemble beaucoup un fichier classique. Vous pouvez le dplacer en utilisant la commande mv et le supprimer avec rm. Si vous essayez de copier un priphrique en utilisant cp, par contre, vous lirez des octets partir de celui-ci (s'il le supporte) et les crirez vers un fichier de destination. Si vous essayez d'craser un fichier de priphrique, vous crirez des octets vers le priphrique concern. Vous pouvez crer un fichier de priphrique en utilisant la commande mknod (saisissez man~1~mknod pour obtenir la page de manuel) ou l'appel systme mknod (man~2~mknod pour la page de manuel). Crer un fichier de priphrique n'implique pas automatiquement que le pilote ou le dispositif matriel soit prsent ou disponible; le fichier de priphrique est en quelque sort un portail pour communiquer avec le pilote, s'il est prsent. Seul les processus superutilisateur peuvent crer des priphriques bloc et caractre via la commande ou l'appel systme mknod. Pour crer un priphrique en utilisant la commande mknod, spcifiez le chemin du fichier le reprsentant comme premier argument de la ligne de commande. Pour le second argument, passez b pour un priphrique bloc ou c pour un priphrique caractre. Fournissez les numros de priphrique majeur et mineur en troisime et quatrime argument, respectivement. Par exemple, la commande suivante cre un fichier de priphrique caractre appel lp0 dans le rpertoire courant. Ce priphrique a le numro de priphrique majeur 6 et le numro mineur 0. Ces nombres correspondent au premier port parallle sur le systme Linux.
% mknod ./lp0 c 6 0

Souvenez-vous que seuls les processus du superutilisateur peuvent crer des priphriques bloc ou caractre, vous devez donc tre connect en tant que root pour invoquer cette commande avec succs. La commande ls affiche les fichiers de priphrique d'une faon particulire. Si vous l'appelez avec les options -l ou -o, le premier caractre de chaque ligne indique le type du fichier. Rappelons que - (un tiret) indique un fichier classique, alors que d indique un rpertoire.

- 100 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

De mme, b dsigne un priphrique bloc et c un priphrique caractre. Pour ces deux derniers, ls affiche les numros de priphrique majeur et mineur l o se trouve habituellement la taille pour les fichiers ordinaires. Par exemple, nous pouvons afficher le priphrique caractre que nous venons juste de crer:
% ls -l lp0 crw-r??- 1 root root 6, 0 Mar 7 17:03 lp0

Dans un programme, vous pouvez dterminer si un fichier est un priphrique bloc ou caractre et donc obtenir ses numros de priphrique via stat. Consultez la Section B.2, stat , de l'Appendice B, pour plus d'informations. Pour supprimer le fichier, utilisez rm. Cela ne supprime pas le priphrique ou son pilote; mais simplement le fichier de priphrique du systme de fichiers.
% rm ./lp0

6-3-1 - Le Rpertoire /dev


Par convention, un systme GNU/Linux inclut un rpertoire /dev contenant tous les fichiers de priphriques caractre ou bloc des priphriques dtects. Les entres de /dev ont des noms standardiss correspondants au numros de priphrique majeur et mineur. Par exemple, le priphrique matre connect au contrleur IDE primaire, qui dispose des numros de priphrique majeur et mineur~3 et~0, a le nom standard /dev/hda. Si ce priphrique gre les partitions, la premire, qui dispose du numro de priphrique mineur~1, a le nom standard /dev/hda1. Vous pouvez le vrifier sur votre propre systme:
% ls -l /dev/hda /dev/hda1 brw-rw?? 1 root disk 3, 0 May 5 1998 /dev/hda brw-rw?? 1 root disk 3, 1 May 5 1998 /dev/hda1

De mme, /dev contient une entre pour le priphrique caractre qu'est le port parallle que nous avons utilis prcdemment:
% ls -l /dev/lp0 crw-rw?? 1 root daemon 6, 0 May 5 1998 /dev/lp0

Dans la plupart des cas, vous ne devriez pas utiliser mknod pour crer vos propres fichiers de priphrique. Utilisez plutt les entres de /dev. Les programmes ne disposant pas des privilges superutilisateur n'ont pas d'autre choix que de les utiliser puisqu'ils ne peuvent pas crer leur propres entres. Typiquement, seuls les administrateurs systme et les dveloppeurs utilisant des priphriques spcifiques ont besoin de crer leurs propres fichiers de priphrique. La plupart des distributions GNU/Linux proposent des utilitaires d'aide la cration de fichiers de priphrique standards avec les noms corrects.

6-3-2 - Accder des Priphriques en Ouvrant des Fichiers


Comment utiliser ces priphriques? Dans le cas de priphriques caractre, cela peut tre relativement simple: ouvrez le priphrique comme s'il s'agissait d'un fichier classique et lisez ou crivez-y. Vous pouvez mme utiliser des commandes conues pour les fichiers traditionnels, comme cat ou la syntaxe de redirection de votre shell pour envoyer ou lire des donnes partir du priphrique. Par exemple, si vous disposez d'une imprimante connecte sur le premier port parallle de votre ordinateur, vous pouvez imprimer des fichiers en les envoyant directement sur /dev/lp0(Les utilisateurs de Windows reconnatront l

- 101 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

un priphrique similaire au fichier magique LPT1 de Windows.). Pour imprimer le contenu de document.txt, invoquez la commande suivante:
% cat document.txt > /dev/lp0

Vous devez disposer des permissions en criture sur le fichier de priphrique pour que la commande n'choue pas; sur beaucoup de systmes GNU/Linux, les permissions sont dfinies de telle faon que seul root et le dmon d'impression systme (lpd) puissent crire dans ce fichier. De plus, ce qui sort de votre imprimante dpend de la faon dont elle interprte les donnes que vous lui envoyez. Certaines imprimantes imprimeront les fichiers texte plats que vous leur enverrez(Votre imprimante peut ncessiter l'ajout de retours chariot, code ASCII 13, la fin de chaque ligne et l'ajout d'un caractre de saut de page, code ASCII 12, la fin de chaque page.), d'autres non. Les imprimantes PostScript interprteront et imprimeront les fichiers PostScript que vous leur envoyez. Dans un programme, envoyer des donnes un priphrique est aussi simple. Par exemple, cet extrait de code utilise des fonctions d'E/S standard de bas niveau pour envoyer le contenu d'un tampon vers /dev/lp0.
int fd = open ("/dev/lp0", O_WRONLY); write (fd, buffer, buffer_length); close (fd);

6-4 - Priphriques Matriels


Quelques priphriques bloc standards sont lists dans le Tableau peribloc. Les numros mineurs des priphriques similaires suivent un motif classique (par exemple, la seconde partition du premier priphrique SCSI est /dev/sda2). Il est parfois utile de savoir quel priphrique correspondent les noms de priphriques lorsque l'on observe les systmes de fichiers monts dans /proc/mounts (consultez la Section 7.5, Lecteurs et Systmes de Fichiers , du Chapitre 7, pour en savoir plus). Listing Partiel des Priphriques Bloc Courants

- 102 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Priphrique Premier lecteur de disquettes Second lecteur de disquettes Contrleur IDE primaire, matre Contrleur IDE primaire, matre, premire partition Contrleur IDE primaire, esclave Contrleur IDE primaire, esclave, premire partition Premier lecteur SCSI Premier lecteur SCSI, premire partition Second lecteur SCSI Second lecteur SCSI, premire partition Premier lecteur CD-ROM SCSI Second lecteur CD-ROM SCSI

Nom /dev/fd0 /dev/fd1 /dev/hda /dev/hda1 /dev/hdb /dev/hdb /dev/sda /dev/sda1 /dev/sdb /dev/sda /dev/scd0 /dev/scd1

N majeur 2 2 3 3 3 3 8 8 8 8 11 11

N mineur 0 1 0 1 64 65 0 1 16 17 0 1

Le Tableau pericar liste quelques priphriques caractre courants. Listing Partiel des Priphriques Caractre Courants Priphrique Port parallle 0 Port parallle 1 Premier port srie Second port srie Lecteur de cassettes IDE Premier lecteur de cassettes SCSI Second lecteur de cassettes SCSI Console systme Premier terminal virtuel Second terminal virtuel Terminal du processus courant Carte son (oss) Nom /dev/lp0 ou /dev/par0 /dev/lp1 ou /dev/par1 /dev/ttyS0 /dev/ttyS1 /dev/ht0 /dev/st0 /dev/st1 /dev/console /dev/tty1 /dev/tty2 /dev/tty /dev/audio N majeur 6 2 4 4 37 9 9 5 4 4 5 14 N mineur 0 1 64 65 0 0 1 1 1 2 0 4

Vous pouvez accder certains composants matriels via plus d'un priphrique caractre; souvent, des priphriques caractre distincts ont une smantique diffrente. Par exemple, lorsque vous utilisez le lecteur de cassettes IDE /dev/ht0, Linux rembobine automatiquement la cassette lorsque vous fermez le descripteur de fichier. Vous pouvez utiliser /dev/nht0 pour accder au mme lecteur de cassettes, la seule diffrence est que Linux ne

- 103 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

rembobine pas la cassette lors de la fermeture. Vous pourriez rencontrer des programmes utilisant /dev/cua0 et des dispositifs similaires; il s'agit d'anciennes interfaces vers les ports srie comme /dev/ttyS0. De temps autre, vous pourriez avoir besoin d'crire des donnes directement sur des priphriques caractre ? par exemple: Un programme terminal peut accder modem directement par le biais d'un port srie. Les donnes crites ou lues partir de ces priphriques sont transmises par le modem un ordinateur distant. Un programme de sauvegarde sur cassette peut crire directement des donnes sur le priphrique de lecture de cassettes. Ce programme peut implmenter son propre format de compression et de vrification d'erreur. Un programme peut crire directement sur le premier terminal virtuel(Sur la plupart des systmes GNU/ Linux, vous pouvez basculer vers le premier terminal en appuyant sur Ctrl+Alt+F1. Utilisez Ctrl+Alt+F2 pour le second terminal virtuel, etc.) en crivant sur /dev/tty1. Les fentres de terminal s'excutant sous un environnement graphique ou les sessions distantes ne sont pas associes des terminaux virtuels mais des pseudos-terminaux. Consultez la Section 6.6, PTY , pour plus d'informations. Parfois, un programme peut avoir besoin d'accder au terminal auquel il est associ. Par exemple, votre application peut avoir besoin de demander un mot de passe l'utilisateur. Pour des raisons de scurit, vous ne voulez pas tenir compte des redirections d'entre et de sortie standards et toujours lire le mot de passe partir du terminal, peut importe la faon dont l'utilisateur appelle votre programme. Une faon de le faire est d'ouvrir /dev/tty, qui correspond toujours au terminal associ au processus effectuant l'ouverture. crivez l'invite de mot de passe sur ce priphrique et lisez le mot de passe. En ignorant l'entre et la sortie standards, vous vitez que l'utilisateur n'alimente votre programme avec un mot de passe stock dans un fichier avec une syntaxe comme celle-ci: % programme_sur < mon-motdepasse.txt Si vous avez besoin d'un mcanisme d'authentification dans votre programme, vous devriez vous tourner vers le dispositif PAM de GNU/Linux. Consultez la Section 10.5, Authentification des Utilisateurs , du Chapitre 10, Scurit , pour plus d'informations. Un programme peut diffuser des sons via la carte son du systme en envoyant des donnes audio vers /dev/ audio. Notez que les donnes audio doivent tre au format Sun (fichiers portant habituellement l'extension .au). Par exemple, beaucoup de distributions GNU/Linux fournissent le fichier son classique /usr/share/ sndconfig/sample.au. Si votre systme dispose de ce fichier, essayez de le jouer grce la commande suivante: % cat /usr/share/sndconfig/sample.au > /dev/audio Si vous devez utiliser des sons dans votre programme, cependant, vous devriez utiliser l'un des multiples bibliothques et services de gestion de sons disponibles pour GNU/Linux. L'environnement de bureau Gnome utilise l'Enlightenment Sound Daemon (EsounD), disponible sur http://www.tux.org/~ricdude/EsounD.html. KDE utilise aRts, disponible sur http:// space.twc.de/~stefan/kde/arts-mcop-doc/. Si vous utilisez l'un de ces systmes de son au lieu d'crire directement sur /dev/audio, votre programme pourra tre utilis plus facilement avec d'autres programmes utilisant la carte son de l'ordinateur.

6-5 - Priphriques Spciaux


Linux fournit galement divers priphriques caractre ne correspondant aucun priphrique matriel. Ces fichiers ont tous le numro de priphrique majeur~1, qui est associ la mmoire du noyau Linux et non un pilote de priphrique.

6-5-1 - /dev/null
Le fichier /dev/null, le priphrique nul, est trs pratique. Il a deux utilisations; vous tes probablement familiers avec la premire: Linux ignore toute donne crite vers /dev/null. Une astuce souvent utilise est de spcifier /dev/null en tant que fichier de sortie lorsque l'on ne veut pas de sortie. Par exemple, pour lancer une commande et ignorer son affichage standard (sans l'afficher ni l'envoyer vers un fichier), redirigez la sortie standard vers /dev/null: % commande_bavarde > /dev/null

- 104 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Lire depuis /dev/null renvoie toujours une fin de fichier. Par exemple, si vous ouvrez un descripteur de fichier correspondant /dev/null en utilisant open puis essayez d'appeler read sur ce descripteur, aucun octet ne sera lu et read renverra 0. Si vous copiez /dev/null vers un autre fichier, la destination sera un fichier de taille nulle: % cp /dev/null fichier_vide % ls -l fichier_vide -rw-rw?? 1 samuel samuel 0 Mar 8 00:27 fichier_vide

6-5-2 - /dev/zero
Le fichier de priphrique /dev/zero se comporte comme s'il contenait une infinit d'octets ~0. Quelle que soit la quantit de donnes que vous essayez de lire partir de /dev/zero, Linux gnrera suffisamment d'octets nuls. Pour illustrer cela, excutons le programme de capture en hexadcimal prsent dans le Listing B.4, Lecture de Donnes , de l'Appendice B. Ce programme affiche le contenu d'un fichier au format hexadcimal.
% ./hexdump /dev/zero 0x000000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...

Appuyez sur Ctrl+C une fois convaincu qu'il continuera indfiniment. Mettre /dev/zero en correspondance avec la mmoire est une technique d'allocation avance. Reportez-vous la Section 5.3.5, Autres Utilisations de nmap , du Chapitre IPC, Communication Interprocessus pour plus d'informations, et consultez l'encadr Obtenir de la Mmoire Aligne sur des Pages , de la Section 8.9, mprotect : Dfinir des Permissions Mmoire , du Chapitre 8, Appels Systme Linux , pour un exemple.

6-5-3 - /dev/full
Le fichier /dev/full se comporte comme s'il se trouvait sur un systme de fichiers ne comportant plus d'espace libre. Une criture vers /dev/full choue et positionne errno ENOSPC, qui indique que le lecteur de destination est plein. Par exemple, vous pouvez tenter d'crire sur /dev/full en utilisant la commande cp:
% cp /etc/fstab /dev/full cp: criture de `/dev/full': Aucun espace disponible sur le priphrique

Le fichier /dev/full est essentiellement utile pour tester la faon dont se comporte votre programme s'il tombe court d'espace disque lors de l'criture d'un fichier.

6-5-4 - Dispositifs de Gnration de Nombres Alatoires


Les priphriques spciaux /dev/random et /dev/urandom donnent accs au dispositif de gnration de nombres alatoires intgr au noyau Linux. La plupart des fonctions logicielles charges de gnrer des nombres alatoires, comme la fonction rand de la bibliothque standard du~C, gnrent en fait des nombres pseudo-alatoires. Bien que ces nombres aient certaines proprits des nombres alatoires, ils sont reproductibles: si vous relancez une srie avec la mme valeur d'initialisation, vous obtiendrez la mme squence de nombres pseudo-alatoires chaque fois. Cet inconvnient est invitable car les ordinateurs sont intrinsquement dterministes et prvisibles. Pour certaines applications, cependant, ce comportement n'est pas souhaitable; par exemple, il peut tre possible de casser un algorithme de chiffrement si l'on connat la squence de nombres alatoires qu'il utilise.

- 105 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

L'obtention de nombres alatoires plus stricts au sein de programmes informatiques ncessite une source externe d'vnements alatoires. Le noyau Linux utilise une source d'vnements alatoires particulirement bonne: vous! En mesurant les carts temporels entre vos actions, comme l'appui sur les touches et les mouvements de la souris, Linux est capable de gnrer un flux de nombres alatoires de grande qualit impossible prdire. Vous pouvez accder ce flux en lisant les fichiers /dev/random et /dev/urandom. Les donnes que vous obtenez sont issues d'un flux d'octets gnr alatoirement. La diffrence entre les deux priphriques n'est visible que lorsque Linux a puis son stock de nombres alatoires. Si vous essayez de lire un nombre important d'octets partir de /dev/random mais ne gnrez aucune action (vous n'utilisez pas le clavier, ne bougez pas la souris ni n'effectuez aucune autre action de ce type), Linux bloque l'opration de lecture. La gnration ne reprendra que lorsque vous effectuerez des actions. Par exemple, essayez d'afficher le contenu de /dev/random en utilisant la commande od(Nous utilisons od plutt que le programme hexdump du Listing B.4, mme s'il font plus ou moins la mme chose, car hexdump se termine lorsqu'il n'y a plus de donnes lire, alors que od attend des donnes supplmentaires. L'option -t x1 indique od d'afficher le contenu du fichier en hexadcimal.). Chaque ligne affiche~16 nombres alatoires.
% od -t 0000000 0000020 0000040 0000060 x1 2c d3 b3 05 /dev/random 9c 7a db 2e 6d 1e a7 91 b0 8d 94 21 a3 02 cb 22

79 05 57 0a

3d 2d f3 bc

65 4d 90 c9

36 c3 61 45

c2 a6 dd dd

e3 de 26 a6

1b 54 ac 59

52 29 94 40

75 f4 c3 22

1e 46 b9 53

1a 04 3a d4

Le nombre de lignes affiches varie ? il peut y en avoir trs peu ? mais la sortie se mettra en pause ds que Linux puisera son stock de nombres alatoires. Essayez maintenant de dplacer votre souris ou de saisir quelque chose au clavier et vrifiez que de nouveaux nombres alatoires apparaissent. Pour en obtenir encore plus, vous pouvez laisser votre chat marcher sur le clavier. Une lecture partir de /dev/urandom, en revanche, ne bloque jamais. Si Linux tombe court de nombre alatoires, il utilise un algorithme de chiffrement pour gnrer des octets pseudo-alatoires partir de la dernire squence d'octets alatoires. Bien que ces octets soient suffisamment alatoires pour la plupart des utilisations, ils ne satisfont pas autant de tests que ceux obtenus partir de /dev/random. Par exemple, si vous invoquez la commande suivante, les octets alatoires dfileront en continu, jusqu' ce que vous tuiez le programme avec Ctrl+C:
% od -t 0000000 0000020 0000040 ... x1 62 26 95 /dev/urandom 71 d6 3e af dd de 62 c0 42 78 bd 29 9c 69 49 3b 95 bc b9 6c 15 16 38 fd 7e 34 f0 ba ce c3 31 e5 2c 8d 8a dd f4 c4 3b 9b 44 2f 20 d1 54

Utiliser des nombres alatoires provenant de /dev/random dans un programme est une chose assez facile. Le Listing randomnumber prsente une fonction qui gnre un nombre alatoire en utilisant les octets lus partir de /dev/ random. Souvenez-vous que la lecture est bloque jusqu' ce qu'il y ait suffisamment d'vnements alatoires pour la satisfaire; vous pouvez utiliser /dev/urandom la place si vous accordez plus de priorit la rapidit d'excution et que vous pouvez vous contenter de nombres alatoires d'une qualit moindre. Fonction Gnrant un Nombre Alatoire partir de /dev/random random_number.c
#include #include #include #include #include <assert.h> <sys/stat.h> <sys/types.h> <fcntl.h> <unistd.h>

/* Renvoie un entier alatoire entre MIN et MAX inclus. La source utilise est /dev/random. */

- 106 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Fonction Gnrant un Nombre Alatoire partir de /dev/random random_number.c

int random_number (int min, int max) { /* Stocke un descripteur de fichier pointant vers /dev/random dans une variable static. De cette faon, nous n'avons pas besoin d'ouvrir le fichier chaque fois que la fonction est appele. */ static int dev_random_fd = -1; char* next_random_byte; int bytes_to_read; unsigned random_value; /* S'assure que MAX est plus grand que MIN. */ assert (max > min); /* S'il s'agit du premier appel de la fonction, ouvre un descripteur de fichier pointant vers /dev/random. */ if (dev_random_fd == -1) { dev_random_fd = open ("/dev/random", O_RDONLY); assert (dev_random_fd != -1); } /* Lit suffisamment d'octets alatoires pour remplir un entier. */ next_random_byte = (char*) &random_value; bytes_to_read = sizeof (random_value); /* Boucle jusqu' ce que l'on ait assez d'octets. Comme /dev/random est gnr partir d'actions de l'utilisateur, la lecture peut bloquer et ne renvoyer qu'un seul octet la fois. */ do { int bytes_read; bytes_read = read (dev_random_fd, next_random_byte, bytes_to_read); bytes_to_read -= bytes_read; next_random_byte += bytes_read; } while (bytes_to_read > 0); /* Calcule un nombre alatoire dans l'intervalle demand. */ return min + (random_value % (max - min + 1)); }

6-5-5 - Priphriques Loopback


Un priphrique loopback vous permet de simuler un priphrique bloc en utilisant un fichier disque ordinaire. Imaginez un lecteur de disque pour lequel les donnes sont crites et lues partir d'un fichier appel image-disque plutt que depuis les pistes et secteurs d'un disque physique rel ou d'une des partition d'un disque (bien sr, le fichier image-disque doit se situer sur un disque physique, qui doit tre plus grand que le disque simul). Un priphrique loopback vous permet d'utiliser un fichier de cette faon. Les priphriques loopback s'appellent /dev/loop0, /dev/loop1, etc. Chacun peut tre utilis pour simuler un priphrique bloc distinct. Notez que seul le superutilisateur peut paramtrer un priphrique loopback. Un tel priphrique peut tre utilis de la mme faon que n'importe quel autre priphrique bloc. En particulier, vous pouvez placer un systme de fichiers sur le priphrique puis monter ce systme de fichiers comme s'il rsidait sur un disque ou une partition classique. Un tel systme de fichiers, qui rside entirement au sein d'un fichier sur disque ordinaire, est appel systme de fichiers virtuel. Pour construire un systme de fichiers et le monter partir d'un priphrique loopback, suivez ces tapes: - Crez un fichier vide qui contiendra le systme de fichiers virtuel. La taille du fichier sera la taille du priphrique loopback une fois mont. Une faon pratique de construire un fichier d'une taille prdtermine est d'utiliser la commande dd. Elle copie des blocs (de 512~octets chacun, par dfaut) d'un fichier vers un autre. Le fichier / dev/zero convient parfaitement pour tre utilis comme source d'octets nuls. Pour construire un fichier de 10Mo appel image-disque, utilisez la commande suivante: % dd if=/dev/zero of=/tmp/image-disque count=20480 20480+0 enregistrements lus. 20480+0 enregistrements crits. % ls -l /tmp/image-disque -rw-rw?? 1 root root 10485760 Mar 8 01:56 /tmp/image-disque - Le fichier que vous venez de crer est rempli avec des octets 0. Avant de le monter, vous devez y placer un systme de fichiers. Cette opration initialise diverses structures de contrle ncessaires l'organisation et au stockage de
- 107 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

fichiers et cre le rpertoire racine. Vous pouvez placer n'importe quel type de systme de fichiers sur votre image disque. Pour crer un systme de fichiers ext3 (le type le plus courant pour les disques Linux), utilisez la commande mke2fs. Comme elle est habituellement excute sur des priphriques bloc, et non pas des fichiers ordinaires, elle demande une confirmation: /sbin/mke2fs -q -j /tmp/image-disque /tmp/image-disque n'est pas un priphrique spcial bloc. Procder malgr tout? (y pour oui, n pour non) y L'option -q supprime les informations rcapitulatives sur le systme de fichiers nouvellement cr. Supprimez-la si vous tes curieux. Dsormais image-disque contient un nouveau systme de fichiers comme s'il s'agissait d'un disque de 10 Mo tout neuf. - Montez le systme de fichiers en utilisant un priphrique loopback. Pour cela, utilisez la commande mount en spcifiant le fichier de l'image disque comme priphrique monter. Passez galement l'option de montage loop=priphrique-loopback, en utilisant l'option -o pour indiquer mount quel priphrique loopback utiliser. Par exemple, pour monter notre systme de fichiers image-disque, utilisez ces commandes. Souvenez-vous, seul le superutilisateur peut utiliser un priphrique loopback. La premire commande cre un rpertoire, /tmp/virtual-fs, que nous allons utiliser comme point de montage pour le systme de fichiers virtuel. % mkdir /tmp/virtual-fs % mount -o loop=/dev/loop0 /tmp/image-disque /tmp/virtual-fs Dsormais, l'image disque est monte comme s'il s'agissait d'un disque de 10Mo ordinaire. % df -h /tmp/virtual-fs Sys. De fich. Tail. Occ. Disp. %Occ. Mont sur /tmp/image-disque 9.7M 13k 9.2M 0% /tmp/virtual-fs Vous pouvez l'utiliser comme n'importe quel autre disque: % cd /tmp/virtual-fs % echo "Coucou !" > test.txt % ls -l total 13 drwxr-xr-x 2 root root 12288 Mar 8 02:00 lost+found -rw-rw?? 1 root root 14 Mar 8 02:12 test.txt % cat test.txt Coucou ! Notez que lost+found est un rpertoire automatiquement cr par mke2fs(Si le systme de fichiers subit des dommage et que des donnes sont rcupres sans tre associes un fichier, elles sont places dans lost+found.). Lorsque vous en avez fini, dmontez le systme de fichiers virtuel. % cd / tmp % umount /tmp/virtual-fs Vous pouvez supprimer image-disque si vous le dsirez ou vous pouvez le monter plus tard pour accder aux fichiers du systme de fichiers virtuel. Vous pouvez galement le copier sur un autre ordinateur o vous pourrez le monter ? le systme de fichiers que vous avez cr sera entirement intact.

Au lieu de crer un systme de fichiers partir de rien, vous pouvez en copier un partir d'un priphrique existant. Par exemple, vous pouvez crer l'image du contenu d'un CD-ROM simplement en le copiant partir d'un lecteur de CD-ROM. Si vous disposez d'un lecteur de CD-ROM IDE, utilisez le nom de priphrique correspondant, par exemple /dev/ hda, dcrit prcdemment. Si vous disposez d'un lecteur CD-ROM SCSI, le nom de priphrique sera du type /dev/ scd0. Le lien symbolique /dev/cdrom peut galement exister sur votre systme, il pointe alors vers le priphrique appropri. Consultez le fichier /etc/fstab pour dterminer quel priphrique correspond au lecteur de CD-ROM de votre ordinateur. Copiez simplement le priphrique vers un fichier. Le rsultat sera une image disque complte du systme de fichiers du CD-ROM situ dans le lecteur ? par exemple:
% cp /dev/cdrom /tmp/cdrom-image

Cette opration peut prendre plusieurs minutes selon le CD-ROM que vous copiez et la vitesse de votre lecteur. Le fichier image rsultant sera relativement gros ? il fera la mme taille que le contenu du CD-ROM. Vous pouvez maintenant monter cette image sans disposer du disque original. Par exemple, pour le monter sur / mnt/cdrom, utilisez cette commande:
% mount -o loop=/dev/loop0 /tmp/cdrom-image /mnt/cdrom

Comme l'image est situe sur le disque dur, les temps d'accs seront bien infrieurs ceux du disque CD-ROM original. Notez que la plupart des CD-ROM utilisent le systme de fichiers ISO-9660.

- 108 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

6-6 - PTY
Si vous excutez la commande mount sans arguments de ligne de commande, ce qui liste les systmes de fichiers monts sur votre systme, vous remarquerez une ligne ressemblant cela:
devpts on /dev/pts type devpts (rw,gid=5,mode=620)

Elle indique qu'un systme de fichiers d'un type particulier, devpts, est mont sur /dev/pts. Ce systme de fichiers, qui n'est pas associ avec un priphrique matriel, est un systme de fichiers magique cr par le noyau Linux. Il est similaire au systme de fichiers /proc; consultez le Chapitre 7 pour plus d'informations sur son fonctionnement. Comme le rpertoire /dev, /dev/pts contient des entres correspondant des priphriques. Mais contrairement /dev, qui est un rpertoire classique, /dev/pts est un rpertoire spcial cr dynamiquement par le noyau Linux. Lecontenu du rpertoire varie avec le temps et reflte l'tat du systme. Les fichiers de /dev/pts correspondent des pseudo-terminaux (ou pseudo-TTY, ou PTY). Linux cre un PTY pour chaque nouvelle fentre de terminal que vous ouvrez et place l'entre correspondante dans /dev/pts. Le priphrique PTY se comporte comme un terminal classique ? il accepte des entres depuis le clavier et affiche les sorties du programme lui correspondant. Les PTY sont numrots et leur numro correspond au nom du fichier correspondant dans /dev/pts. Vous pouvez afficher le terminal associ un processus grce la commande ps. Indiquez tty comme l'un des champ de format personnalis avec l'option -o. Pour afficher l'identifiant de processus, le TTY et la ligne de commande de chaque processus partageant le mme terminal, invoquez ps -o pid,tty,cmd.

6-6-1 - Exemple d'utilisation des PTY


Par exemple, vous pouvez dterminer le PTY associ une fentre de terminal donne en invoquant cette commande au sein de la fentre:
% ps -o pid,tty,cmd PID TT CMD 28832 pts/4 bash 29287 pts/4 ps -o pid,tty,cmd

La fentre o est lance la commande s'excute au sein du PTY~4. Le PTY a un fichier correspondant dans /dev/pts:
% ls -l /dev/pts/4 crw?w?? 1 samuel tty 136, 4 Mar 8 02:56 /dev/pts/4

Notez qu'il s'agit d'un priphrique caractre et son propritaire est celui du processus pour lequel il a t cr. Vous pouvez lire ou crire sur un priphrique PTY. Si vous lisez partir de celui-ci, vous intercepterez les saisies clavier destines au programme s'excutant au sein du PTY. Si vous essayez d'y crire, les donnes apparatrontdans la fentre correspondante. Essayez d'ouvrir un nouveau terminal et dterminez son PTY en invoquant ps -o pid,tty,cmd. Depuis une autre fentre, crivez du texte sur ce priphrique. Par exemple, si le numro de PTY du nouveau terminal est~7, invoquez cette commande depuis une autre fentre:

- 109 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) % echo "Hello, other window!" > /dev/pts/7

La sortie apparat dans la fentre de terminal. Si vous la fermez, l'entre numro~7 de /dev/pts disparat. Si vous invoquez ps pour dterminer le TTY depuis un terminal virtuel en mode texte (appuyez sur Ctrl+Alt+F1 pour basculer vers le premier terminal virtuel, par exemple), vous remarquerez qu'il s'excute au sein d'un priphrique de terminal ordinaire et non pas un PTY:
% ps -o pid,tty,cmd PID TT CMD 29325 tty1 -bash 29353 tty1 ps -o pid,tty,cmd

6-7 - ioctl
L'appel systme ioctl est une interface destine au contrle de dispositifs matriels. Le premier argument de ioctl est un descripteur de fichier qui doit pointer sur le priphrique que vous voulez contrler. Le second argument est un code de requte indiquant l'opration que vous souhaitez effectuer. Diffrents codes de requtes sont disponibles pour chacun des priphriques. Selon le code, il peut y avoir des arguments supplmentaires servant passer des donnes ioctl. La plupart des codes de requte disponibles pour les diffrents priphriques sont lists sur la page de manuel de ioctl_list. L'utilisation de ioctl ncessite gnralement une connaissance approfondie du pilote de priphrique du matriel que vous souhaitez contrler. Il s'agit d'un sujet qui dpasse le cadre de ce livre. Cependant, nous prsentons un exemple vous donnant un aperu de la faon dont ioctl est utilis. jecte un CD-ROM cdrom-eject.c
#include #include #include #include #include #include <fcntl.h> <linux/cdrom.h> <sys/ioctl.h> <sys/stat.h> <sys/types.h> <unistd.h>

int main (int argc, char* argv[]) { /* Ouvre un descripteur de fichier vers le priphrique pass sur la ligne int fd = open (argv[1], O_RDONLY); /* jecte le CD-ROM. */ ioctl (fd, CDROMEJECT); /* Ferme le descripteur. */ close (fd); return 0;

de commande. */

Le Listing cdromeject est un court programme qui jecte le disque prsent dans un lecteur de CD-ROM (si ce dernier le supporte). Il prend un argument en ligne de commande, le priphrique correspondant au lecteur de CD-ROM. Il ouvre un descripteur de fichier pointant vers le priphrique et invoque ioctl avec le code de requte CDROMEJECT. Cette requte, dfinie dans l'entte <linux/cdrom.h>, indique au priphrique d'jecter le disque. Par exemple, si votre systme dispose d'un lecteur de CD-ROM connect en tant que priphrique matre sur le second contrleur IDE, le priphrique correspondant est /dev/hdc. Pour jecter le disque du lecteur, invoquez cette commande:
% ./cdrom-eject /dev/hdc

- 110 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

7 - Le Systme de Fichiers /proc


Essayez d'invoquer la commande mount sans argument -- elle liste les systmes de fichiers actuellement monts sur votre systme GNU/Linux. Vous apercevrez une ligne de ce type:
proc on /proc type proc (rw)

Il s'agit du systme de fichiers spcial /proc. Notez que le premier champ, proc, indique qu'il n'est associ aucun priphrique matriel, comme un lecteur de disque. Au lieu de cela, /proc est une fentre sur le noyau Linux en cours d'excution. Les fichiers de /proc ne correspondent pas des fichiers rels sur un disque physique. Il s'agit plutt d'objets magiques qui se comportent comme des fichiers mais donnent accs des paramtres, des structures de donnes et des statistiques du noyau. Le contenu de ces fichiers n'est pas compos de blocs de donnes, comme celui des fichiers ordinaires. Au lieu de cela, il est gnr la vole par le noyau Linux lorsque vous lisez le fichier. Vous pouvez galement changer la configuration du noyau en cours d'excution en crivant dans certains fichiers du systme de fichiers /proc. tudions un exemple:
% ls -l /proc/version -r--r--r-1 root root 0 Jan 17 18:09 /proc/version

Notez que la taille du fichier est zro; comme le contenu du fichier est gnr par le noyau, le concept de taille de fichier n'a pas de sens. De plus, si vous essayez cette commande, vous remarquerez que la date de modification est la date courante. Qu'y a-t-il dans ce fichier? Le contenu de /proc/version est une chane dcrivant le numro de version du noyau Linux. Il correspond aux informations qui seraient renvoyes par l'appel systme uname, dcrit dans le Chapitre 8, Appels Systme Linux , Section 8.15, uname , ainsi que des informations supplmentaires comme la version du compilateur utilis pour construire le noyau. Vous pouvez lire /proc/version comme n'importe quel autre fichier. Par exemple, au moyen de cat:
% cat /proc/version Linux version 2.2.14-5.0 (root@porky.devel.redhat.com) (gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)) #1 Tue Mar 7 21:07:39 EST 2000

Les diffrentes entres du systme de fichiers /proc sont dcrites dans la page de manuel de /proc (Section 5). Pour la consulter, utilisez la commande suivante:
% man 5 proc

Dans ce chapitre, nous dcrirons certaines fonctionnalits du systme de fichiers /proc qui sont les plus susceptibles de servir des programmeurs et nous donnerons des exemples d'utilisation. Quelques unes de ces fonctionnalits sont galement utiles pour le dbogage. Si vous tes intress par le fonctionnement exact de /proc, consultez le code source du noyau Linux situ dans / usr/src/linux/fs/proc.

7-1 - Obtenir des Informations partir de /proc


La plupart des fichiers de /proc donnent des informations formates pour pouvoir tre lues par des humains, cependant leur prsentation reste suffisamment simple pour qu'elles puissent tre analyses automatiquement. Par exemple, /proc/cpuinfo contient des informations sur le processeur (ou les processeurs dans le cas d'une machine
- 111 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

multiprocesseur). Son contenu est un tableau de valeurs, une par ligne, avec une description de la valeur et deux points avant chacune d'entre elles. Par exemple, son contenu peut ressembler cela:
% cat /proc/cpuinfo processor :0 vendor_id : GenuineIntel cpu family :6 model :5 model name : Pentium II (Deschutes) stepping :2 cpu MHz : 400.913520 cache size : 512 KB fdiv_bug : no hlt_bug : no sep_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level :2 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr bogomips : 399.77

Nous dcrirons quoi correspondent certains de ces champs dans la Section 7.3.1, Informations sur le Processeur . Une mthode simple pour extraire une valeur de cette liste est de lire le fichier dans un tampon et de l'analyser en mmoire au moyen de sscanf. Le Listing clockspeed montre une faon de faire. Le programme dclare une fonction, get_cpu_clock_speed, qui charge /proc/cpuinfo en mmoire et en extrait la vitesse d'horloge du premier processeur. Exemple d'utilisation de /proc/cpuinfo clock-speed.c
#include <stdio.h> #include <string.h> /* Renvoie la vitesse d'horloge du processeur en MHZ, d'aprs /proc/cpuinfo. Sur un systme multiprocesseur, renvoie la vitesse du premier. Renvoie zro en cas d'erreur. */ float get_cpu_clock_speed () { FILE* fp; char buffer[1024]; size_t bytes_read; char* match; float clock_speed; /* Charge le contenu de /proc/cpuinfo dans le tampon. */ fp = fopen ("/proc/cpuinfo", "r"); bytes_read = fread (buffer, 1, sizeof (buffer), fp); fclose (fp); /* Traite le cas o la lecture choue ou le buffer est trop petit. */ if (bytes_read == 0 || bytes_read == sizeof (buffer)) return 0; /* Place un caractre nul la fin de la chane. */ buffer[bytes_read] = "\0"; /* Recherche la ligne commenant par "cpu MHz". */ match = strstr (buffer, "cpu MHz"); if (match == NULL) return 0; /* Analyse la ligne pour extraire la vitesse d'horloge. */ sscanf (match, "cpu MHz : %f", &clock_speed); - 112 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Exemple d'utilisation de /proc/cpuinfo clock-speed.c


} return clock_speed;

int main () { printf ("CPU clock speed: %4.0f MHz\n", get_cpu_clock_speed ()); return 0; }

Soyez conscient du fait que les noms, la signification et les formats de sortie des entres du systme de fichiers / proc peuvent changer au fil des rvisions du noyau Linux. Si vous les utilisez au sein d'un programme, assurez-vous que le comportement du programme reste cohrent si une entre est absente ou formate d'une faon inconnue.

7-2 - Rpertoires de Processus


Le systme de fichiers /proc contient un rpertoire par processus s'excutant sur le systme. Le nom de chacun de ces rpertoires est l'identifiant du processus auquel il correspond(Sur certains systmes UNIX, les identifiants de processus sont aligns au moyen de zros. Ce n'est pas le cas sous GNU/Linux.). Ces rpertoires apparaissent et disparaissent dynamiquement lors du dmarrage et de l'arrt d'un processus. Chaque dossier contient plusieurs entres donnant accs aux informations sur le processus en cours d'excution. C'est de ces rpertoires de processus que le systme de fichiers /proc tire son nom. Chaque rpertoire de processus contient ces fichiers: cmdline contient la liste d'argument du processus. L'entre cmdline est dcrite dans la Section 7.2.2, Liste d'Arguments d'un Processus . cwd est un lien symbolique pointant vers le rpertoire de travail courant du processus (qui peut tre dfini par exemple, via un appel chdir). environ contient l'environnement du processus. Le fichier environ est dcrit dans la Section 7.2.3, Environnement de Processus . exe est un lien symbolique pointant vers l'image binaire excute par le processus. L'entre exe est dcrite dans la Section 7.2.5, Descripteurs de Fichiers d'un Processus . maps contient des informations sur les fichiers mis en correspondance avec l'espace mmoire du processus. Consultez le Chapitre IPC, Section mmap, Mmoire Mappe pour plus de dtails sur le fonctionnement des fichiers mis en correspondance avec la mmoire. Pour chaque fichier mis en correspondance, maps affiche l'intervalle d'adresses de l'espace mmoire du processus avec lequel le fichier est mis en correspondance, les permissions applicables ces adresses, le nom du fichier ainsi que d'autres informations. Le tableau maps affiche pour chaque processus le fichier binaire en cours d'excution, les bibliothques partages actuellement charges et les autres fichiers que le processus a mis en correspondance avec sa mmoire. root est un lien symbolique vers le rpertoire racine du processus. Gnralement, il s'agit d'un lien vers /, le rpertoire racine du systme. Le rpertoire racine d'un processus peut tre chang par le biais de l'appel chroot ou de la commande chroot(L'appel et la commande chroot sortent du cadre de ce livre. Consultez la page de manuel de chroot dans la section 1 pour des informations sur la commande (man 1 chroot) ou la page de manuel de chroot dans la section 2 (man 2 chroot) pour plus d'informations sur l'appel.). stat contient des statistiques et des informations sur le statut du processus. Ce sont les mmes donnes que celles prsentes dans le fichier status, mais au format numrique brut, sur une seule ligne. Ce format est difficile lire mais plus adapt au traitement automatique par des programmes. Si vous voulez utiliser le fichier stat dans vos programmes, consultez la page de manuel de proc qui dcrit son contenu en invoquant man 5 proc. statm contient des informations sur la mmoire utilise par le processus. Le fichier statm est dcrit dans la Section 7.2.6, Statistiques Mmoire de Processus . status contient des informations statistiques et statut sur le processus formate de faon tre lisibles par un humain. La Section 7.2.7, Statistiques sur les Processus prsente une description du fichier status. Le fichier cpu n'apparat que sur les noyaux Linux SMP. Il contient un rcapitulatif de la consommation en temps (utilisateur et systme) du processus, par processeur.

- 113 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Notez que pour des raisons de scurit, les permissions de certains fichiers sont dfinies de faon ce que seul l'utilisateur propritaire du processus (ou le superutilisateur) puisse y accder.

7-2-1 - /proc/self
Une entre particulire dans le systme de fichiers /proc facilite son utilisation par un programme pour obtenir des informations sur le processus au sein duquel il s'excute. L'entre /proc/self est un lien symbolique vers le rpertoire de /proc correspondant au processus courant. La destination du lien /proc/self dpend du processus qui l'utilise: chaque processus voit son propre rpertoire comme destination du lien. Par exemple, le programme du Listing getpid utilise la destination du lien /proc/self pour dterminer son identifiant de processus (nous ne faisons cela que dans un but d'illustration; l'appel de la fonction getpid, dcrite dans le Chapitre processus, Processus , Section idprocessus, Identifiants de Processus constitue une faon beaucoup plus simple d'arriver au mme rsultat). Ce programme utilise l'appel systme readlink, prsent dans la Section 8.11, readlink : Lire des Liens Symboliques , pour extraire la cible du lien. Rcuprer son PID partir de /proc/self get-pid.c
#include <stdio.h> #include <sys/types.h> #include <unistd.h> /* Renvoie l'identifiant de processus de l'appelant, dtermin partir du lien symbolique /proc/self. */ pid_t get_pid_from_proc_self () { char target[32]; int pid; /* Lit la cible du lien symbolique. */ readlink ("/proc/self", target, sizeof (target)); /* La cible est un rpertoire portant de nom du PID. */ sscanf (target, "%d", &pid); return (pid_t) pid; } int main () { printf ("/proc/self renvoie l'identifiant %d\n", (int) get_pid_from_proc_self ()); printf ("getpid() renvoie l'identifiant %d\n", (int) getpid ()); return 0; }

7-2-2 - Liste d'Arguments d'un Processus


Le fichier cmdline contient la liste d'arguments du processus (consultez le Chapitre logicielsQualite, crire des Logiciels GNU/Linux de Qualit , Section listeArgs, La Liste d'Arguments ). Les arguments sont prsents sous forme d'une seule chane de caractres, les arguments tant spars par des NUL. La plupart des fonctions de traitement de chanes de caractres s'attendent ce la chane en elle-mme soit termine par un NUL et ne greront pas les NUL contenus dans la chane correctement, vous aurez donc traiter le contenu de cette chane d'une faon spciale. NUL est le caractre dont la valeur dcimale est 0. Il est diffrent de NULL, qui est un pointeur avec une valeur de 0. En C, une chane de caractres est habituellement termine par un caractre NUL. Par exemple, la chane de caractres "Coucou !" occupe 9 octets car il y a un NUL implicite aprs le point d'exclamation indiquant la fin de la chane. NULL, par contre, est une valeur de pointeur dont vous pouvez tre sr qu'elle ne correspondra jamais une adresse mmoire relle dans votre programme. En C et en C++, NUL est reprsent par la constante de caractre '0', ou (char) 0. La dfinition de NULL diffre

- 114 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

selon le systme d'exploitation; sous Linux, NULL est dfini comme %%((void*)0)%% en C et tout simplement 0 en C++. Dans la Section listeArgs, nous avons prsent un programme, Listing argcv, qui affichait sa liste d'arguments. En utilisant l'entre cmdline du systme de fichiers /proc, nous pouvons crer un programme qui affiche la liste d'arguments d'un autre processus. Le Listing printarglist est un programme de ce type; il affiche la liste d'arguments du processus dont l'identifiant est pass en paramtre. Comme il peut y avoir plusieurs NUL dans le contenu de cmdline et non un seul en fin de chane, nous ne pouvons pas dterminer la taille de la chane en utilisant strlen (qui se contente de compter le nombre de caractres jusqu' ce qu'elle rencontre un NUL). Au lieu de cela, nous dterminons la longueur de cmdline grce read, qui renvoie le nombre d'octets lus. Affiche la Liste d'Arguments d'un Processus print-arg-list.c
#include #include #include #include #include #include <fcntl.h> <stdio.h> <stdlib.h> <sys/stat.h> <sys/types.h> <unistd.h>

/* Affiche la liste d'aguments, un par ligne, du processus dont l'identifiant est pass en paramtre. */ void print_process_arg_list (pid_t pid) { int fd; char filename[24]; char arg_list[1024]; size_t length; char* next_arg; /* Gnre le nom du fichier cmdline pour le processus. */ snprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid); /* Lit le contenu du fichier. */ fd = open (filename, O_RDONLY); length = read (fd, arg_list, sizeof (arg_list)); close (fd); /* read n'ajoute pas de NULL la fin du tempon, nous le faisons ici. */ arg_list[length] = '\0'; /* Boucle sur les arguments. Ceux-ci sont spars par des NUL. */ next_arg = arg_list; while (next_arg < arg_list + length) { /* Affiche l'argument. Chaque argument est termin par NUL, nous le traitons donc comme une chane classique. */ printf ("%s\n", next_arg); /* Avance l'argument suivant. Puisque chaque argument est termin par NUL, strlen renvoie la longueur de l'argument, pas celle de la liste. */ next_arg += strlen (next_arg) + 1; }

int main (int argc, char* argv[]) { pid_t pid = (pid_t) atoi (argv[1]); print_process_arg_list (pid); return 0; }

Par exemple, supposons que le processus 372 soit le dmon de journalisation systme, syslogd.
% ps 372 PID TTY 372 ?

STAT S

TIME COMMAND 0:00 syslogd -m 0

- 115 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) % ./print-arg-list 372 syslogd -m 0

Dans ce cas, syslogd a t invoqu avec les arguments -m 0.

7-2-3 - Environnement de Processus


Le fichier environ contient l'environnement du processus (consultez la Section environnement, L'Environnement ). Comme pour cmdline, les diffrentes variables d'environnement sont spares par des NUL. Le format de chaque lment est le mme que celui utilis dans la variable environ, savoir VARIABLE=valeur. Le Listing printenvironment prsente une gnralisation du programme du Listing printenv de la Section environnement. Cette version prend un identifiant du processus sur la ligne de commande et affiche son environnement en le lisant partir de /proc. Affiche l'Environnement d'un Processus print-environment.c
#include #include #include #include #include #include <fcntl.h> <stdio.h> <stdlib.h> <sys/stat.h> <sys/types.h> <unistd.h>

/* Affiche l'environnement du processus dont l'identifiant est pass en paramtre, une variable par ligne. */ void print_process_environment (pid_t pid) { int fd; char filename[24]; char environment[8192]; size_t length; char* next_var; /* Gnre le nom du fichier environ pour le processus. */ snprintf (filename, sizeof (filename), "/proc/%d/environ", (int) pid); /* Lit le contenu du fichier. */ fd = open (filename, O_RDONLY); length = read (fd, environment, sizeof (environment)); close (fd); /* read ne place pas de caractre NUL la fin du tampon. */ environment[length] = '\0'; /* Boucle sur les variables. Elles sont spares par des NUL. */ next_var = environment; while (next_var < environment + length) { /* Affiche la variable. Elle est termine par un NUL, on la traite donc comme une chane ordinaire. */ printf ("%s\n", next_var); /* Passe la variable suivante. Puisque chaque variable est termine par un NUL, strlen calcule bien la taille de la prochaine variable, et non pas de toute la liste. */ next_var += strlen (next_var) + 1; }

} int main (int argc, char* argv[]) { pid_t pid = (pid_t) atoi (argv[1]); print_process_environment (pid); return 0; }

- 116 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

7-2-4 - Excutable de Processus


L'entre exe pointe vers le fichier binaire excut par le processus. Dans la Section listeArgs, nous avons expliqu que le nom de ce fichier est habituellement pass comme premier lment de la liste d'argument. Notez cependant qu'il s'agit d'une convention; un programme peut tre invoqu avec n'importe quelle liste d'arguments. Utiliser l'entre exe du systme de fichiers /proc est une faon plus fiable de dterminer le fichier binaire en cours d'excution. De mme, il est possible d'extraire l'emplacement absolu de l'excutable, partir du systme de fichiers /proc. Pour beaucoup de programmes, les fichiers auxiliaires se trouvent dans des rpertoires relatifs l'excutable, il est donc ncessaire de dterminer o se trouve rellement le fichier binaire. La fonction get_executable_path du Listing getexepath dtermine le chemin du fichier binaire s'excutant au sein du processus appelant en examinant le lien symbolique /proc/self/exe. Obtient l'Emplacement du Fichier Binaire en Cours d'Excution get-exe-path.c
#include #include #include #include <limits.h> <stdio.h> <string.h> <unistd.h>

/* Recherche l'emplacement du fichier binaire en cours d'excution. Ce chemin est plac dans BUFFER, de taille LEN. Renvoie le nombre de caractres dans le chemin ou -1 en cas d'erreur. */ size_t get_executable_path (char* buffer, size_t len) { char* path_end; /* Lit la cible de /proc/self/exe. */ if (readlink ("/proc/self/exe", buffer, len) <= 0) return -1; /* Recherche la dernire occurrence du caractre slash. */ path_end = strrchr (buffer, "/"); if (path_end == NULL) return -1; /* Se place sur le caractre suivant le dernier slash. */ ++path_end; /* Rcupre le rpertoire contenant le programme en tronquant le chemin aprs le dernier slash. */ *path_end = "\0";

/* La longueur du chemin est le nombre de caractres jusqu'au dernier slash. */ return (size_t) (path_end - buffer);

int main () { char path[PATH_MAX]; get_executable_path (path, sizeof (path)); printf ("ce programme se trouve dans le rpertoire %s\n", path); return 0; }

7-2-5 - Descripteurs de Fichiers d'un Processus


Le rpertoire fd contient des sous-rpertoires correspondant aux descripteurs de fichiers ouverts par un processus. Chaque entre est un lien symbolique vers le fichier ou le priphrique dsign par le descripteur de fichier. Vous pouvez lire ou crire sur ces liens symboliques; cela revient crire ou lire depuis le fichier ou le priphrique ouvert dans le processus cible. Les noms des entres du sous-rpertoire fd correspondent aux numros des descripteurs de fichiers.

- 117 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Voici une astuce amusante que vous pouvez essayer avec les entres fd de /proc. Ouvrez une nouvelle fentre de terminal et rcuprez l'identifiant de processus du processus shell enlanant ps.
% ps PID TTY TIME CMD 1261 pts/4 00:00:00 bash 2455 pts/4 00:00:00 ps

Dans ce cas, le shell (bash) s'excute au sein du processus 1261. Ouvrez maintenant une seconde fentre et jetez un oeil au contenu du sous-rpertoire fd pour ce processus.
% ls -l /proc/1261/fd total 0 lrwx-----1 samuel samuel 64 Jan 30 01:02 0 -> /dev/pts/4 lrwx-----1 samuel samuel 64 Jan 30 01:02 1 -> /dev/pts/4 lrwx-----1 samuel samuel 64 Jan 30 01:02 2 -> /dev/pts/4

(Il peut y avoir d'autres lignes correspondant d'autres descripteurs de fichiers ouverts). Souvenez-vous de ce que nous avons dit dans la Section ESStandards, E/S Standards : les descripteurs de fichiers 0, 1 et 2 sont initialiss pour pointer vers l'entre, la sortie et la sortie d'erreurs standards, respectivement. Donc, en crivant dans le fichier /proc/1261/fd/1 vous pouvez crire sur le priphrique attach stdout pour le processus shell -- dans ce cas, un pseudo TTY correspondant la premire fentre. Dans la seconde fentre, essayez d'crire un message dans ce fichier:
% echo "Coucou." >> /proc/1261/fd/1

Le texte apparat dans la premire fentre. Les descripteurs de fichiers autres que l'entre, la sortie et la sortie d'erreurs standards apparaissent dans le sousrpertoire fd. Le Listing openandspin prsente un programme qui se contente d'ouvrir un descripteur pointant vers un fichier pass sur la ligne de commande et de boucler indfiniment. Ouvre un Fichier en Lecture open-and-spin.c
#include #include #include #include #include <fcntl.h> <stdio.h> <sys/stat.h> <sys/types.h> <unistd.h>

int main (int argc, char* argv[]) { const char* const filename = argv[1]; int fd = open (filename, O_RDONLY); printf ("dans le processus %d, le descripteur %d pointe vers %s\n", (int) getpid (), (int) fd, filename); while (1); return 0; }

Essayez de le lancer dans une fentre:


% ./open-and-spin /etc/fstab dans le processus 2570, le descripteur 3 pointe vers /etc/fstab

Dans une autre fentre, observez le contenu du sous-rpertoire fd correspondant

- 118 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

ce processus.
% ls -l /proc/2570/fd total 0 lrwx-----1 samuel samuel lrwx------ 1 samuel samuel 64 lrwx------ 1 samuel samuel 64 lr-x------ 1 samuel samuel 64

64 Jan Jan 30 Jan 30 Jan 30

30 01:30 0 01:30 1 -> 01:30 2 -> 01:30 3 ->

-> /dev/pts/2 /dev/pts/2 /dev/pts/2 /etc/fstab

Notez que l'entre du descripteur de fichier 3 est lie au fichier /etc/fstab vers lequel pointe le descripteur. Les descripteurs de fichiers peuvent pointer vers des sockets ou des tubes (consultez le Chapitre IPC pour plus d'informations). Dans un tel cas, la cible du lien symbolique correspondant au descripteur de fichier indiquera socket ou pipe au lieu de pointer vers un fichier ou un priphrique classique.

7-2-6 - Statistiques Mmoire de Processus


L'entre statm contient une liste de sept nombres, spars par des espaces. Chaque valeur correspond au nombre de pages mmoire utilises par le processus dans une catgorie donne. Les voici , dans l'ordre: Taille totale du processus. Taille du processus rsident en mmoire. Mmoire partage avec d'autres processus -- c'est--dire les pages mises en correspondance avec l'espace mmoire du processus et d'au moins un autre (comme les bibliothques partages ou les pages en copie l'criture non modifies). Taille du texte du processus -- c'est--dire la taille du code charg. Taille des bibliothques partages charges pour le processus. Mmoire utilise pour la pile du processus. Le nombre de pages modifies par le programme.

7-2-7 - Statistiques sur les Processus


L'entre status contient un certain nombre d'informations propos du processus, formates de faon tre comprhensibles par un humain. Parmi ces informations on trouve l'identifiant du processus et de son pre, les identifiants d'utilisateur et de groupe rel et effectif, l'utilisation mmoire et des masques binaires indiquant quels signaux sont intercepts, ignors et bloqus.

7-3 - Informations sur le Matriel


Plusieurs autres fichiers du systme de fichiers /proc donnent accs des informations sur le matriel. Bien qu'elles n'intressent gnralement que les administrateurs systme, ces informations peuvent parfois tre utiles aux programmeurs. Nous prsenterons ici les plus utiles.

7-3-1 - Informations sur le Processeur


Comme nous l'avons dit prcdemment, /proc/cpuinfo contient des informations sur le ou les processeur(s) du systme. Le champ Processor indique le numro du processeur; il est 0 sur un systme monoprocesseur. Les champs Vendor, CPU Family, Model et Stepping vous permettent de dterminer le modle et la rvision exacts du processeur. Plus utile, le champ Flags indique quels indicateurs sont positionns, ils indiquent les fonctionnalits disponibles pour processeur. Par exemple, mmx, indique que les instructions MMX(Reportez-vous au IA-32 Intel Architecture Software Developer's Manuel pour une documentation complte sur les instructions MMX, et consultez le Chapitre 9, Code Assembleur en Ligne , dans ce livre pour plus d'informations sur leur utilisation au sein de programmes GNU/Linux.) sont disponibles.

- 119 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La plupart des informations renvoyes par /proc/cpuinfo sont obtenues partir de l'instruction assembleur x86 cpuid. Il s'agit d'un mcanisme de bas niveau permettant d'obtenir des informations sur le processeur. Pour mieux comprendre l'affichage de /proc/cpuinfo, consultez la documentation de l'instruction cpuid dans le IA-32 Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference d'Intel (en anglais). Ce manuel est disponible sur http:// developer.intel.com/design. Le dernier champ, bogomips, est une valeur propre Linux. Il s'agit d'un indicateur de la vitesse du processeur mesure au moyen d'une boucle et qui est donc relativement mauvais.

7-3-2 - Informations sur les Priphriques


Le fichier /proc/devices dresse la liste des numros de priphrique majeurs pour les priphriques caractres et bloc disponibles pour le systme. Consultez le Chapitre peripheriques, Priphriques , pour plus d'informations sur les types de priphriques et leurs numros.

7-3-3 - Informations sur le Bus PCI


Le fichier /proc/pci donne un aperu des priphriques attachs au(x) bus PCI. Il s'agit de cartes d'extension PCI mais cela peut aussi inclure les priphriques intgrs la carte mre ainsi que les cartes graphiques AGP. Le listing inclut le type de priphrique; son identifiant et son constructeur; son nom s'il est disponible; des informations sur ses capacits et sur les ressources PCI qu'il utilise.

7-3-4 - Informations sur le Port Srie


Le fichier /proc/tty/driver/serial contient des informations de configuration et des statistiques sur les ports srie. Ceuxci sont numrots partir de 0(Notez que sous DOS et Windows, les port srie sont numrots partir de 1, donc COM1 correspond au port srie 0 sous Linux.). Les informations de configuration sur les ports srie peuvent galement tre obtenues, ainsi que modifies, via la commande setserial. Cependant, /proc/tty/driver/serial contient des statistiques supplmentaires sur le nombre d'interruptions reues par chaque port srie. Par exemple, cette ligne de /proc/tty/driver/serial pourrait dcrire le port srie 1 (qui serait COM2 sous Windows):
1: uart:16550A port:2F8 irq:3 baud:9600 tx:11 rx:0

Elle indique que le port srie est gr par un USART (Universal Synchronous Asynchronous Receiver Transmitter) de type 16550, utilise le port 0x2f8 et l'IRQ 3 pour la communication et tourne 9 600 bauds. Le port a reu 11 interruptions d'mission et 0 interruption de rception. Consultez la Section periphmat, Priphriques Matriels pour des informations sur les priphriques srie.

7-4 - Informations sur le Noyau


La plupart des entres de /proc donnent accs des informations sur la configuration et l'tat du noyau en cours d'excution. Certaines de ces entres sont la racine de /proc; d'autres se trouvent sous /proc/sys/kernel.

7-4-1 - Informations de Version


Le fichier /proc/version contient une chane dcrivant la version du noyau et donnant des informations sur sa compilation. Elle comprend galement des informations sur la faon dont il a t compil: par qui, sur quelle machine, quand et avec quel compilateur -- par exemple:

- 120 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) % cat /proc/version Linux version 2.2.14-5.0 (root@porky.devel.redhat.com) (gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)) #1 Tue Mar 7 21:07:39 EST 2000

Cette commande indique que le systme s'excute sur une version 2.2.14 du noyau Linux, compil avec la version 1.1.2 de EGCS (EGC, l'Experimental GNU Compiler System, tait un prcurseur du projet GCC actuel). Les informations les plus intressantes de cet affichage, le nom de l'OS et la version et la rvision du noyau, sont galement disponibles dans des fichiers individuels de /proc. Il s'agit de /proc/sys/kernel/ostype, /proc/sys/kernel/ osrelease et /proc/sys/kernel/version, respectivement.
% cat /proc/sys/kernel/ostype Linux % cat /proc/sys/kernel/osrelease 2.2.14-5.0 % cat /proc/sys/kernel/version #1 Tue Mar 7 21:07:39 EST 2000

7-4-2 - Noms d'Hte et de Domaine


Les fichiers /proc/sys/kernel/hostname et /proc/sys/kernel/domainname contiennent les noms d'hte et de domaine de l'ordinateur, respectivement. Ces informations sont les mmes que celles renvoyes par l'appel systme uname, dcrit dans la Section 8.15.

7-4-3 - Utilisation Mmoire


Le fichier /proc/meminfo contient des informations sur l'utilisation mmoire du systme. Les informations sont prsentes la fois pour la mmoire physique et pour l'espace d'change (swap). Les trois premires lignes prsentent les totaux, en octets; les lignes suivantes reprennent ces informations en kilooctets -- par exemple:
% cat /proc/meminfo total: used: free: shared: buffers: cached: Mem: 529694720 519610368 10084352 82612224 10977280 82108416 Swap: 271392768 44003328 227389440 MemTotal: 517280 kB MemFree: 9848 kB MemShared: 80676 kB Buffers: 10720 kB Cached: 80184 kB BigTotal: 0 kB BigFree: 0 kB SwapTotal: 265032 kB SwapFree: 222060 kB

Cet affichage montre un total de 512 Mo de mmoire physique, dont environ 9 Mo sont libres et 258 Mo d'espace d'change (swap), dont 216 Mo sont libres. Dans la ligne correspondant la mmoire physique, trois autres valeurs sont prsentes: La colonne Shared affiche la quantit de mmoire partage actuellement alloue sur le systme (consultez la Section memoirepartagee, Mmoire Partage ). La colonne Buffers donne la mmoire alloue par Linux pour les tampons des priphriques bloc. Ces tampons sont utiliss par les pilotes de priphriques pour conserver les blocs de donnes lus ou crits sur le disque. La colonne Cached indique la mmoire alloue par Linux pour le cache de page. Cette mmoire est utilise pour mettre en cache les accs des fichiers mis en correspondance avec la mmoire.

Vous pouvez utiliser la commande free pour afficher les mmes informations sur la mmoire.
- 121 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

7-5 - Lecteurs et Systmes de Fichiers


Le systme de fichiers /proc contient galement des informations sur les lecteurs de disques prsents sur le systme et les systmes de fichiers monts qui y correspondent.

7-5-1 - Systmes de Fichiers


L'entre /proc/filesystems dresse la liste de tous les types de systmes de fichiers connus par le noyau. Notez qu'elle n'est pas trs utile car elle n'est pas complte: des systmes de fichiers peuvent tre chargs et dchargs dynamiquement sous forme de modules noyau. Le contenu de /proc/filesystems ne reflte que les types de systmes de fichiers lis statiquement au noyau ou actuellement chargs. D'autres types de systmes de fichiers peuvent tre disponibles sous forme de modules mais ne pas tre chargs.

7-5-2 - Lecteurs et Partitions


Le systme de fichiers /proc donne galement des informations sur les priphriques connects aux contrleurs IDE ou SCSI (si le systme en dispose). Sur un systme classique, le rpertoire /proc/ide peut contenir un ou deux sous-rpertoires, ide0 et ide1, relatifs aux contrleurs primaire et secondaire du systme(S'il est correctement configur, le noyau Linux peut prendre en charge des contrleurs IDE supplmentaires. Ils sont numrots de faon squentielle partir de ide2.). Ils contiennent d'autres sous-rpertoires correspondants aux priphriques physiques connects ces contrleurs. Les rpertoires de chaque contrleur ou priphrique peuvent tre absents si Linux n'a dtect aucun priphrique connect. Les chemins absolus des quatre priphriques IDE possibles sont prsents dans le Tableau periphide.

Chemins Absolus des Quatre Priphriques IDE Possibles Contrleur Primaire Primaire Secondaire Secondaire Priphrique Matre Esclave Matre Esclave Rpertoire /proc/ide/ide0/hda/ /proc/ide/ide0/hdb/ /proc/ide/ide1/hdc/ /proc/ide/ide1/hdd/

Consultez la Section periphmat, Priphriques Matriels , pour plus d'informations sur les noms des priphriques IDE. Chaque rpertoire de priphrique IDE contient plusieurs entres donnant accs des informations d'identification et de configuration sur le priphrique. Voici les plus utiles: model contient la chane d'identification du modle de priphrique. media donne le type de mdia lu par le priphrique. Les valeurs possibles sont disk, cdrom, tape (cassette), floppy (disquette) et UNKNOWN (inconnu). capacity indique la capacit du priphrique en blocs de 512 octets. Notez que pour les lecteurs de CD-ROM, cette valeur sera 2<sup>31</sup>-1, et non pas la capacit du disque prsent dans le lecteur. La valeur de capacity reprsente la capacit total du disque physique; la capacit des systmes de fichiers contenus dans les partitions du disque sera plus petite.

Par exemple, ces commandes vous montrent comment dterminer le type de mdia et le priphrique primaire connect au contrleur IDE secondaire. Dans ce cas, il s'agit d'un lecteur CD-ROM Toshiba.
% cat /proc/ide/ide1/hdc/media cdrom - 122 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) % cat /proc/ide/ide1/hdc/model TOSHIBA CD-ROM XM-6702B

Si des priphriques SCSI sont prsents, /proc/scsi/scsi contient la liste de leurs identifiants. Par exemple, son contenu peut ressembler cela:
% cat /proc/scsi/scsi Attached devices: Host: scsi0 Channel: 00 Id: 00 Lun: 00 Vendor: QUANTUM Model: ATLAS_V%%__%%9_WLS Rev: 0230 Type: Direct-Access ANSI SCSI revision: 03 Host: scsi0 Channel: 00 Id: 04 Lun: 00 Vendor: QUANTUM Model: QM39100TD-SW Rev: N491 Type: Direct-Access ANSI SCSI revision: 02

Cet ordinateur contient un contrleur SCSI simple canal (appel scsi0), auquel sont connects deux disques Quantum, avec les identifiants de priphrique SCSI 0 et 4. Le fichier /proc/partitions contient des informations sur les partitions des lecteurs de disque reconnus. Pour chaque partition, il dcrit les numros de priphrique majeur et mineur, le nombre de blocs de 1024 octets qu'elle contient et le nom du priphrique correspondant cette partition. L'entre /proc/sys/dev/cdrom/info donne diverses informations sur les fonctionnalits des lecteurs CD-ROM. Les informations n'ont pas besoin d'explication.
% cat /proc/sys/dev/cdrom/info CD-ROM information, Id: cdrom.c 2.56 1999/09/09 drive name: hdc drive speed: 48 drive # of slots: 0 Can close tray: 1 Can open tray: 1 Can lock tray: 1 Can change speed: 1 Can select disk: 0 Can read multisession: Can read MCN: 1 Reports media changed: Can play audio: 1

1 1

7-5-3 - Points de Montage


Le fichier /proc/mounts donne un aperu des systmes de fichiers monts. Chaque ligne correspond un descripteur de montage et donne le priphrique mont, le point de montage et d'autres informations. Notez que /proc/mounts contient les mmes informations que le fichier traditionnel /etc/mtab, qui est automatiquement mis jour par la commande mount. Voici les diffrents lments d'un descripteur de point de montage: Le premier lment de la ligne est le priphrique mont (consultez le autoref{chap:peripheriques}). Le second lment est le point de montage, l'emplacement dans le systme de fichiers racine o le contenu du systme de fichiers apparat. Pour le systme de fichiers racine lui-mme, le point de montage est /. Pour les espaces d'change, le point de montage est not swap. Le troisime lment est le type de systme de fichiers. Actuellement, la plupart des systmes GNU/Linux utilisent le systme de fichiers ext2 (ou ext3) pour les lecteurs de disques, mais des disques DOS ou Windows peuvent tre monts et seront alors de type fat ou vfat. La plupart des CD-ROM sont au format iso9660. Consultez la page de manuel de la commande mount pour une liste des types de systmes de fichiers.

- 123 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Les deux derniers lments des lignes de /proc/mounts sont toujours 0 et n'ont pas de signification. Consultez la page de manuel de fstab pour plus de dtail sur le format des descripteurs de point de montage(Le fichier /etc/fstab dcrit la configuration des points de montage statiques d'un systme GNU/Linux.). GNU/Linux dispose de fonction destines vous aider analyser ces descripteurs; consultez la page de manuel de la fonction getmntent pour plus d'informations sur leur utilisation.

7-5-4 - Verrous
La Section 8.3, fcntl : Verrous et Autres Oprations sur les Fichiers , dcrit l'utilisation de l'appel systme fcntl pour manipuler des verrous en lecture ou en criture sur les fichiers. Le fichier /proc/locks dcrit tous les verrous de fichiers actuellement en place sur le systme. Chaque ligne correspond un verrou. Pour les verrous crs avec fcntl, les deux premires mentions de la ligne sont POSIX ADVISORY. La troisime est WRITE ou READ, selon le type de verrou. Le nombre qui suit les l'identifiant du processus possdant le verrou. Les trois chiffres suivants, spars par deux-points, sont les numros de priphrique majeur et mineur du priphrique sur lequel se trouve le fichier et le numro d'inode, qui situe le fichier dans le systme de fichiers. Le reste de la ligne est constitu de valeurs utilises en interne par le noyau et qui ne sont gnralement d'aucune utilit. Traduire le contenu de /proc/locks en informations utiles demande un petit travail d'investigation. Vous pouvez observer l'volution de /proc/locks en excutant le programme du Listing 8.2 qui cre un verrou en lecture sur le fichier /tmp/test-file.
% touch /tmp/test-file % ./lock-file /tmp/test-file fichier /tmp/test-file ouverture de /tmp/test-file verrouillage verrouill ; appuyez sur entre pour dverrouiller...

Dans une autre fentre, observez le contenu de /proc/locks.


% cat /proc/locks 1: POSIX ADVISORY WRITE 5467 08:05:181288 0 2147483647 d1b5f740 00000000 dfea7d40 00000000 00000000

Il peut y avoir des lignes supplmentaires si d'autres programmes ont pos des verrous. Dans notre cas, 5467 est l'identifiant de processus du programme lock-file. Utilisez ps pour observer ce qu'est en train d'excuter ce processus.
% ps 5467 PID TTY STAT TIME COMMAND 5467 pts/28 S 0:00 ./lock-file /tmp/test-file

Le fichier verrouill, /tmp/test-file, se trouve sur le priphrique avec les numros majeur et mineur 8 et 5, respectivement. Ces nombres correspondent /dev/sda5.
% df /tmp Filesystem 1k-blocks Used Available Use% Mounted on /dev/sda5 8459764 5094292 2935736 63% / % ls -l /dev/sda5 brw-rw---1 root disk 8, 5 May 5 1998 /dev/sda5

Le fichier /tmp/test-file se trouve au niveau de l'inode 181 288 sur ce priphrique.

- 124 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) % ls --inode /tmp/test-file 181288 /tmp/test-file

Consultez la Section noperiph, Numros de Priphrique pour plus d'informations sur ceux-ci.

7-6 - Statistiques Systme


Deux entres de /proc contiennent des statistiques utiles sur le systme. Le fichier /proc/loadavg donne des informations sur sa charge. Les trois premiers nombres reprsentent le nombre de tches actives sur le systme -processus en cours d'excution -- avec une moyenne sur les 1, 5 et 15 dernires minutes. Le chiffre suivant indique le nombre courant de tches excutables -- processus programms pour tre excuts, l'oppos de ceux bloqus dans un appel systme -- et le nombre total de processus sur le systme. Le dernier champ correspond l'identifiant du processus ayant eu la main le plus rcemment. Le fichier /proc/uptime contient le temps coul depuis le dmarrage du systme, ainsi que la dure pendant laquelle le systme a t inactif. Ces deux valeurs sont donnes sous forme dcimale, en secondes.
% cat /proc/uptime 3248936.18 3072330.49

Le programme du Listing printuptime extrait le temps depuis lequel le systme est dmarr ainsi que sa dure d'inactivit et les affiche de faon lisible. Affiche des Informations sur les Temps Systme print-uptime.c
#include <stdio.h> /* Affiche une dure sur la sortie standard de faon lisible. TIME est la dure, en secondes, et LABEL est une lgende courte. */ void print_time (char* label, long time) { /* Constantes de conversion. */ const long minute = 60; const long hour = minute * 60; const long day = hour * 24; /* Affiche la dure. */ printf ("%s: %ld jours, %ld:%02ld:%02ld\n", label, time / day, (time % day) / hour, (time % hour) / minute, time % minute); } int main () { FILE* fp; double uptime, idle_time; /* Lit le temps coul depuis le dmarrage et le temps d'inactivit partir de /proc/uptime. */ fp = fopen ("/proc/uptime", "r"); fscanf (fp, "%lf %lf\n", &uptime, &idle_time); fclose (fp); /* L'affiche. */ print_time ("Temps coul depuis le dmarrage ", (long) uptime); print_time ("Temps d'inactivit ", (long) idle_time); return 0; }

La commande uptime et l'appel systme sysinfo (dcrit dans la Section 8.14, sysinfo : Obtenir des Statistiques Systme ) permettent galement d'obtenir le temps coul depuis le dmarrage. La commande uptime affiche galement les moyennes de charge systme contenues dans /proc/loadavg.

- 125 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

8 - Appels Systme Linux


Jusqu'ici, nous avons prsent diverses fonctions que votre programme peut utiliser pour accomplir des actions relatives au systme, comme analyser des options de ligne de commande, manipuler des processus et mapper de la mmoire. Si vous y regardez de plus prs, vous remarquerez que ces fonctions se rpartissent en deux catgories, selon la faon dont elles sont implantes. Une fonction de bibliothque est une fonction ordinaire qui se trouve dans une bibliothque externe votre programme. La plupart des fonctions que nous avons prsent jusqu'ici se trouvent dans la bibliothque standard du C, libc. Par exemple, getopt_long et mkstemp en font partie. Un appel une fonction de bibliothque est identique l'appel de n'importe quelle autre fonction. Les arguments sont placs dans des registres du processeur ou sur la pile et l'excution est transfre au dbut de la fonction qui se trouve gnralement dans une bibliothque partage. Un appel systme est implant au sein du noyau Linux. Lorsqu'un programme effectue un appel systme, les arguments sont mis en forme et transfrs au noyau qui prend la main jusqu' la fin de l'appel. Un appel systme n'est pas identique un appel de fonction classique et une procdure spcifique est ncessaire pour transfrer le contrle au noyau. Cependant, la bibliothque C GNU (l'implmentation de la bibliothque standard du C fournie avec les systmes GNU/Linux) masque les appels systmes par des fonctions classiques afin qu'ils soient plus simples utiliser. Les fonctions d'E/S de bas niveau comme open ou read font partie des appels systmes Linux. Les appels systme Linux constituent l'interface de base entre les programmes et le noyau Linux. chaque appel correspond une opration ou une fonctionnalit de base. Certains appels sont trs puissants et influent au niveau du systme. Par exemple, il est possible d'teindre le systme ou d'utiliser des ressources systme tout en interdisant leur accs aux autres utilisateurs. De tels appels ne sont utilisables que par des programmes s'excutant avec les privilges superutilisateur (lanc par root). Ils chouent si le programme ne dispose pas de ces droits.

Notez qu'une fonction de bibliothque peut son tour appeler une ou plusieurs fonctions de bibliothques ou appels systme. Linux propose prs de 300~appels systme. La liste des appels disponibles sur votre systme se trouve dans le fichier /usr/include/asm/unistd.h. Certains ne sont destins qu' tre utiliss en interne par le noyau et d'autres ne servent que pour l'implmentation de certaines bibliothques. Dans ce chapitre, nous vous prsenterons ceux qui nous semblent les plus susceptibles de vous servir. La plupart sont dclars dans le fichier d'en-tte /usr/include/asm/unistd.h.

8-1 - Utilisation de strace


Avant de commencer parler des appels systme, il est ncessaire de prsenter une commande qui peut vous en apprendre beaucoup sur les appels systme. La commande strace trace l'excution d'un autre programme en dressant la liste des appels systme qu'il effectue et des signaux qu'il reoit. Pour observer l'enchanement des appels systme et des signaux d'un programme invoquez simplement strace, suivi du nom du programme et de ses arguments. Par exemple, pour observer les appels systmes effectus par la commande hostname(Invoque sans aucune option, la commande hostname affiche simplement le nom d'hte de la machine sur la sortie standard.), utilisez cette commande:
% strace hostname

Elle vous affichera un certain nombre d'informations. Chaque ligne correspond un appel systme. Pour chaque appel, vous trouverez son nom suivi de ses arguments (ou de leur abrviation s'ils sont trop longs) et de sa valeur de retour. Lorsque c'est possible, strace utilise des noms symboliques pour les arguments et la valeur de retour plutt

- 126 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

que leur valeur numrique et affiche les diffrents champs des structures passes via un pointeur l'appel systme. Notez que strace ne montre pas les appels de fonctions classiques. La premire ligne affiche par strace hostname montre l'appel systme execve qui invoque le programme hostname(Sous Linux, la famille de fonction exec est implmente via l'appel systme execve.):
execve("/bin/hostname", ["hostname"], [/* 49 vars */]) = 0

Le premier argument est le nom du programme excuter; le second sa liste d'arguments, constitue d'un seul lment; et le troisime l'environnement du programme que strace n'affiche pas par souci de concision. Les 30 lignes suivantes, approximativement, font partie du mcanisme qui charge la bibliothque standard du~C partir d'un fichier de bibliothque partag. Les appels systmes effectivement utiliss par le programme pour fonctionner se trouvent vers la fin de l'affichage. L'appel systme uname permet d'obtenir le nom d'hte du systme partir du noyau:
uname({sys="Linux", node="myhostname", ...}) = 0

Remarquez que strace donne le nom des champs (sys et node) de la structure passe en argument. Cette structure est renseigne par l'appel systme ? Linux place le nom du systme d'exploitation dans le champ sys et le nom d'hte du systme dans le champ node. L'appel systme uname est dtaill dans la Section 8.15, uname . Enfin, l'appel systme write affiche les informations. Souvenez-vous que le descripteur de fichier~1 correspond la sortie standard. Le troisime argument est le nombre de caractres crire et la valeur de retour est le nombre de caractres effectivement crits.
write(1, "myhostname\n", 11) = 11

L'affichage peut paratre un peu confus lorsque vous excutez strace car la sortie du programme hostname est mlange avec celle de strace. Si le programme que vous analysez affiche beaucoup d'informations, il est parfois plus pratique de rediriger la sortie de strace vers un fichier. Pour cela, utilisez l'option -o nom_de_fichier. La comprhension de tout ce qu'affiche strace ncessite une bonne connaissance du fonctionnement du noyau Linux et de l'environnement d'excution ce qui prsente un intrt limit pour les programmeurs d'application. Cependant, une comprhension de base est utile pour dboguer des problmes sournois et comprendre le fonctionnement d'autres programmes.

8-2 - access : Tester les Permissions d'un Fichier


L'appel systme access dtermine si le processus appelant le droit d'accder un fichier. Il peut vrifier toute combinaison des permissions de lecture, criture ou excution ainsi que tester l'existence d'un fichier. L'appel access prend deux argument. Le premier est le chemin d'accs du fichier tester. Le second un OU binaire entre R_OK, W_OK et X_OK, qui correspondent aux permissions en lecture, criture et excution. La valeur de retour est zro si le processus dispose de toutes les permissions passes en paramtre. Si le fichier existe mais que le processus n'a pas les droits dessus, access renvoie~-1 et positionne errno EACCES (ou EROFS si l'on a test les droits en criture d'un fichier situ sur un systme de fichiers en lecture seule). Si le second argument est F_OK, access vrifie simplement l'existence du fichier. Si le fichier existe, la valeur de retour est~0; sinon, elle vaut~-1 et errno est positionn ENOENT. errno peut galement tre positionn EACCES si l'un des rpertoires du chemin est inaccessible.
- 127 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Le programme du Listing checkaccess utilise access pour vrifier l'existence d'un fichier et dterminer ses permissions en lecture et en criture. Spcifiez le nom du fichier vrifier sur la ligne de commande. Vrifier les Droits d'Accs un Fichier check-access.c
#include <errno.h> #include <stdio.h> #include <unistd.h> int main (int argc, char* argv[]) { char* path = argv[1]; int rval; /* Vrifie l'existence du fichier. */ rval = access (path, F_OK); if (rval == 0) printf ("%s existe\n", path); else { if (errno == ENOENT) printf ("%s n'existe pas\n", path); else if (errno == EACCES) printf ("%s n'est pas accessible\n", path); return 0; } /* Vrifie l'accs en lecture. */ rval = access (path, R_OK); if (rval == 0) printf ("%s est accessible en lecture\n", path); else printf ("%s n'est pas accessible en lecture (accs refus)\n", path); /* Vrifie l'accs en criture. */ rval = access (path, W_OK); if (rval == 0) printf ("%s est accessible en criture\n", path); else if (errno == EACCES) printf ("%s n'est pas accessible en criture (accs refus)\n", path); else if (errno == EROFS) printf ("%s n'est pas accessible en criture (SF en lecture seule)\n", path); return 0;

Par exemple, pour tester les permissions d'accs un fichier appel README situ sur un CD-ROM, invoquez le programme comme ceci:
% ./check-access /mnt/cdrom/README /mnt/cdrom/README existe /mnt/cdrom/README est accessible en lecture /mnt/cdrom/README n'est pas accessible en criture (SF en lecture seule)

8-3 - fcntl : Verrous et Oprations sur les Fichiers


L'appel systme fcntl est le point d'accs de plusieurs oprations avances sur les descripteurs de fichiers. Le premier argument de fcntl est un descripteur de fichiers ouvert et le second est une valeur indiquant quelle opration doit tre effectue. Pour certaines d'entre-elles, fcntl prend un argument supplmentaire. Nous dcrirons ici l'une des oprations les plus utiles de fcntl: le verrouillage de fichier. Consultez la page de manuel de fcntl pour plus d'informations sur les autres oprations. L'appel systme fcntl permet un programme de placer un verrou en lecture ou en criture sur un fichier, d'une faon similaire celle utilise pour les verrous mutex traits dans le Chapitre threads, Threads . Un verrou en lecture se place sur un descripteur de fichier accessible en lecture et un verrou en criture sur un descripteur de

- 128 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

fichier accessible en criture. Plusieurs processus peuvent dtenir un verrou en lecture sur le mme fichier au mme moment, mais un seul peut dtenir un verrou en criture et le mme fichier ne peut pas tre verrouill la fois en lecture et en criture. Notez que le fait de placer un verrou n'empche pas rellement les autres processus d'ouvrir le fichier, d'y lire des donnes ou d'y crire, moins qu'il ne demandent eux aussi un verrou avec fcntl. Pour placer un verrou sur un fichier, il faut tout d'abord crer une variable struct flock et la remplir de zros. Positionnez le champ l_type de la structure F_RDLCK pour un verrou en lecture ou F_WRLCK pour un verrou en criture. Appelez ensuite fcntl en lui passant le descripteur du fichier verrouiller, le code d'opration F_SETLKW et un pointeur vers la variable struct flock. Si un autre processus dtient un verrou qui empche l'acquisition du nouveau, l'appel fcntl bloque jusqu' ce que ce verrou soit relch. Le programme du Listing lockfile ouvre en criture le fichier dont le nom est pass en paramtre puis place un verrou en criture dessus. Le programme attend ensuite que l'utilisateur appuie sur la touche Entre puis dverrouille et ferme le fichier. Cre un Verrou en criture avec fcntl lock-file.c
#include #include #include #include <fcntl.h> <stdio.h> <string.h> <unistd.h>

int main (int argc, char* argv[]) { char* file = argv[1]; int fd; struct flock lock; printf ("ouverture de %s\n", file); /* Ouvre un descripteur de fichier. */ fd = open (file, O_WRONLY); printf ("verrouillage\n"); /* Initialise la structure flock. */ memset (&lock, 0, sizeof(lock)); lock.l_type = F_WRLCK; /* Place un verrou en criture sur le fichier. */ fcntl (fd, F_SETLKW, &lock); printf ("verrouill ; appuyez sur Entre pour dverrouiller... "); /* Attend l'appui sur Entre. */ getchar (); printf ("dverrouillage\n"); /* Libre le verrou. */ lock.l_type = F_UNLCK; fcntl (fd, F_SETLKW, &lock); close (fd); return 0;

Compilez et lancez le programme sur un fichier test ? par exemple, /tmp/fichier-test ? comme suit:
% cc -o lock-file lock-file.c % touch /tmp/fichier-test % ./lock-file /tmp/fichier-test ouverture de /tmp/fichier-test verrouillage verrouill ; appuyez sur Entre pour dverouiller...

Maintenant, dans une autre fentre, essayez de le lancer sur le mme fichier:
% ./lock-file /tmp/fichier-test - 129 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) ouverture de /tmp/fichier-test verrouillage

Notez que la seconde instance est bloqu lors de la tentative de verrouillage du fichier. Revenez dans la premire fentre et appuyez sur Entre:
Dverrouillage

Le programme s'excutant dans la seconde fentre acquiert immdiatement le verrou. Si vous prfrez que fcntl ne soit pas bloquant si le verrou ne peut pas tre obtenu, utilisez F_SETLK au lieu de F_SETLKW. Si le verrou ne peut pas tre acquis, fcntl renvoie~-1 immdiatement. Linux propose une autre implmentation du verrouillage de fichiers avec l'appel flock. La fonction fcntl dispose d'un avantage majeur: elle fonctionne sur les fichiers se trouvant sur un systme de fichiers NFS(Network File System (NFS) est une technologie de partage de fichiers courante, comparable aux partages et aux lecteurs rseau Windows.) (si tant est que le serveur NFS soit relativement rcent et correctement configur). Ainsi, si vous disposez de deux machines qui ont toutes deux le mme systme de fichiers mont via NFS, vous pouvez reproduire l'exemple ci-dessus en utilisant deux machines diffrentes. Lancez lock-file sur une machine en lui passant un fichier situ sur le systme de fichiers NFS puis lancez le sur la seconde en lui passant le mme fichier. NFS relance le second programme lorsque le verrou est relch par le premier.

8-4 - fsync et fdatasync : Purge des Tampons Disque


Sur la plupart des systme d'exploitation, lorsque vous crivez dans un fichier, les donnes ne sont pas immdiatement crites sur le disque. Au lieu de cela, le systme d'exploitation met en cache les donnes crites dans un tampon en mmoire, pour rduire le nombre d'critures disque requises et amliorer la ractivit du programme. Lorsque le tampon est plein ou qu'un vnement particulier survient (par exemple, au bout d'un temps donn), le systme crit les donnes sur le disque. Linux fournit un systme de mise en cache de ce type. Normalement, il s'agit d'une bonne chose en termes de performances. Cependant, ce comportement peut rendre instables les programmes qui dpendent de l'intgrit de donnes stockes sur le disque. Si le systme s'arrte soudainement ? par exemple, en raison d'un crash du noyau ou d'une coupure de courant ? toute donne crite par le programme qui rside dans le cache en mmoire sans avoir t crite sur le disque est perdue. Par exemple, supposons que vous criviez un programme de gestion de transactions qui tient un fichier journal. Ce dernier contient les enregistrements concernant toutes les transactions qui ont t traites afin que si une panne systme survient, le statut des donnes impliques par les transactions puisse tre restaur. Il est bien sr important de prserver l'intgrit du fichier journal ? chaque fois qu'une transaction a lieu, son entre dans le journal doit tre envoye immdiatement sur le disque dur. Pour faciliter l'implmentation de tels mcanismes, Linux propose l'appel systme fsync. Celui-ci prend un seul argument, un descripteur de fichier ouvert en criture, et envoie sur le disque toutes les donnes crites dans le fichier. L'appel fsync ne se termine pas tant que les donnes n'ont pas t physiquement crites. La fonction du Listing writejournalentry illustre l'utilisation de fsync. Il crit un enregistrement d'une ligne dans un fichier journal. crit et Synchronise un enregistrement write_journal_entry.c
#include #include #include #include #include <fcntl.h> <string.h> <sys/stat.h> <sys/types.h> <unistd.h>

- 130 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

crit et Synchronise un enregistrement write_journal_entry.c


const char* journal_filename = "journal.log"; void write_journal_entry (char* entry) { int fd = open (journal_filename, O_WRONLY | O_CREAT | O_APPEND, 0660); write (fd, entry, strlen (entry)); write (fd, "\n", 1); fsync (fd); close (fd); }

Un autre appel systme, fdatasync, a la mme fonction. Cependant, alors que fsync garantit que la date de modification du fichier sera mise jour, ce n'est pas le cas de fdatasync; ce dernier ne garantit que le fait que les donnes seront crites. Cela signifie qu'en gnral, fdatasync peut s'excuter plus vite que fsync car il n'a qu'une seule criture effectuer au lieu de deux. Cependant, sur les version actuelles de Linux, ces deux appels systme font en fait la mme chose, mettant tous deux jour la date de modification du fichier. L'appel systme fsync vous permet de forcer explicitement l'criture d'un tampon. Vous pouvez galement ouvrir un fichier en mode entres/sorties synchrones, ce qui signifie que toutes les critures sont envoyes sur le disque immdiatement. Pour cela, passez l'option O_SYNC lors de l'ouverture du fichier avec open.

8-5 - getrlimit et setrlimit : Limites de Ressources


Les appels systme getrlimit et setrlimit permettent un processus de connatre et de dfinir des limites sur les ressources systme qu'il peut consommer. Vous connaissez peut tre la commande shell ulimit, qui vous permet de restreindre la consommation de ressources des programmes que vous excutez(Consultez la page de manuel de votre shell pour plus d'informations sur ulimit.); ces appels systme permettent un programme de faire la mme chose par programmation. Pour chaque ressource il existe deux limites, la limite stricte et la limite souple. La limite souple ne doit jamais dpasser la limite dure. Typiquement, une application rduira la limite souple pour viter une monte en puissance de sa consommation de ressources. getrlimit et setrlimit prennent tous deux en argument un code spcifiant le type de limite de ressource et un pointeur vers une variable struct rlimit. L'appel getrlimit renseigne les champs de cette structure, tandis que setrlimit modifie la limite selon son contenu. La structure rlimit a deux champs: rlim_cur qui est la limite souple et rlim_max qui est la limite stricte. Voici une liste des limites de ressources pouvant tre modifies les plus utiles, avec le code correspondant: RLIMIT_CPU ? Temps processeur maximum, en secondes, utilis par un programme. Il s'agit du temps pendant lequel le programme utilise effectivement le processeur, qui n'est pas forcment le temps d'excution du programme. Si le programme dpasse cette limite, il est interrompu par un

signal SIGXCPU. RLIMIT_DATA ? Quantit maximale de mmoire qu'un programme peut allouer pour ses donnes. Toute allocation au-del de cette limite chouera. RLIMIT_NPROC ? Nombre maximum de processus fils pouvant tre excuts par l'utilisateur. Si le processus appel fork et que trop de processus appartenant l'utilisateur sont en cours d'excution, fork chouera. RLIMIT_NOFILE ? Nombre maximum de descripteurs de fichiers que le processus peut ouvrir en mme temps.

- 131 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Consultez la page de manuel de setrlimit pour une liste complte des ressources systme. Le programme du Listing limitcpu illustre l'utilisation de la limite de temps processeur consomm par un programme. Il dfinit un temps processeur de une seconde puis entre dans une boucle infinie. Linux tue le processus peu aprs, lorsqu'il dpasse la seconde de temps processeur. Dmonstration de la Limite de Temps Processeur limit-cpu.c
#include <sys/resource.h> #include <sys/time.h> #include <unistd.h> int main () { struct rlimit rl; /* Rcupre la limite courante. */ getrlimit (RLIMIT_CPU, &rl); /* Dfinit une limite de temps processeur d'une seconde. */ rl.rlim_cur = 1; setrlimit (RLIMIT_CPU, &rl); /* Occupe le programme. */ while (1); } return 0;

Lorsque le programme est termin par SIGXCPU, le shell affiche un message interprtant le signal:
% ./limit_cpu Temps UCT limite expir

8-6 - getrusage : Statistiques sur les Processus


L'appel systme getrusage obtient des statistiques sur un processus partir du noyau. Il peut tre utilis pour obtenir des statistiques pour le processus courant en passant RUSAGE_SELF comme premier argument ou pour les processus fils termins qui ont t crs par ce processus et ses fils en passant RUSAGE_CHILDREN. Le second argument de getrusage est un pointeur vers une variable de type struct rusage, qui est renseigne avec les statistiques. Voici quelques-uns des champs les plus intressant d'une struct rusage: ru_utime ? Champ de type struct timeval contenant la quantit de temps utilisateur, en secondes, que le processus a utilis. Le temps utilisateur est le temps processeur pass excut le programme par opposition celui pass dans le noyau pour des appels systme. ru_stime ? Champ de type struct timeval contenant la quantit de temps systme, en seconde, que le processus a utilis. Le temps systme est le temps processeur pass excut des appels systme la demande du processus. ru_maxrss ? Quantit maximale de mmoire physique occupe par le processus au cours son excution.

La page de manuel de getrusage liste tous les champs disponibles. Consultez la Section gettimeofday, gettimeofday : Heure Systme pour plus d'informations sur le type struct timeval. La fonction du Listing printcputimes affiche les temps systme et utilisateur du processus en cours. Affiche les Temps Utilisateur et Processeur print-cpu-times.c
#include <stdio.h> #include <sys/resource.h> - 132 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Affiche les Temps Utilisateur et Processeur print-cpu-times.c


#include <sys/time.h> #include <unistd.h>

void print_cpu_time() { struct rusage usage; getrusage (RUSAGE_SELF, &usage); printf ("Temps processeur : %ld.%06lds utilisateur, %ld.%06lds systme\n", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec); }

8-7 - gettimeofday : Heure Systme


L'appel systme gettimeofday renvoie l'heure systme. Il prend un pointeur vers une variable de type struct timeval. Cette structure reprsente un temps, en secondes, spar en deux champs. Le champ tv_sec contient la partie entire du nombre de secondes et le champ tv_usec la fraction de microsecondes. La valeur de la struct timeval reprsente le nombre de secondes coul depuis le dbut de l'epoch UNIX, c'est--dire le premier janvier 1970 minuit UTC. L'appel gettimeofday prend galement un second argument qui doit tre NULL. Incluez <sys/time.h> si vous utilisez cet appel systme. Le nombre de secondes depuis l'epoch UNIX n'est gnralement pas une faon trs pratique de reprsenter les dates. Les fonctions de la bibliothque standard localtime et strftime aident manipuler les valeurs renvoyes par gettimeofday. La fonction localtime prend un pointeur vers un nombre de secondes (le champ tv_sec de struct timeval) et renvoie un pointeur vers un objet struct tm. Cette structure contient des champs plus utiles renseigns selon le fuseau horaire courant: tm_hour, tm_min, tm_sec ? Heure du jour en heures, minutes et secondes. tm_year, tm_mon, tm_day ? Anne, mois, jour. tm_wday ? Jour de la semaine. Zro reprsente le Dimanche. tm_yday ? Jour de l'anne. tm_isdst ? Drapeau indiquant si l'heure d't est en vigueur ou non.

La fonction strftime permet de produire partir d'un pointeur vers une struc tm une chane personnalise et formate reprsentant la date et l'heure. Le format est spcifi d'une faon similaire printf, sous forme d'une chane contenant des codes qui indiquent les champs inclure. Par exemple, la chane de format
"%Y-%m-%d %H:%M:%S"

Correspond une date de la forme:


2006-07-15 21:00:42

Passez strftime un tampon pour recevoir la chane, la longueur du tampon, la chane de format et un pointeur vers une variable struct tm. Consultez la page de manuel de strftime pour une liste complte des codes qui peuvent tre utiliss dans la chane de format. Notez que ni localtime ni strftime ne prennent en compte la partie fractionnaire de l'heure courante avec une prcision suprieure la seconde. Si vous voulez exploiter le champ tv_usec de la struct timeval, vous devrez le faire manuellement. Incluez <time.h> si vous appelez localtime ou strftime. La fonction du Listing printtime affiche la date et l'heure courante, avec une prcision de l'ordre de la milliseconde. Affiche la Date et l'Heure print-time.c

- 133 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Affiche la Date et l'Heure print-time.c


#include #include #include #include <stdio.h> <sys/time.h> <time.h> <unistd.h>

void print_time () { struct timeval tv; struct tm* ptm; char time_string[40]; long milliseconds; /* Rcupre l'heure courante et la convertit en struct tm. */ gettimeofday (&tv, NULL); ptm = localtime (&tv.tv_sec); /* Formate la date et l'heure la seconde prs. */ strftime (time_string, sizeof (time_string), "%Y-%m-%d %H:%M:%S", ptm); /* Calcule les millisecondes partir des microsecondes. */ milliseconds = tv.tv_usec / 1000; /* Affiche l'heure de faon formate, en secondes, suivie d'un point dcimal et des millisecondes. */ printf ("%s.%03ld\n", time_string, milliseconds);

8-8 - La famille mlock : Verrouillage de la Mmoire Physique


La famille d'appels systme mlock permet un programme de verrouiller tout ou partie de son espace d'adressage en mmoire physique. Cela vite que Linux ne l'envoie vers l'espace d'change, mme si le programme n'y accde pas pendant quelques temps. Un programme pour lequel le temps est une ressource critique peut verrouiller la mmoire physique car le temps ncessaire au processus d'change peut tre trop long ou trop imprvisible. Un programme sensible au niveau de la scurit pourrait vouloir empcher l'envoi de donnes critiques vers un espace d'change partir duquel elles pourraient tre rcupres aprs la fin du programme. Le verrouillage d'une rgion de la mmoire consiste simplement appeler mlock en lui passant un pointer vers le dbut de la rgion ainsi que la longueur de la rgion. Linux divise la mmoire en pages et ne peut verrouiller que des pages dans leur intgralit; chaque page qui contient une partie de la rgion de la mmoire passe mlock est verrouille. La fonction getpagesize renvoie la taille de page du systme qui est de~4Ko sous Linux x86. Par exemple pour allouer 32Mo d'espace d'adressage et le verrouiller en RAM, vous utiliserez ce code:
const int alloc_size = 32 * 1024 * 1024; char* memory = malloc (alloc_size); mlock (memory, alloc_size);

Notez que le simple fait d'allouer une page mmoire et de la verrouiller avec mlock ne rserve pas de mmoire physique pour le processus appelant car les pages peuvent tre en copie l'criture(La copie l'criture (copy on write) signifie que Linux ne fait une copie prive de la page que lorsque le processus crit une valeur l'intrieur.). Vous devriez donc crire une valeur quelconque sur chaque page:
size_t i; size_t page_size = getpagesize (); for (i = 0; i < alloc_size; i += page_size) memory[i] = 0;

Le fait d'crire sur toutes les pages force Linux allouer une page mmoire unique, non partage, au processus pour chacune.

- 134 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour dverrouiller une rgion, appelez munlock, qui prend les mmes arguments que mlock. Si vous voulez que l'intgralit de l'espace d'adressage de votre programme soit verrouill en mmoire physique, appelez mlockall. Cet appel systme ne prend qu'un seul argument: MCL_CURRENT verrouille toute la mmoire alloue mais les allocations suivantes ne sont pas verrouilles; MCL_FUTURE verrouille toutes les pages qui sont alloues aprs l'appel. Utilisez MCL_CURRENT|MCL_FUTURE pour verrouiller en mmoire la fois les allocation en cours et les futures allocations. Le verrouillage de grandes quantits de mmoire, en particulier en via mlockall, peut tre dangereux pour tout le systme Linux. Le verrouillage de mmoire sans discernement est un bon moyen de ralentir considrablement le systme au point de le faire s'arrter car les autres processus en cours d'excution doivent s'arranger avec des ressources en mmoire physique moindres et avec le fait d'tre envoy et repris depuis l'espace d'change. Si vous verrouillez trop de mmoire, le systme sera totalement court de mmoire et Linux commencera tuer des processus. Pour cette raison, seuls les processus avec les privilges de superutilisateur peuvent verrouiller de la mmoire avec mlock ou mlockall. Si un processus ne disposant pas de ces privilges appel l'une de ces fonctions, elle chouera en renvoyant~-1 et en positionnant errno EPERM. L'appel munlockall dverrouille toute la mmoire verrouille par le processus courant, qu'elle ait t verrouille par mlock ou mlockall. Un moyen pratique de surveiller l'utilisation mmoire de votre programme est d'utiliser la commande top. La colonne VIRT indique la taille de l'espace d'adressage virtuel de chaque programme (cette taille inclut le code, les donnes et la pile dont certains peuvent tre envoys vers l'espace d'change). La colonne RES (pour resident size) indique la quantit de mmoire physique effectivement occupe par le programme. La somme de toutes les valeurs prsentes dans la colonne RES pour tous les programmes en cours d'excution ne peut pas excder la taille de la mmoire physique de votre ordinateur et la somme de toutes les tailles d'espace d'adressage est limite 2Go(NdT Cette limite n'est plus d'actualit avec les sries 2.4 et 2.6.) (pour les versions 32~bits de Linux). Incluez <sys/mman.h> si vous utilisez l'un des appels systme mlock.

8-9 - mprotect : Dfinir des Permissions Mmoire


Dans la Section mmap, Mmoire Mappe , nous avons montr comment utiliser l'appel systme mmap pour mettre en correspondance un fichier avec la mmoire. Souvenez vous que le troisime argument de mmap est un ou binaire entre les indicateurs de protection mmoire PROT_READ, PROT_WRITE et PROT_EXEC pour des permissions en lecture, criture ou excution, respectivement, ou PROT_NONE pour empcher l'accs la mmoire. Si un programme tente d'effectuer une opration sur un emplacement mmoire sur lequel il n'a pas les bonnes permissions, il se termine sur la rception d'un signal SIGSEGV (erreur de segmentation). Une fois que la mmoire a t mappe, ces permissions peuvent tre modifie par l'appel systme mprotect. Les arguments de mprotect sont l'adresse d'une rgion mmoire, la taille de cette rgion et un jeu d'indicateurs de protection. La rgion mmoire consiste en un ensemble de pages compltes: l'adresse de la rgion doit tre aligne sur la taille de page systme et la longueur de la rgion doit tre un multiple de la taille de page. Les indicateurs de protection de ces pages sont remplacs par la valeur passe en paramtre. Notez que les rgions mmoire renvoyes par malloc ne sont gnralement pas alignes sur des pages, mme si la taille de la mmoire est un multiple de la taille de page. Si vous voulez protger de la mmoire obtenue via malloc, vous devez allouer une rgion plus importante que celle dsire et trouver une sous-rgion qui soit aligne sur une page. Vous pouvez galement utiliser l'appel systme mmap pour court-circuiter malloc et allouer de la mmoire aligne sur des pages directement partir du noyau Linux. Consultez la Section mmap, Mmoire Mappe , pour plus de dtails.

- 135 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Par exemple, supposons que votre programme alloue une page mmoire en mappant /dev/zero, comme dcrit dans la Section mmapautres, Autres Utilisations de mmap . La mmoire est initialement en lecture/criture.
int fd = open ("/dev/zero", O_RDONLY); char* memory = mmap (NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); close (fd);

Plus loin, votre programme peut protger la mmoire en criture en appelant mprotect:
mprotect (memory, page_size, PROT_READ);

une technique avance pour surveiller les accs mmoire est de protger une rgion mmoire avec mmap ou mprotect puis de grer le signal SIGSEGV que Linux envoie au programme lorsqu'il tente d'accder cette mmoire. L'exemple du Listing mprotect illustre cette technique. Dtecter un Accs Mmoire en Utilisant mprotect mprotect.c
#include #include #include #include #include #include #include #include <fcntl.h> <signal.h> <stdio.h> <string.h> <sys/mman.h> <sys/stat.h> <sys/types.h> <unistd.h>

static int alloc_size; static char* memory; void segv_handler (int signal_number) { printf ("accs mmoire !\n"); mprotect (memory, alloc_size, PROT_READ | PROT_WRITE); } int main () { int fd; struct sigaction sa; /* Installe segv_handler comme gestionnaire de signal SIGSEGV. memset (&sa, 0, sizeof (sa)); sa.sa_handler = &segv_handler; sigaction (SIGSEGV, &sa, NULL); */

/* Alloue une page de mmoire en mappant /dev/zero. Mappe la mmoire en criture seule initialement. */ alloc_size = getpagesize (); fd = open ("/dev/zero", O_RDONLY); memory = mmap (NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0); close (fd); /* crit sur la page pour en obtenir une copie prive. */ memory[0] = 0; /* Protge la mmoire en criture. */ mprotect (memory, alloc_size, PROT_NONE); /* crit dans la rgion qui vient d'tre alloue. */ memory[0] = 1; /* Termin ; libre la mmoire. */ printf ("Fini\n"); munmap (memory, alloc_size); - 136 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Dtecter un Accs Mmoire en Utilisant mprotect mprotect.c


} return 0;

Le programme effectue les oprations suivantes: - Il dclare un gestionnaire de signal pour SIGSEGV; - Il alloue une page mmoire en mappant /dev/zero et en crivant une valeur dans la page obtenue pour en obtenir une copie prive. - Il protge la mmoire en appelant mprotect avec l'option PROT_NONE; - Lorsque le programme tente d'crire en mmoire, Linux envoie un SIGSEGV qui est pris en charge par segv_handler. Le gestionnaire de signal supprime la protection de la mmoire ce qui autorise l'accs; - Lorsque le gestionnaire de signal se termine, le contrle repasse main, o le programme libre la mmoire via munmap.

8-10 - nanosleep : Pause en Haute Prcision


L'appel systme nanosleep est une version en haute prcision de l'appel UNIX sleep. Au lieu de suspendre l'excution pendant un nombre entier de secondes, nanosleep prend comme argument un pointeur vers un objet de type struct timespec, qui peut indiquer un temps la nanoseconde prs. Cependant, en raison de dtails d'implmentation du noyau Linux, la prcision fournie par nanosleep n'est que de 10~millisecondes ? c'est toujours mieux que celle offerte par sleep. Cette prcision supplmentaire peut tre utile, par exemple, pour ordonnancer des oprations frquentes avec de faibles intervalles de temps entre elles. La structure struct timespec a deux champs: tv_sec, le nombre entier de secondes et tv_nsec, un nombre supplmentaire de nanosecondes. La valeur de tv_nsec doit tre infrieure 10<sup>9</sup>. L'appel nanosleep offre un autre avantage par rapport sleep. Comme pour sleep, l'arrive d'un signal interrompt l'excution de nanosleep, qui positionne alors errno EINTR et renvoie~-1. Cependant, nanosleep prend un second argument, un autre pointeur vers un objet struct timespec, qui, s'il n'est pas NULL, est renseign avec le temps de pause qu'il restait faire (c'est--dire la diffrence entre le temps de suspension demand le temps de suspension effectif). Cela facilite la reprise de l'opration de suspension. La fonction du Listing bettersleep fournit une implmentation alternative de sleep. Contrairement l'appel systme classique, cette fonction prend en paramtre une valeur en virgule flottante correspondant la dure en secondes pour laquelle il faut suspendre l'excution et reprend l'opration de suspension si elle est interrompue. Fonction de Suspension Haute prcision better-sleep.c
#include <errno.h> #include <time.h> int better_sleep (double sleep_time) { struct timespec tv; /* Construit l'objet timespec partir du nombre entier de seconde... tv.tv_sec = (time_t) sleep_time; /* ... et le reste en nanosecondes. */ tv.tv_nsec = (long) ((sleep_time - tv.tv_sec) * 1e+9); while (1) { /* Suspend l'excution pour un temps spcifi par tv. Si l'on est

*/

- 137 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Fonction de Suspension Haute prcision better-sleep.c

} return 0;

interrompu par un signal, le temps restant est replac dans tv. */ int rval = nanosleep (&tv, &tv); if (rval == 0) /* On a suspendu l'excution pour le temps demand ; termin. */ return 0; else if (errno == EINTR) /* Interrompu par un signal. Ressaie. */ continue; else /* Autre erreur, abandon. */ return rval;

8-11 - readlink : Lecture de Liens Symboliques


L'appel systme readlink rcupre la cible d'un lien symbolique. Il prend trois arguments: le chemin vers le lien symbolique, un tampon pour recevoir la cible du lien et sa longueur. Une particularit de readlink est qu'il n'insre pas de caractre NUL la fin de la chane qu'il place dans le tampon. Cependant, il renvoie le nombre de caractres composant le chemin cible, placer soi-mme le caractre NUL est donc simple. Si le premier argument de readlink pointe vers un fichier qui n'est pas un lien symbolique, readlink positionne errno EINVAL et renvoie~-1. Le petit programme du Listing printsymlink affiche la cible du lien symbolique pass sur la ligne de commande. Affiche la Cible d'un Lien Symbolique print-symlink.c
#include <errno.h> #include <stdio.h> #include <unistd.h> int main (int argc, char* argv[]) { char target_path[256]; char* link_path = argv[1]; /* Tente de lire la cible du lien symbolique. */ int len = readlink (link_path, target_path, sizeof (target_path)); if (len == -1) { /* L'appel a chou. */ if (errno == EINVAL) /* Il ne s'agit pas d'un lien symbolique ; en informe l'utlisateur. */ fprintf (stderr, "%s n'est pas un lien symbolique\n", link_path); else /* Autre problme ; affiche un message gnrique. */ perror ("readlink"); return 1; } else { /* Place un caractre NUL la fin de la cible. */ target_path[len] = '\0'; /* L'affiche. */ printf ("%s\n", target_path); return 0; }

Par exemple, voici comment crer un lien symbolique et utiliser print-symlink pour le lire:
% ln -s /usr/bin/wc my_link % ./print-symlink my_link - 138 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) /usr/bin/wc

8-12 - sendfile : Transferts de Donnes Rapides


L'appel systme sendfile constitue un mcanisme efficace pour copier des donnes entre deux descripteurs de fichier. Les descripteurs de fichiers peuvent pointer vers un fichier sur le disque, un socket ou tout autre dispositif. Typiquement, pour copier des donnes d'un descripteur de fichier vers un autre, un programme alloue un tampon de taille fixe, y copie des donnes provenant d'un des descripteurs, l'crit sur l'autre et recommence jusqu' ce que toutes les donnes aient t crites. Ce procd n'est pas efficace que ce soit en termes de temps ou d'espace car il ncessite l'utilisation de mmoire supplmentaire pour le tampon et ajoute une copie intermdiaire pour le remplir. En utilisant sendfile, le tampon intermdiaire peut tre supprim. Appelez sendfile en lui passant le descripteur de fichier de destination, le descripteur source, un pointeur vers une variable de dplacement et le nombre d'octets transfrer. La variable de dplacement contient le dplacement partir duquel lire le fichier source (0 correspond au dbut du fichier) et est mis jour avec la position au sein du fichier l'issue du transfert. La valeur de retour contient le nombre d'octets transfrs. Incluez <sys/sendfile.h> dans votre programme s'il utilise sendfile. Le programme du Listing copy est une implmentation simple mais extrmement efficace de copie de fichier. Lorsqu'il est invoqu avec deux noms de fichiers sur la ligne de commande, il copie le contenu du premier dans le second. Il utilise fstat pour dterminer la taille, en octets, du fichier source. Copie de Fichier avec sendfile copy.c
#include #include #include #include #include #include #include <fcntl.h> <stdlib.h> <stdio.h> <sys/sendfile.h> <sys/stat.h> <sys/types.h> <unistd.h>

int main (int argc, char* argv[]) { int read_fd; int write_fd; struct stat stat_buf; off_t offset = 0; /* Ouvre le fichier source. */ read_fd = open (argv[1], O_RDONLY); /* Evalue le fichier afin d'obtenir sa taille. */ fstat (read_fd, &stat_buf); /* Ouvre le fichier de destination en criture avec les mmes permissions que le fichier source. */ write_fd = open (argv[2], O_WRONLY | O_CREAT, stat_buf.st_mode); /* Transfre les octets d'un fichier l'autre. */ sendfile (write_fd, read_fd, &offset, stat_buf.st_size); /* Ferme tout. */ close (read_fd); close (write_fd); } return 0;

L'appel sendfile peut tre utilis dans de multiples occasions pour amliorer l'efficacit des copies. Un bon exemple est un serveur Web ou tout autre service rseau, qui envoie le contenu d'un fichier un client via le rseau. Typiquement, ce genre de programme reoit une requte depuis un socket connect la machine cliente. Le programme serveur ouvre le fichier local transfrer et crit son contenu sur un socket rseau. Utiliser sendfile peut acclrer cette opration de faon significative. D'autres facteurs ont une influence sur l'efficacit du transfert, comme le paramtrage correct du socket. Cependant, ils sortent du cadre de ce livre.

- 139 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

8-13 - setitimer : Crer des Temporisateurs


L'appel systme setitimer est une gnralisation de la fonction alarm. Il programme l'envoi d'un signal au programme aprs coulement une priode de temps donne. Un programme peut crer trois types diffrents de temporisateurs: Si le code temporisateur est ITIMER_REAL, le processus reoit un signal SIGALRM aprs que le temps spcifi s'est coul; Si le code temporisateur est ITIMER_VIRTUAL, le processus reoit un signal SIGVTALRM aprs s'tre excut pendant un temps donn. Le temps pendant lequel le programme ne s'excute pas (c'est--dire lorsque le noyau ou un autre processus est en cours d'excution) n'est pas pris en compte; Si le code temporisateur est ITIMER_PROF, le processus reoit un signal SIGPROF lorsque le temps d'excution du processus ou des appels systme qu'il a invoqu atteint le temps spcifi.

Le premier argument de setitimer est un code temporisateur, indiquant le type de temporisateur mettre en place. Le second argument est un pointeur vers un objet struct itimerval spcifiant les nouveaux paramtres du temporisateur. Le troisime argument, s'il n'est pas NULL est un pointeur vers un autre objet struct itimerval qui reoit l'ancien paramtrage du temporisateur. Une variable struct itimerval est constitue de deux champs: it_value est un champ de type struct timeval qui contient le temps avant l'expiration suivante du temporisateur et l'envoi du signal. S'il vaut 0, le temporisateur est dsactiv; it_interval est un autre champ de type struct timeval contenant la valeur avec laquelle sera rinitialis aprs son expiration. S'il vaut~0, le temporisateur sera dsactiv aprs expiration. S'il est diffrent de zro, le temporisateur expirera de faon rptitive chaque coulement de l'intervalle.

Le type struct timeval est dcrit dans la Section gettimeofday, gettimeofday : Heure Systme . Le programme du Listing timer illustre l'utilisation de setitimer pour suivre le temps d'excution d'un programme. Un temporisateur est configur pour expirer toutes les 250 millisecondes et envoyer un signal SIGVTALRM. Exemple d'Utilisation d'un Temporisateur timer.c
#include #include #include #include <signal.h> <stdio.h> <string.h> <sys/time.h>

void timer_handler (int signum) { static int count = 0; printf ("le temporisateur a expir %d fois.\n", ++count); } int main () { struct sigaction sa; struct itimerval timer; /* Installe timer_handler en tant que gestionnaire pour SIGVTALRM. */ memset (&sa, 0, sizeof (sa)); sa.sa_handler = &timer_handler; sigaction (SIGVTALRM, &sa, NULL); /* Configure le temporisateur pour expirer aprs 250ms... */ timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 250000; /* ... puis rgulirement toutes les 250ms. */ timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 250000;

- 140 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Exemple d'Utilisation d'un Temporisateur timer.c

/* Dmarre un temporisateur virtuel. Il dcompte ds que le processus s'excute. */ setitimer (ITIMER_VIRTUAL, &timer, NULL); /* Perd du temps. while (1); */

8-14 - sysinfo : Rcupration de Statistiques Systme


L'appel systme sysinfo renseigne une structure avec des statistiques sur le systme. Son seul argument est un pointeur vers une variable struct sysinfo. Voici quelques uns des champs les plus intressants de cette structure: uptime ? Temps coul depuis le dmarrage du systme, en secondes; totalram ? Mmoire physique disponible au total; freeram ? Mmoire physique libre; procs ? Nombre de processus s'excutant sur le systme.

Consultez la page de manuel de sysinfo pour une description complte du type struct sysinfo. Incluez <linux/kernel.h>, <linux/sys.h> et <sys/sysinfo.h> si vous utilisez sysinfo. Le programme du Listing sysinfo affiche des statistiques sur le systme courant. Affiche des Statistiques Systme sysinfo.c
#include #include #include #include <linux/kernel.h> <linux/sys.h> <stdio.h> <sys/sysinfo.h>

int main () { /* Facteurs de conversion. */ const long minute = 60; const long hour = minute * 60; const long day = hour * 24; const double megabyte = 1024 * 1024; /* Rcupre les statistiques systme. */ struct sysinfo si; sysinfo (&si); /* Affiche un rsum des informations intressantes. */ printf ("uptime systme : %ld jours, %ld:%02ld:%02ld\n", si.uptime / day, (si.uptime % day) / hour, (si.uptime % hour) / minute, si.uptime % minute); printf ("RAM totale : %5.1f Mo\n", si.totalram / megabyte); printf ("RAM libre : %5.1f Mo\n", si.freeram / megabyte); printf ("nb processus : %d\n", si.procs); return 0; }

8-15 - uname
L'appel systme uname renseigne une structure avec diverses informations systme, comme le nom de l'ordinateur, le nom de domaine et la version du systme d'exploitation en cours d'excution. Ne passez qu'un seul argument uname: un pointeur vers un objet struct utsname. Incluez <sys/utsname.h> si vous utilisez uname. L'appel uname renseigne les champs suivants: sysname ? Nom du systme d'exploitation (par exemple, Linux); release, version ? Numros de version majeure et mineure du noyau;

- 141 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

machine ? Informations sur la plateforme systme. Pour un Linux x86, ce sera

i386 ou i686 selon le processeur; node ? Nom non qualifi de l'ordinateur; %%__%%domainname ? Nom de domaine de l'ordinateur.

Chacun de ces champs est une chane de caractres. Le petit programme du Listing printuname affiche les numros de version du noyau et des informations sur le matriel. Affiche le Numro de Version et des Infos Matrielles print-uname.c
#include <stdio.h> #include <sys/utsname.h> int main () { struct utsname u; uname (&u); printf ("%s version %s.%s sur systme %s\n", u.sysname, u.release, u.version, u.machine); return 0; }

- 142 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

9 - Code Assembleur en Ligne


Aujourd'hui, peu de programmeurs utilisent le langage assembleur. Des langages de plus haut niveau comme le C ou le C++ s'excutent sur quasiment toutes les architecture et permettent une productivit plus importante lors de l'criture et de la maintenance du code. Parfois, les programmeurs ont besoin d'utiliser des instructions assembleur dans leurs programmes, la GNU Compiler Collection permet aux programmeurs d'ajouter des instructions en langage assembleur dpendantes de l'architecture leurs programmes. Les instructions assembleur GCC en ligne ne doivent pas tre utilises de faon excessive. Les instructions en langage assembleur dpendant de l'architecture, aussi, des programmes utilisant des instructions x86 ne peuvent pas tre compils sur des PowerPC. Pour les utiliser, vous aurez besoin d'un dispositif permettant de les traduire dans le jeu d'instruction de votre architecture. Cependant, les instructions assembleur vous permettent d'accder au matriel directement et peuvent permettre de produire du code plus performant. L'instruction asm vous permet d'insrer des instructions assembleur dans des programmes C ou C++. Par exemple, l'instruction
asm ("fsin" : "=t" (answer) : "0" (angle));

est une faon spcifique l'architecture x86 de coder cette instruction C(L'expression sin (angle) est gnralement implmente par un appel la bibliothque math, mais si vous demandez une option -01 ou plus, GCC remplacera cet appel par une unique instruction assembleur fsin.):
answer = sin (angle);

Notez que contrairement aux instructions assembleur classiques, les constructions asm vous permettent de spcifier des oprandes en utilisant la syntaxe du C. Pour en savoir plus sur le jeu d'instructions x86, que nous utiliserons dans ce chapitre, consultez http:// developer.intel.com/design/pentiumii/manuals/ et http://www.x86-64.org/documentation.

9-1 - Quand Utiliser du Code Assembleur


Bien qu'il ne faille pas abuser des constructions asm, elles permettent vos programmes d'accder au matriel directement et peuvent produire des programmes qui s'excutent rapidement. Vous pouvez les utiliser lors de l'criture du code faisant partie du systme d'exploitation qui a besoin d'interagir avec le matriel. Par exemple, /usr/include/ asm/io.h contient des instructions assembleur pour accder aux ports d'entre/sortie directement. Le fichier source du noyau situ dans /usr/src/linux/arch/i386/kernel/process.s offre un autre exemple en utilisant hlt dans une boucle d'inactivit. Consultez d'autres fichiers source du noyau Linux situs dans /usr/src/linux/arch/ et /usr/src/linux/drivers/. Les instructions assembleur peuvent galement acclrer la boucle de traitement interne de certains programmes. Par exemple, si la majorit du temps d'excution d'un programme est consacr au calcul du sinus et du cosinus du mme angle, il est possible d'utiliser l'instruction x86 fsincos(Les amliorations au niveau des algorithmes ou des structures de donnes sont souvent plus efficaces dans la rduction du temps d'excution d'un programme que l'utilisation d'instructions assembleur.). Consultez par exemple /usr/include/bits/mathinline.h qui encapsule des squences assembleur au sein de macros afin d'acclrer le calcul de certaines fonctions transversales. Vous ne devriez utiliser des instructions assembleur en ligne pour acclrer l'excution qu'en dernier ressort. Les compilateurs actuels sont sophistiqus et connaissent les dtails des processeurs pour lesquels ils gnrent du code. Ainsi, ils peuvent souvent slectionner des squences de code qui paraissent contre-intuitives mais qui s'excutent en fait plus vite que d'autres. moins que vous ne compreniez le jeu d'instructions et les proprits d'ordonnancement

- 143 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

du processeur cible dans les dtails, vous feriez sans doute mieux de laisser les optimiseurs du compilateur gnrer du code assembleur votre place pour la plupart des oprations. De temps autre, une ou deux instructions assembleur peuvent remplacer plusieurs lignes de code d'un langage de plus haut niveau. Par exemple, dterminer la position du bit non nul le plus significatif d'un entier en utilisant le C ncessite une boucle ou des calculs en virgule flottante. Beaucoup d'architecture, y compris le x86 disposent d'une instruction assembleur (bsr) qui calcule cette position. Nous illustrerons son utilisation dans la Section 9.4, Exemple .

9-2 - Assembleur en Ligne Simple


Nous allons maintenant prsenter la syntaxe des instructions assembleur asm avec un exemple dcalant une valeur de 8 bits vers la droite sur architecture x86:
asm ("shrl $8, %0" : "=r" (answer) : "r" (operand) : "cc");

Le mot-clef asm est suivi par une expression entre parenthses constitue de sections spares par deux-points. La premire section contient une instruction assembleur et ses oprandes, dans cet exemple, shrl dcale droite les bits du premier oprande. Celui-ci est reprsent par %0, le second est la constante immdiate $8. La deuxime section indique les sorties. Ici, la premire sortie de l'instruction sera place dans la variable C answer, qui doit tre une lvalue. La chane "=r" contient un signe gal indiquant un oprande de sortie et un r indiquant que answer est stock dans un registre. La troisime section spcifie les entres. La variable C operand donne la valeur dcaler. La chane "r" indique qu'elle est stocke dans un registre mais ne contient pas le signe gal car il s'agit d'un oprande d'entre et non pas de sortie. La quatrime et dernire section indique que l'instruction modifie la valeur du registre de code condition cc.

9-2-1 - Convertir un asm en Instructions Assembleur


Le traitement des constructions asm par GCC est trs simple. Il produit des instructions assembleur pour traiter les oprandes de l'asm et replace la construction asm par l'instruction que vous spcifiez. Il n'analyse pas du tout l'instruction. Par exemple, GCC convertit cet extrait de code:
double foo, bar; asm ("mycool_asm %1, %0" : "=r" (bar) : "r" (foo));

en la squence d'instructions x86 suivante :


movl -8(%ebp),%edx movl -4(%ebp),%ecx mycool_asm %edx, %edx movl %edx,-16(%ebp) movl %ecx,-12(%ebp)

#APP #NO_APP

Souvenez-vous que foo et bar requirent chacun deux mots d'espace de stockage dans la pile sur une architecture x86 32 bits. Le registre ebp pointe vers les donnes sur la pile.

- 144 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Les deux premires instructions copient foo dans les registres EDX et ECX qu'utilise mycool_asm. Le compilateur a dcid d'utiliser les mmes registres pour stocker la rponse, qui est copie dans bar par les deux dernires instructions. Il choisit les registres adquats, peut mme les rutiliser, et copie les oprandes partir et vers les emplacements appropris automatiquement.

9-3 - Syntaxe Assembleur Avance


Dans les sous-sections qui suivent, nous dcrivons les rgles de syntaxe pour les constructions asm. Leurs sections sont spares par deux-points. Nous utiliserons l'instruction asm suivante qui calcule l'expression boolenne $x > y$:
asm ("fucomip %%st(1), %%st; seta %%al" : "=a" (result) : "u" (y), "t" (x) : "cc", "st");

Tout d'abord, fucomip compare ses deux oprandes x et y et stocke les valeurs indiquant le rsultat dans le registre de code condition. Puis, seta convertit ces valeurs en 0 ou 1.

9-3-1 - Instructions Assembleur


La premire section contient les instructions assembleur, entre guillemets. La construction asm exemple contient deux instructions assembleur, fucomip et seta, spares par un point-virgule. Si l'assembleur n'autorise pas les pointsvirgule, utilisez des caractres de nouvelle ligne (n) pour sparer les instructions. Le compilateur ignore le contenu de cette premire section, except qu'il supprime un niveau de signes pourcent, <nowiki>%%</nowiki> devient donc %. La signification de <nowiki>%%</nowiki>st(1) et d'autres termes similaires dpend de l'architecture. GCC se plaindra si vous spcifiez l'option -traditional ou -ansi lors de la compilation d'un programme contenant des constructions asm. Pour viter de telles erreurs, utilisez le mot cl alternatif %%__%%asm%%__%% comme dans les fichiers d'en-tte cits prcdemment.

9-3-2 - Sorties
La seconde section spcifie les oprandes de sortie des instructions en utilisant une syntaxe C. Chaque oprande est dcrit par une contrainte d'oprande sous forme de chane suivie d'une expression C entre parenthses. Pour les oprandes de sortie, qui doivent tre des lvalues, la chane de contrainte doit commencer par un signe gal. Le compilateur vrifie que l'expression C pour chaque oprande de sortie est effectivement une lvalue. Les lettres spcifiant les registres pour une architecture donne peuvent tre trouve dans le code source de GCC, dans la macro REG_CLASS_FROM_LETTER. Par exemple, le fichier de configuration gcc/con?g/i386/i386.h de GCC liste les lettres de registre pour l'architecture x86(Vous devrez avoir une certaine familiarit avec le fonctionnement de GCC pour comprendre le contenu de ce fichier.). Le Tableau registresx86 en fait le rsum. Lettres correspondant aux Registres sur l'Architecture Intel x86

- 145 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Lettre R q f t u a b c d x y A D S

Registres ventuellement utiliss par gcc Registre gnral (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP) Registre de donnes gnral (EAX, EBX, ECX, EDX) Registre virgule flottante Registre en virgule flottante suprieur Second registre en virgule flottante Registre EAX Registre EBX Registre ECX Registre EDX Registre SSE (Streaming SIMD Extension) Registres multimdias MMX Valeur de 8 octes forme partir de EAX et EDX Pointeur de destination pour les oprations sur les chanes (EDI) Pointeur source pour les oprations sur les chanes (ESI)

Lorsqu'une structure asm utilise plusieurs oprandes, elles doivent tre dcrites par une chane de contrainte et une expressions C et spares par des virgules, comme l'illustre l'exemple donn prcdemment. Vous pouvez spcifier jusqu' 10 oprandes, numrotes de %0 %9 dans les sections d'entre et de sortie. S'il n'y a pas d'oprandes de sortie ou de registre affects, laissez la section de sortie vide ou signalez la avec un commentaire du type /* no outputs */.

9-3-3 - Entres
La troisime section dcrit les entres des instructions assembleur. La chane de contrainte d'un oprande d'entre ne doit pas contenir de signe gal, ce qui indique une lvalue. Mis part cela, leur syntaxe est identique celle des oprandes de sortie. Pour indiquer qu'un registre est la fois lu et crit par la mme construction asm, utilisez l'indice de l'oprande de sortie comme chane de contrainte d'entre. Par exemple, pour indiquer qu'un registre d'entre est le mme que le premier registre de sortie, utilisez 0. Spcifier la mme expression C pour des oprandes d'entre et de sortie ne garantit pas que les deux valeurs seront places dans le mme registre. La section d'entre peut tre omise s'il n'y a pas d'oprandes d'entre et que le section de dclaration des modifications est vide.

9-3-4 - Dclaration des Modifications


Si une instruction modifie les valeur d'un ou plusieurs registres par effet de bord, spcifiez ces registres dans la quatrime section de la structure asm. Par exemple, l'instruction fucomip modifie le registre de code condition, qui est dsign par cc. Les registres modifis sont dcrits dans des chanes individuelles spares par des virgules. Si l'instruction est susceptible de modifier un emplacement mmoire arbitraire, spcifiez memory. En utilisant les informations sur la modification des registres, le compilateur dtermine les valeurs qui doivent tre restaures aprs l'excution du bloc asm. Si vous ne renseignez pas ces informations correctement, GCC pourrait supposer que certains registres contiennent des valeurs qui ont en fait t crases, ce qui pourrait affecter le fonctionnement de votre programme.

- 146 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

9-3-5 - Exemple
L'architecture x86 dispose d'instructions qui dterminent les positions du bit non nul le plus significatif ou le moins significatif dans un mot. Le processeur peut excuter ces instructions de faon relativement efficace. Par contre, l'implmentation de la mme opration en C ncessite une boucle et un dcalage binaire. Par exemple, l'instruction assembleur bsrl calcule la position du bit le plus significatif de son premier oprande et place cette position ( partir de 0 qui reprsente le bit le moins significatif) dans le second. Pour placer la position du bit non nul le plus significatif de number dans position, nous pourrions utiliser cette structure asm:
asm ("bsrl %1, %0" : "=r" (position) : "r" (number));

Une faon d'implmenter la mme opration en C, est d'utiliser une boucle de ce genre :
long i; for (i = (number >> 1), position = 0; i != 0; ++position) i >>= 1;

Pour comparer les vitesses de ces deux versions, nous allons les placer dans une boucle qui calcule les positions pour de grands nombres. Le Listing bitposloop utilise l'implmentation en C. Le programme boucle sur des entiers, en partant de 1 jusqu' la valeur passe sur la ligne de commande. Pour chaque valeur de number, il calcule la position du bit non nul le plus significatif. Le Listing bitposasm effectue les mmes oprations en utilisant une construction assembleur en ligne. Notez que dans les deux versions, nous plaons la position calcule une variable volatile result. Cela permet d'viter que l'optimiseur du compilateur ne supprime le calcul; si le rsultat n'est pas utilis ou stock en mmoire, l'optimiseur limine le calcul en le considrant comme du code mort. Recherche d'un Bit en Utilisant une Boucle bit-pos-loop.c
#include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { long max = atoi (argv[1]); long number; long i; unsigned position; volatile unsigned result; /* Rpte l'opration pour un nombre important de valeurs. */ for (number = 1; number <= max; ++number) { /* Dcale le nombre vers la droite jusqu' ce qu'il vaille zro. Mmorise le nombre de dcalage qu'il a fallu faire. */ for (i = (number >> 1), position = 0; i != 0; ++position) i >>= 1; /* La position du nombre non nul le plus significatif est le nombre de dcalages qu'il a fallu aprs le premier. */ result = position; } } return 0;

Recherche d'un Bit en Utilisant bsrl bit-pos-asm.c


#include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { long max = atoi (argv[1]); long number; - 147 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Recherche d'un Bit en Utilisant bsrl bit-pos-asm.c

unsigned position; volatile unsigned result; /* Repeat the operation for a large number of values. */ for (number = 1; number <= max; ++number) { /* Compute the position of the most signi?cant set bit using the bsrl assembly instruction. */ asm ("bsrl %1, %0" : "=r" (position) : "r" (number)); result = position; } return 0;

Compilons les deux versions avec les optimisations actives:


% cc -O2 -o bit-pos-loop bit-pos-loop.c % cc -O2 -o bit-pos-asm bit-pos-asm.c

prsent, lanons-les en utilisant la commande time pour mesurer le temps d'excution. Il est ncessaire de passer une valeur importante comme argument afin de s'assurer que chaque version mette un minimum de temps s'excuter.
% time ./bit-pos-loop 250000000 real 0m27.042s user 0m24.583s sys 0m0.135s % time ./bit-pos-asm 250000000 real 0m1.472s user 0m1.442s sys 0m0.013s

Voyez comme la version utilisant l'assembleur s'excute beaucoup plus vite (vos propres rsultats peuvent varier).

9-4 - Problmes d'Optimisation


L'optimiseur de GCC tente de rordonner et de rcrire le code du programme pour minimiser le temps d'excution mme en prsence d'expressions asm. Si l'optimiseur dtermine que les valeurs de sortie ne sont pas utilises, l'instruction sera supprime moins que le mot cl volatile ne figure entre asm et ses arguments (par ailleurs, GCC ne dplacera pas une structure asm sans oprande de sortie en dehors d'une boucle). Toute structure asm peut tre dplace de faon difficile prvoir, mme d'un saut l'autre. La seule faon de garantir l'ordre d'un bloc assembleur est d'inclure toutes les instructions dans la mme structure asm. L'utilisation de constructions asm peut limiter l'efficacit de l'optimiseur car le compilateur ne connat pas leur smantique. GCC est forc de faire des suppositions qui peuvent interdire certaines optimisations. Caveat emptor!

9-5 - Problmes de Maintenance et de Portabilit


Si vous dcidez d'utiliser des constructions asm non-portables et dpendantes de l'architecture, les encapsuler dans des macros peut aider la maintenance et amliorer la portabilit. Placer toutes ces macros dans un fichier et les documenter facilitera le portage de l'application vers une autre architecture, quelque chose qui arrive souvent mme pour les programmes la va vite . De cette faon, le programmeur n'aura besoin de rcrire qu'un seul fichier pour adapter le logiciel une autre architecture. Par exemple, la plupart des instructions asm du code source Linux sont regroupes dans les fichiers d'en-tte situs sous /usr/src/linux/include/asm/ et /usr/src/linux/include/asm-i386/ et les fichiers source situs sous /usr/src/linux/ arch/i386/ et /usr/src/linux/drivers/.
- 148 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

10 - Scurit
Une grande partie de la puissance d'un systme GNU/Linux vient de sa capacit grer plusieurs utilisateurs et de son bon support pour le rseau. Beaucoup de gens peuvent utiliser le systme en mme temps et se connecter au systme distance. Malheureusement, cette puissance prsente des risques, en particulier pour les systme connects Internet. Dans certaines circonstances, un hacker distant peut se connecter au systme et lire, modifier ou supprimer des fichiers stocks sur la machine. Ou bien, un utilisateur pourrait lire, modifier ou supprimer les fichier d'un autre sans y tre autoris. Lorsque ce type d'vnement se produit, la scurit du systme est dite compromise. Le noyau Linux fournit divers mcanismes afin de s'assurer que de telles situations ne se prsentent pas. Mais les applications classiques doivent elles aussi tre attentives viter les failles de scurit. Par exemple, imaginons que vous dveloppiez un logiciel de comptabilit. Bien que vous puissiez vouloir permettre tous les utilisateurs de remplir des notes de frais, il n'est certainement pas souhaitables qu'ils puissent tous les valider. De mme, les utilisateurs ne doivent pouvoir consulter que leur propre bulletin de paie et les managers ne connatre que les salaires des employs de leur dpartement. Pour forcer ce type de contrle, vous devez tre trs attentif. Il est tonnamment facile de faire une erreur permettant des utilisateurs de faire quelque chose qu'ils ne devraient pas pouvoir faire. La meilleur approche est de demander de l'aide des experts en scurit. Toutefois, tout dveloppeur d'application doit matriser quelques bases.

10-1 - Utilisateurs et Groupes


Chaque utilisateur sous Linux est identifi par un numro unique appel user ID, ou UID. Bien sr, lorsque vous vous connectez, vous utilisez un nom d'utilisateur plutt que cet UID. Le systme effectue la conversion entre ce nom d'utilisateur et l'UID, et partir de ce moment, seul l'user ID est pris en compte. Vous pouvez en fait faire correspondre plus d'un nom d'utilisateur au mme UID. En ce qui concerne le systme, seuls les UID comptent. Il n'y a aucune faon de donner plus de droits un utilisateur qu' un autre s'ils ont tous deux le mme UID. Vous pouvez contrler l'accs un fichier ou toute autre ressource en l'associant un UID particulier. Dans ce cas, seul l'utilisateur disposant de cet UID peut accder la ressource. Par exemple, vous pouvez crer un fichier que vous serez le seul pouvoir lire ou un rpertoire o vous seul pourrez crer de nouveau fichier. Cela suffit dans la plupart des cas simples. Parfois, cependant, vous avez besoin de partager une ressource entre plusieurs utilisateurs. Par exemple, si vous tes un manager, vous pourriez vouloir crer un fichier que tous les managers pourraient lire mais pas les employs ordinaires. Linux ne vous permet pas d'associer plusieurs UID au mme fichier, vous ne pouvez donc pas crer une liste de tous les gens auxquels vous souhaitez donner accs au fichier et les lier au fichier. Par contre, vous pouvez crer un groupe. chaque groupe est associ un numro unique, appel group ID ou GID. Chaque groupe contient un ou plusieurs user ID. Un mme UID peut faire partie de plusieurs groupes mais un groupe ne peut pas contenir d'autres groupes; ils ne peuvent contenir que des utilisateurs. Comme les utilisateurs, les groupes ont des noms. Tout comme pour les noms d'utilisateurs, le nom d'un groupe n'a pas d'importance, le systme utilise toujours le GID en interne. Pour continuer avec notre exemple, vous pourriez crer un groupe managers et y placer les UID de tous les managers. Vous pourriez alors crer un fichier qui peut tre lu par n'importe qui dans ce groupe mais pas par les gens qui y sont extrieurs. En gnral, vous ne pouvez associer qu'un seul groupe une ressource. Il n'y a aucun moyen de spcifier que les utilisateurs peuvent accder un fichier s'ils font partie du groupe~7 ou du groupe~42, par exemple. Si vous tes curieux de connatre votre UID et les groupes auxquels vous appartenez, vous pouvez utiliser la commande id. Voici un exemple de la sortie de cette commande:

- 149 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

% id uid=501(mitchell) gid=501(mitchell) groups=501(mitchell),503(csl)

la premire partie indique que l'UID de l'utilisateur ayant invoqu la commande est~501. La commande dtermine le nom d'utilisateur correspondant et l'affiche entre parenthses. On voit ici que l'utilisateur 501 est dans deux groupes: le groupe~501 (appel mitchell) et le groupe~503 (appel csl). Vous vous demandez certainement pourquoi le groupe 501 apparat deux fois: une premire fois dans le champ gid et une seconde dans le champ groups. Nous y reviendrons plus tard.

10-1-1 - Le superutilisateur
Un des comptes utilisateur est trs spcial(Le fait qu'il n'y ait qu'un seul utilisateur spcial est l'origine du nom d'UNIX donn par AT&T son systme d'exploitation. Un systme d'exploitation plus ancien qui disposait de plusieurs utilisateurs spciaux a t baptis MULTICS. GNU/Linux, bien sr, est compatible avec UNIX. ). Cet utilisateur a l'UID~0 est a gnralement le nom d'utilisateur root. On y fait parfois rfrence en parlant du compte superutilisateur. L'utilisateur root peut littralement tout faire: lire ou supprimer n'importe quel fichier, ajouter de nouveaux utilisateurs, dsactiver l'accs rseau, etc. Beaucoup d'oprations spciales ne peuvent tre ralises que par des processus s'excutant avec les privilges root ? c'est--dire s'excutant sous le compte utilisateur root. Le problme de cette conception est qu'un nombre important de programmes doivent tre excuts par root car beaucoup de programmes ont besoin d'accder ces oprations spciales. Si l'un de ses programmes ne se comporte pas correctement, les consquences peuvent tre dramatique. Il n'y a aucun moyen de contenir un programme lorsqu'il est excut par root; il peut faire n'importe quoi. Les programmes lancs par root doivent tre crit avec beaucoup de prcautions.

10-2 - User et Group ID de Processus


Jusqu' maintenant, nous n'avons parl que des commandes excutes par un utilisateur en particulier. Cela ne colle pas exactement la ralit car l'ordinateur ne sait jamais rellement quel utilisateur l'excute. Si ve dcouvre le nom d'utilisateur et le mot de passe d'Alice, ve peut se connecter en tant qu'Alice et le systme laisserait faire ve tout ce qu'Alice peut faire. Il ne connat que l'UID en cours et non pas l'utilisateur qui saisit les commandes. Si on ne peut pas faire confiance Alice pour garder secret son mot de passe, alors rien de ce que vous pourrez faire en tant que programmeur d'application ne pourra empcher ve d'accder aux fichiers d'Alice. La responsabilit de la scurit du systme est partage entre le dveloppeur, les utilisateurs du systme et les administrateurs du systme. chaque processus est associ un UID et un GID. Lorsque vous invoquez une commande, elle lance un processus dont l'UID et le GID sont les vtres. Lorsque nous disons qu'un utilisateur effectue une opration quelconque, nous voulons dire en ralit qu'un processus avec l'UID correspondant effectue cette opration. Lorsque le processus excute un appel systme, le noyau dcide s'il y est autoris. Pour cela, il examine les permissions associes aux ressources auxquelles le processus tente d'accder et vrifie l'UID et le GID associs au processus tentant d'excuter l'appel. Dsormais, vous savez ce que signifie le champ gid de la sortie de la commande id. Il indique le GID du processus courant. Mme si l'utilisateur~501 fait partie de plusieurs groupes, le processus courant ne peut avoir qu'un seul GID. Dans l'exemple prsent prcdemment, le GID en cours est~501. Si vous devez manipuler des UID ou des GID dans votre programme (et vous aurez le faire si vous crivez des programmes concernant la scurit), vous devez utiliser les types uid_t et gid_t dfinis dans l'en-tte <sys/types.h> ? mme si les UID et GID ne sont en fait que des entiers, vitez de faire des suppositions sur le nombre de bits utilis dans ces types ou d'effectuer des oprations arithmtiques en les utilisant. Traitez les comme un moyen obscur de manipuler les identifiants de groupe et d'utilisateur.

- 150 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour rcuprer l'UID et le GID du processus courant, vous pouvez utiliser les fonctions geteuid et getegid dfinies dans <unistd.h>. Ces fonctions ne prennent aucun paramtre et n'chouent jamais, il n'y a pas d'erreur contrler. Le Listing simpleid prsente un simple programme qui fournit un sous-ensemble des fonctionnalits de la commande id: Affiche les Identifiants d'Utilisateur et de Groupe simpleid.c
#include <sys/types.h> #include <unistd.h> #include <stdio.h> int main() { uid_t uid = geteuid (); gid_t gid = getegid (); printf ("uid=%d gid=%d\n", (int) uid, (int) gid); return 0; }

Lorsque ce programme est excut (par le mme utilisateur que celui qui a lanc le programme id dans l'exemple prcdemment), la sortie est la suivante :
% ./simpleid uid=501 gid=501

10-3 - Permissions du Systme de Fichiers


Un bon moyen de voir les utilisateurs et les groupes en actions est d'tudier les permissions du systme de fichiers. En examinant la faon dont le systme associe les permissions avec chaque fichier et en observant comment le noyau vrifie qui est autoris accder quels fichiers, le concept d'identifiant utilisateur et de groupe devrait devenir clair. Chaque fichier a exactement un utilisateur propritaire et un groupe propritaire. Lorsque vous crez un nouveau fichier, il est dtenu par l'utilisateur et le groupe du processus crateur(En fait, il existe de rares exceptions faisant intervenir les sticky bits, dcrits plus loin dans la Section 10.3.2, Sticky Bits .). Les oprations de base sur les fichiers, en ce qui concerne Linux, sont la lecture, l'criture et l'excution (Notez que la cration et la suppression ne sont pas considres comme des oprations sur le fichier, elles sont considres comme des oprations sur le rpertoire contenant le fichier. Nous en parlerons plus loin). Si vous ne pouvez pas lire un fichier, Linux ne vous laissera pas en examiner le contenu. Si vous ne pouvez pas y crire, vous ne pouvez pas modifier son contenu. Si vous ne disposez pas des droits d'excution sur un fichier contenant le code d'un programme, vous ne pouvez pas excuter ce programme. Linux vous permet d'indiquer lesquelles de ces trois actions ? lire, crire et excuter ? peuvent tre ralises par l'utilisateur propritaire, le groupe propritaire et toute autre personne. Par exemple, vous pouvez dire que l'utilisateur propritaire aura tous les droits, que les membres du groupe propritaire pourront lire et excuter le fichier (mais pas y crire) et que personne d'autre n'y a accs. Vous pouvez consulter ces bits de permission de faon interactive avec la commande ls en utilisant les options -l ou -o et par programmation via l'appel systme stat. Vous pouvez dfinir de faon interactive les bits de permission avec le programme chmod(On fait parfois rfrence aux bits de permission en utilisant le terme mode. Le nom de la commande chmod est un diminutif pour change mode .) et par programmation avec l'appel systme du mme nom. Pour examiner les permissions d'un fichier appel hello, utilisez ls -l hello. Voici un exemple de sortie:
% ls -l hello -rwxr-x--1 samuel csl 11734 Jan 22 16:29 hello

Les indications samuel et csl signifient que l'utilisateur propritaire est samuel et que le groupe propritaire est csl.
- 151 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La chane de caractres a dbut de la ligne indique les permissions associes au fichier. Le premier tiret indique que le fichier est un fichier classique. Il serait remplac par d pour un rpertoire ou d'autres lettres dans le cas de fichiers spciaux comme les priphriques (reportez-vous au Chapitre peripheriques, Priphriques ) ou les canaux nomms (voyez le Chapitre IPC, Communication Interprocessus , Section tubes, Tubes ). Les trois caractres suivants reprsentent les permissions de l'utilisateur propritaire: ils indiquent que samuel peut lire, crire et excuter le fichier. Les trois caractres d'aprs donnent les permissions des membres du groupe csl; ses membres ne peuvent que lire et excuter le fichier. Enfin, les trois derniers caractres indiquent les permissions de toute autre personne; ici, tout utilisateur n'tant pas samuel et ne faisant pas partie du groupe csl ne peut rien faire avec le fichier hello. Voyons comment cela fonctionne. Tout d'abord, essayons d'accder au fichier en tant qu'utilisateur nobody, qui ne fait pas partie du groupe csl:
% id uid=99(nobody) gid=99(nobody) groups=99(nobody) % cat hello cat: hello: Permission denied % echo hi > hello sh: ./hello: Permission denied % ./hello sh: ./hello: Permission denied

Nous n'avons pas le droit de lire le fichier, c'est pourquoi cat choue; nous ne pouvons pas crire dans le fichier, c'est pourquoi echo choue; et nous n'avons pas le droit d'excuterle fichier, c'est pourquoi ./hello choue. Les choses s'arrangent si nous accdons au fichier en tant que mitchell, qui n'est pas membre du groupe csl:
% id uid=501(mitchell) gid=501(mitchell) groups=501(mitchell),503(csl) % cat hello #!/bin/bash echo "Hello, world." % ./hello Hello, world. % echo hi > hello bash: ./hello: Permission denied

Nous pouvons afficher le contenu du fichier et nous pouvons l'excuter (il s'agit d'un simple script shell) mais nous ne pouvons toujours pas y crire. Si nous sommes identifis en tant que propritaire (samuel), nous pouvons mme craser le fichier:
% id uid=502(samuel) gid=502(samuel) groups=502(samuel),503(csl) % echo hi > hello % cat hello hi

Vous pouvez modifier les permissions associes avec un fichier si vous tes son propritaire (ou le superutilisateur). Par exemple, si vous voulez dsormais permettre tout le monde d'excuter le fichier, vous pouvez faire ce qui suit:
% chmod o+x hello % ls -l hello -rwxr-x--x 1 samuel csl 3 Jan 22 16:38 hello

Notez qu'il y a maintenant un x la fin de la premire chane de caractres. L'option o+x signifie que vous voulez donner la permission d'excution tous les autres gens (ni le propritaire, ni les membres du groupe propritaire).

- 152 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour rvoquer les permissions en criture du groupe, vous utiliseriez g-w. Consultez la page de manuel de la section~1 sur chmod pour plus de dtails sur sa syntaxe:
% man 1 chmod

Dans un programme, vous pouvez utiliser l'appel systme stat pour dterminer les permissions associes un fichier. Cette fonction prend deux paramtres: le nom du fichier sur lequel vous voulez des renseignements et l'adresse d'une structure de donnes renseigne avec des informations sur le fichier. Consultez l'Appendice B, E/S de Bas Niveau , Section B.2, stat , pour une prsentation des autres informations que vous pouvez obtenir via stat. Le Listing statperm montre un exemple d'utilisation de stat pour dterminer les permissions associes un fichier. Dterminer si le Propritaire a les Droits d'criture stat-perm.c
#include <stdio.h> #include <sys/stat.h> int main (int argc, char* argv[]) { const char* const filename = argv[1]; struct stat buf; /* Rcupre les informations sur le fichier. */ stat (filename, &buf); /* Affiche un message si les permissions sont dfinies de faons ce que le propritaire puisse y crire. */ if (buf.st_mode & S_IWUSR) printf ("Le propritaire peut crire dans '%s'.\n", filename); return 0;

Si vous excutez ce programme sur notre script hello, il indique:


% ./stat-perm hello Le propritaire peut crire dans 'hello'.

La constante S_IWUSR correspond la permission en criture pour le propritaire. Il y a une constante pour chaque bit. Par exemple, S_IRGRP correspond la permission en lecture pour le groupe propritaire et S_IXOTH la permission en excution pour les utilisateurs qui ne sont ni propritaires, ni membre du groupe. Si vous stockez les permissions dans une variable, utilisez le typedef mode_t. Comme la plupart des appels systme, stat renverra~-1 et positionnera errno s'il ne peut pas obtenir les informations sur le fichier. Vous pouvez utiliser la fonction chmod pour modifier les bits de permission d'un fichier existant. Appelez chmod avec le nom du fichier dont vous voulez changer les permission et les bits activer sous forme d'un OU binaire entre les diffrentes constantes cites prcdemment. Par exemple, l'extrait suivant rend hello lisible et excutable par le propritaire mais dsactive toutes les autres permissions associes hello:
chmod ("hello", S_IRUSR | S_IXUSR);

Le mme systme de bits de permission s'applique aux rpertoires mais ces bits ont des significations diffrentes. Si un utilisateur est autoris lire le rpertoire, alors il peut consulter la liste des fichiers prsents dans ce rpertoire. Avec les droits en criture, il est possible d'ajouter ou de supprimer des fichiers. Notez qu'un utilisateur peut supprimer des fichiers d'un rpertoire s'il a les droits en criture sur celui-ci, mme s'il n'a pas la permission de modifier le fichier qu'il supprime. Si un utilisateur a les droits d'excution sur un rpertoire, il a le droit d'y entrer et d'accder aux fichiers qu'il contient. Sans droit d'excution sur un rpertoire, un utilisateur ne peut pas accder aux fichiers qu'il contient, indpendamment des droits qu'il dtient sur les fichiers eux-mmes.

- 153 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pour conclure, observons comment le noyau dcide d'autoriser ou non un processus accder un fichier donn. Tout d'abord, il dtermine si l'utilisateur demandant l'accs est le propritaire du fichier, un membre du groupe propritaire ou quelqu'un d'autre. La catgorie dans laquelle tombe l'utilisateur est utilise pour dterminer quel ensemble de bits lecture/criture/excution est vrifi. Puis, le noyau contrle l'opration effectue par rapport aux permissions accordes l'utilisateur(Le noyau peut galement refuser l'accs un fichier si un rpertoire dans le chemin du fichier est inaccessible. Par exemple, si un processus n'a pas le droit d'accder au rpertoire /tmp/private, il ne pourra pas lire /tmp/private/data, mme si les permissions de ce dernier sont dfinies de faon l'y autoriser.). Il y a une exception qu'il convient de signaler: les processus s'excutant en tant que root (avec l'user ID~0) sont toujours autoriss accder n'importe quel fichier, quelques soient les permissions qui y sont associes.

10-3-1 - Faille de scurit: les Programmes non Excutables


Voici un premier exemple de situation ou la scurit se complique. Vous pourriez penser que si vous interdisez l'excution d'un programme, personne ne pourra le lancer. Aprs tout, c'est ce que sous-entend l'interdiction d'excution. Mais un utilisateur ingnieux pourrait copier le programme, modifier les permissions pour rendre la copie excutable et la lancer! Si vous interdisez l'excution de programmes sans interdire aux utilisateurs de les copier, vous crez une faille de scurit ? un moyen pour les utilisateurs de faire des choses que vous n'aviez pas prvu.

10-3-2 - Sticky Bits


En plus des permissions en lecture, criture et excution, il existe un bit magique appel sticky bit(Ce terme est anachronique; il remonte un temps o l'activation du sticky bit (bit collant ) permettait de conserver un programme en mmoire mme lorsqu'il avait termin de s'excuter. Les pages alloues pour le programme taient colles en mmoire. Les sticky bits sont galement parfois appels bits de rappel .). Ce bit ne concerne que les rpertoires. Un rpertoire pour lequel le sticky bit est actif ne vous autorise dtruire un fichier que si vous en tes le propritaire. Comme nous l'avons dit prcdemment, vous pouvez normalement supprimer un fichier si vous avez un accs en criture sur le rpertoire qui le contient, mme si vous n'en tes pas le propritaire. Lorsque le sticky bit est activ, vous devez toujours avoir les droits en criture sur le rpertoire, mais vous devez en plus tre le propritaire du fichier que vous voulez supprimer. Seuls certains rpertoires sur un systme GNU/Linux ont le sticky bit positionn. Par exemple, le rpertoire /tmp, dans lequel tout utilisateur peut placer des fichiers temporaires, en fait partie. Ce rpertoire est spcifiquement conu pour pouvoir tre utilis par tous les utilisateur, tout le monde doit donc y crire. Mais il n'est pas souhaitable qu'un utilisateur puisse supprimer les fichiers d'un autre, le sticky bit est donc activ pour ce rpertoire. De cette faon, seul le propritaire (ou root, bien sr) peut supprimer le fichier. Vous pouvez voir que le sticky bit est actif grce au t la fin de la liste des permissions si vous lancez ls sur /tmp:
% ls -ld /tmp drwxrwxrwt 12 root root 2048 Jan 24 17:51 /tmp

La constante correspondante utiliser avec stat et chmod est S_ISVTX. Si votre programme cre des rpertoires qui se comportent comme /tmp, c'est--dire que beaucoup de personne doivent y crire sans pouvoir supprimer les fichier des autres, vous devez activer le sticky bit sur ce rpertoire. Vous pouvez le faire en utilisant la commande chmod de cette faon:
% chmod o+t rpertoire

Pour le faire par programmation, appelez chmod avec le drapeau de mode S_ISVTX. Par exemple, pour activer le sticky bit du rpertoire spcifi par dir_path et donner un accs complet tous les utilisateur, effectuez l'appel suivant:
- 154 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

chmod (dir_path, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);

10-4 - ID Rels et Effectifs


Jusqu' maintenant, nous avons parl de l'UID et du GID associs avec un processus comme s'il n'y en avait qu'un seul de chaque. Mais, en ralit, ce n'est pas aussi simple. Chaque processus a en ralit deux user ID: l'user ID effectif et l'user ID rel (bien sr, il y a galement un group ID effectif et un group ID rel; presque tout ce qui est vrai pour les user ID l'est galement pour les group ID). La plupart du temps, le noyau ne se proccupe que du user ID effectif. Par exemple, si un processus tente d'ouvrir un fichier, le noyau vrifie l'user ID effectif pour dcider s'il laisse le processus accder au fichier. Les fonctions geteuid et getegid dcrites prcdemment renvoient l'user ID et le group ID effectifs. Les fonctions analogues getuid et getgid renvoient l'user ID et le group ID rels. Si le noyau ne s'occupe que de l'user ID effectif, il ne semble pas trs utile de faire la distinction entre user ID rel et effectif. Cependant, il y a un cas trs important dans lequel le user ID rel est pris en compte. Si vous voulez changer l'user ID effectif d'un processus en cours d'excution, le noyau vrifie l'user ID rel et l'user ID effectif. Avant d'observer comment vous pouvez changer l'user ID effectif d'un processus, examinons pourquoi vous pourriez vouloir le faire en reprenant l'exemple de notre application de comptabilit. Supposons qu'il y ait un processus serveur qui ait besoin de consulter tout fichier prsent sur le systme, peu importe qui l'ait cr. Un tel processus doit s'excuter en tant que root car lui seul est sr de pouvoir accder n'importe que fichier. Mais supposons maintenant qu'une requte arrive de la part d'un utilisateur particulier (disons mitchell) pour accder des fichiers quelconques. Le processus serveur pourrait examiner avec attention les permissions associes avec les fichiers concerns et essayer de dterminer si mitchell devrait tre autoris accder ces fichiers. Mais cela signifierait dupliquer tous les traitements que le noyau ferait de toutes faons. Rimplmenter cette logique serait complexe, sujet des erreurs et pnible. Une meilleure approche est simplement de modifier temporairement l'user ID effectif du processus pour ne plus qu'il soit celui de root mais celui de mitchell puis d'essayer d'effectuer les oprations demandes. Si mitchell n'a pas le droit d'accder aux donnes, le noyau interdira l'accs au processus et renverra des informations adquates sur l'erreur. Une fois que les oprations demandes par mitchell sont termines, le processus peut rcuprer son user ID effectif original qui est root. Les programmes qui authentifient les utilisateurs lorsqu'ils essaient de se connecter tirent eux aussi avantage de cette possibilit de modifier les user ID. Ces programmes s'excutent en tant que root ? lorsque l'utilisateur saisit un login et un mot de passe, le programme de connexion vrifie le nom d'utilisateur et le mot de passe dans la base de donnes du systme. Puis il change la fois ses user ID rel et effectif afin de devenir cet utilisateur. Enfin, le programme de connexion appelle exec pour lancer le shell de l'utilisateur, ce qui permet l'utilisateur d'avoir un environnement dans lequel les user ID rel et effectif sont les siens. La fonction utilise pour modifier les user ID d'une processus est setreuid (il y a, bien sr, une fonction setregid similaire). Cette fonction prend deux arguments. Le premier argument est l'user ID rel dsir; le second est l'user ID effectif demand. Par exemple, voici comment vous changeriez les user ID rel et effectif:
setreuid (geteuid(), getuid ());

Bien sr, le noyau ne laisse pas n'importe quel processus changer son user ID. Si un processus pouvait modifier son user ID effectif volont, alors, toute personne pourrait facilement prendre l'identit de n'importe quel autre utilisateur simplement en changeant l'user ID effectif de l'un de ses processus. Le systme laissera un processus disposant d'un user ID effectif de 0 modifier son user ID sa guise (encore une fois, remarquez la puissance dont dispose un processus s'excutant en tant que root! Un processus dont l'user ID effectif est 0 peut faire tout ce qu'il lui plat). Tout autre processus, par contre, ne peut faire que l'une des actions suivantes:
- 155 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

dfinir son user ID effectif pour qu'il soit le mme que son user ID rel ; dfinir son user ID rel pour qu'il soit le mme que son user ID effectif ; changer les deux identifiants.

La premire possibilit serait utilise par notre systme de comptabilit lorsqu'il a termin d'accder au fichiers en tant que mitchell et veut redevenir root. La seconde par un programme de connexion une fois qu'il a dfini l'user ID effectif pour qu'il soit celui de l'utilisateur qui s'est connect. Dfinir l'user ID rel permet de s'assurer que l'utilisateur ne pourra jamais redevenir root. L'change des deux identifiants est avant tout une fonctionnalit historique, les programmes modernes l'utilisent rarement. Vous pouvez passer~-1 la place de l'un des deux arguments de setreuid si vous ne voulez pas modifier l'user ID correspondant. Il existe galement une fonction raccourci appele seteuid. Cette fonction dfinit l'user ID effectif mais ne modifie pas l'user ID rel. Les deux instructions suivantes font toutes deux la mme chose:
seteuid (id); setreuid (-1, id);

10-4-1 - Programmes Setuid


En utilisant les techniques prsentes ci-dessus, vous tes en mesure de crer des processus root qui s'excutent sous une autre identit de faon temporaire puis redeviennent root. Vous pouvez galement faire abandonner tous ses privilges un processus root en redfinissant ses user ID rel et effectif. Voici une nigme: un processus non root peut il devenir root? Cela semble impossible, en utilisant les techniques prcdentes, mais voici une preuve que a l'est:
% whoami mitchell % su Password: ... % whoami root

La commande whoami est similaire id, except qu'elle n'affiche que l'user ID effectif, pas les autres informations. La commande su vous permet de devenir le superutilisateur si vous connaissez le mot de passe root. Comment fonctionne su? Comme nous savons que le shell initial s'excute avec des user ID rel et effectif qui sont ceux de mitchell, setreuid ne nous permettra pas de les changer. L'astuce est que le programme su est un programme setuid. Cela signifie que lorsqu'il s'excute, son user ID effectif sera celui du propritaire du fichier et non l'user ID du processus qui effectue l'appel exec (l'user ID rel est cependant toujours dtermin par ce dernier). Pour crer un programme setuid, utilisez la commande chmod +s ou l'option S_ISUID si vous appelez chmod par programmation(Bien sr, il existe la notion parallle de programme setgid. Lorsqu'un tel programme s'excute, son group ID effectif est celui du groupe propritaire du fichier. La plupart des programmes setuid sont galement des programmes setgid.). tudions le programme du Listing setuidtest. Programme de Dmonstration de setuid setuid-test.c
#include <stdio.h> #include <unistd.h> int main () { printf ("uid=%d euid=%d\n", (int) getuid (), (int) geteuid ());

- 156 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Programme de Dmonstration de setuid setuid-test.c


} return 0;

Supposons maintenant que ce programme est en mode setuid et que root en est le propritaire. Dans ce cas, la sortie de ls -l ressemblerait cela:
-rwsrws--x 1 root root 11931 Jan 24 18:25 setuid-test

Le bit s indique que le fichier n'est pas seulement excutable (comme l'indiquerait un x) mais qu'il est galement setuid et setgid. Lorsque nous utilisons ce programme, il affiche quelque chose de ce genre:
% whoami mitchell % ./setuid-test uid=501 euid=0

Notez que l'user ID effectif est 0 lorsque le programme s'excute. Vous pouvez utiliser la commande chmod avec les arguments u+s ou g+s pour activer les bits setuid et setgid sur un fichier excutable, respectivement ? par exemple:
% ls -l program -rwxr-xr-x 1 samuel csl 0 Jan 30 23:38 program % chmod g+s program % ls -l program -rwxr-sr-x 1 samuel csl 0 Jan 30 23:38 program % chmod u+s program % ls -l program -rwsr-sr-x 1 samuel csl 0 Jan 30 23:38 program

Vous pouvez galement utiliser l'appel chmod avec les indicateurs de mode S_ISUID et S_ISGID. su est capable de modifier l'user ID effectif par le biais de ce mcanisme. Il s'excute initialement avec un user ID effectif 0. Puis il vous demande un mot de passe. Si le mot de passe concorde avec celui de root, il positionne son user ID rel de sorte qu'il soit celui de root puis lance un nouveau shell. Dans le cas contraire, il se termine, vous renvoyant votre shell d'utilisateur non privilgi. Observons les permissions du programme su:
% ls -l /bin/su -rwsr-xr-x 1 root root 14188 Mar 7 2000 /bin/su

Notez que root est le propritaire et que le bit setuid est actif. Remarquez que su ne change pas rellement l'user ID du shell partir duquel il a t lanc. Au lieu de cela, il lance un nouveau shell avec le nouvel user ID. Le shell original est bloqu jusqu' ce que le nouveau se termine.

10-5 - Authentifier les utilisateurs


Souvent, lorsque vous crez un programme setuid, vous souhaitez n'en autoriser l'accs qu' certains utilisateurs. Par exemple, le programme su ne vous laisse devenir root que si vous disposez du mot de passe correspondant. Le programme vous oblige prouver que vous pouvez devenir root avant de vous permettre de faire quoi que ce soit. Ce mcanisme est appel authentification ? le programme su vrifie que vous tes celui que vous prtendez tre.
- 157 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si vous administrez un systme trs scuris, l'authentification des utilisateurs par le biais d'un simple mot de passe ne vous satisfera probablement pas. Les utilisateurs ont tendence noter leur mot de passe et les personnes mal intentionnes les dcouvrir. Les utilisateurs choisissent gnralement des mots de passe comme leur date de naissance, le nom de leur animal de compagnie, etc(On a dcouvert que les administrateurs systme avaient tendance choisir le mot dieu pour mot de passe plutt que n'importe quel autre (pensez-en ce que vous voulez). Aussi, si vous avez besoin d'un accs root sur une machine et que l'administrateur n'est pas l, une inspiration divine pourra peut tre vous aider.). Les mot de passe ne sont finalement pas une bonne garantie de scurit. Par exemple, beaucoup d'organisations utilisent dsormais un systme de mots de passe usage unique gnrs par des cartes d'identit lectronique que les utilisateurs gardent sur eux. Le mme mot de passe ne peut tre utilis deux fois et vous ne pouvez obtenir de mot de passe valide partir de la carte qu'en entrant un code d'identification. L'attaquant doit donc obtenir la carte et le code pour accder au systme. Dans des complexes extrmement scuriss, les scan rtiniens et d'autres types de systmes d'identification biomtriques sont utiliss. Si vous crivez un programme qui doit authentifier ses utilisateurs, vous devriez permettre l'administrateur systme de slectionner le moyen d'authentification qu'il souhaite utiliser. GNU/Linux propose une bibliothque trs utile pour vous faciliter la tche. Ce mcanisme, appel Pluggable Authentication Modules (Modules d'Authentification Enfichable), ou PAM, facilite l'criture d'application qui authentifient leurs utilisateurs comme le dsire l'administrateur systme. Il est plus simple de comprendre le fonctionnement de PAM en tudiant une application PAM simple. Le Listing pam illustre l'utilisation de PAM. Exemple d'Utilisation de PAM pam.c
#include <security/pam_appl.h> #include <security/pam_misc.h> #include <stdio.h> int main () { pam_handle_t* pamh; struct pam_conv pamc; /* Initialise la conversation PAM. */ pamc.conv = &misc_conv; pamc.appdata_ptr = NULL; /* Dmarre une nouvelle session d'authentification. */ pam_start ("su", getenv ("USER"), &pamc, &pamh); /* Authentifie l'utilisateur. */ if (pam_authenticate (pamh, 0) != PAM_SUCCESS) fprintf (stderr, "chec de l'authentification !\n"); else fprintf (stderr, "Authentification OK.\n"); /* Fini. */ pam_end (pamh, 0); return 0;

Pour compiler ce programme, vous devez le lier deux bibliothques: libpam et une bibliothque utilitaire appele libpam_misc:
% gcc -o pam pam.c -lpam -lpam_misc

Ce programme commence par construire un objet de conversation PAM. Cet objet est utilis par la bibliothque PAM lorsqu'elle a besoin d'obtenir des informations de la part des utilisateurs. La fonction misc_conv utilise dans cet exemple est une fonction standard utilisant le terminal pour les entres sorties. Vous pouvez crire votre propre fonction qui affiche une bote de dialogue, utilise la parole pour les entres/sorties ou propose mme des mthodes d'interactions plus exotiques. Le programme appelle ensuite pam_start. Cette fonction initialise la bibliothque PAM. Le premier argument est un nom de service. Vous devez utiliser un nom qui identifie de faon unique votre application.

- 158 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Par exemple, si votre application s'appelle whizbang, vous devriez utiliser ce nom pour le service. Cependant, le programme ne fonctionnera probablement pas moins qu'un administrateur systme ne configure explicitement le systme pour fonctionner avec votre service. Revenons notre exemple, nous utilisons le service su, qui indique que notre programme authentifie les utilisateurs de la mme faon que la commande su. Vous ne devriez pas utiliser cette technique dans un programme rel. Slectionnez un nom de service qui vous est propre et concevez vos scripts d'installation pour aider l'administrateur configurer PAM correctement pour votre application. Le second argument est le nom de l'utilisateur que vous voulez authentifier. Dans cet exemple, nous utilisons la valeur de la variable d'environnement USER (en thorie, il s'agit du nom d'utilisateur correspondant l'user ID effectif du processus courant, mais ce n'est pas toujours le cas). Dans la plupart des programmes rels, vous afficheriez une invite pour saisir le nom d'utilisateur. Le troisime argument est la conversation PAM, que nous avons prsente prcdemment. L'appel pam_start renseigne le handle pass en quatrime argument. Passez ce handle aux appels ultrieurs aux fonctions de la bibliothque PAM. Ensuite, le programme appelle pam_authenticate. Le second argument vous permet de spcifier diverses options; la valeur 0 demande l'utilisation des valeurs par dfaut. La valeur de retour de cette fonction indique le rsultat de l'authentification. Finalement, le programme appelle pam_end pour librer toutes les structures de donnes alloues. Supposons que le mot de passe pour l'utilisateur courant soit password (un mot de passe extrmement faible). Alors, l'excution de ce programme avec le mot de passe correct produit le comportement attendu:
% ./pam Password: password Authentification OK.

Si vous excutez ce programme dans un terminal, le mot de passe n'apparatra probablement pas lorsque vous le saisirez: il est masqu pour viter qu'une autre personne ne puisse l'apercevoir alors que vous l'entrez. Par contre, si un hacker tente d'utiliser un mauvais mot de passe, la bibliothque PAM signalera l'chec correctement:
% ./pam Password: rat chec de l'authentification !

Les bases que nous avons prsent sont suffisantes pour la plupart des programmes simples. Une documentation complte sur le fonctionnement de PAM est disponible sous /usr/doc/pam sur la plupart des systmes GNU/Linux.

10-6 - Autres failles de scurit


Bien que ce chapitre prsente quelques failles de scurit rpandues, vous ne devez en aucun cas compter sur ce livre pour couvrir toutes les failles possibles. Beaucoup on dj t trouvs et beaucoup plus attendent de l'tre. Si vous essayez d'crire du code scuris, il n'y a rellement pas d'autre solution que de faire appel un expert pour un audit de code.

10-6-1 - Dpassement de tampon


Pratiquement toutes les applications Internet majeures, y compris sendmail, finger, tal et d'autres, ont a un moment donn t victimes de failles dites de dpassement de tampon. Si vous crivez du code destin tre excut en tant que root, vous devez absolument tre familier avec ce type de failles de scurit. Cela s'applique galement si vous crivez un programme qui utilise les mcanismes de communication interprocessus. Si vous crivez un programme qui lit des fichiers (ou pourrait lire des fichiers) vous
- 159 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

devez l aussi connatre les concepts de cette faille. Ce dernier critre s'applique presque tous les programmes. Fondamentalement, si vous avez l'intention d'crire des applications GNU/Linux, vous devez connatre ce type de failles. L'ide sous-jacente d'une attaque par dpassement de tampon est de faire excuter un programme du code qu'il n'tait pas cens excuter. Le mode opratoire habituel est d'craser une partie de la pile du processus. La pile du programme contient, entre autres, l'adresse mmoire laquelle le programme doit transfrer le contrle la fin de la fonction en cours. Ainsi, si vous placez le code que vous voulez excuter quelque part en mmoire et que vous modifiez l'adresse de retour pour pointer cet emplacement, vous pouvez faire excuter n'importe quoi au programme. Lorsque le programme terminera la fonction en cours d'excution, il sautera vers le nouveau code et excutera ce qu'il contient, avec les privilges du processus en cours. Il est clair que si le programme s'excute en tant que root, ce serait un dsastre. Si le processus s'excute avec les privilges d'un autre utilisateur, ce n'est un dsastre que pour cetutilisateur ? et par consquent pour les utilisateurs dpendants de ses fichiers. Si le programme s'excute en tant que dmon en attente de connexions rseau, la situation est encore pire. Un dmon s'excute habituellement en tant que root. S'il contient des bugs de type dpassement de tampon, n'importe quelle personne pouvant se connecter l'ordinateur excutant le dmon peut en prendre le contrle en envoyant une squence de donnes au dmon via le rseau. Un programme qui n'utilise pas les communications rseau est plus sr car seuls les utilisateurs disposant d'un compte sur la machine peuvent l'attaquer. Les versions de finger, talk et sendmail concernes par ce type de bug partageaient toutes la mme faille. Toutes utilisaient un tampon de taille fixe pour lire une chane, ce qui impliquait une limite suprieure constante pour la taille de la chane, mais permettaient quand mme aux clients d'envoyer des chanes plus grandes que le tampon. Voici le genre de code qu'elles pouvaient contenir:
#include <stdio.h> int main () { /* Personne de cens n'aurait plus de 32 caractres dans son nom d'utilisateur. De plus, il me semble qu'UNIX ne permet que des nom de 8 caractres. Il y a donc suffisamment de place. */ char username[32]; /* Demande le nom de l'utilisateur. */ printf ("Saisisez votre identifiant de connexion : "); /* Lit une ligne saisie par l'utilisateur. */ gets (username); /* Traitements divers... */ } return 0;

L'utilisation conjointe d'un tampon de 32 caractres et de la fonction gets ouvre la porte un dpassement de tampon. La fonction gets lit la saisie de l'utilisateur jusqu' ce qu'un caractre de nouvelle ligne apparasse et stocke le rsultat dans le tampon username. Les commentaires dans le code sont corrects en ce sens que les utilisateurs ont gnralement des identifiants courts, aucun utilisateur bien intentionn ne saisirait plus de 32 caractres. Mais vous crivez un logiciel scuris, vous devez adopter le point de vue d'un attaquant. Dans ce cas, l'attaquant pourrait dlibrment saisir un nom d'utilisateur trs long. Les variables locales comme username sont stockes dans la pile, aussi, en dpassant les limites du tableau, il est possible de placer des octets arbitraires sur la pile au del de la zone rserve la variable username. Le nom d'utilisateur dpasse alors le tampon et crase une partie de la pile, permettant une attaque telle que celle dcrite prcdemment. Heureusement, il est facile d'viter les dpassements de tampon. Lorsque vous lisez des chanes, vous devriez toujours utiliser soit une fonction, comme getline, qui alloue dynamiquement suffisamment d'espace soit une fonction qui interrompt la lecture lorsque le tampon est plein. Voici un exemple d'utilisation de getline:

Cet appel utilise automatiquement malloc pour allouer un tampon suffisamment grand pour contenir la ligne et vous le renvoie. Vous ne devez pas oublier d'appeler free pour librer le tampon afin d'viter les fuites mmoire.
- 160 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Vous vous faciliterez la vie si vous utiliser le C++ ou un autre langage proposant des primitives simples pour lire les saisies utilisateur. En C++, par exemple, vous pouvre utiliser cette simple instruction:
string username; getline (cin, username);

La chane username sera galement dsalloue automatiquement, vous n'avez pas besoin d'appeler free(Certains programmeurs pensent que le C++ est un langage horrible et compliqu.Leurs arguments sur l'hritage multiple et d'autres complication ont un certain mrite, mais il est plus simple d'crire du code vitant les dpassements de tampon et autres problmes similaires en C++ qu'en C.). Bien sr, les dpassements de tampon peuvent survenir avec n'importe quel tableau dimensionn de faon statique, pas seulement avec les chanes de caractres. Si vous voulez produire un code scuris, vous ne devriez jamais crire dans une structure de donnes, sur la pile ou ailleurs, sans vrifier que vous n'allez pas dpasser ses limites.

10-6-2 - Conditions de concurrence critique dans /tmp


Une autre problme trs rpandu converne la cration de fichiers avec des noms prdictibles, typiquement dans le rpertoire /tmp. Supposons que votre programme prog, qui s'excute avec les droits root, cre toujours un fichier temporaire appel /tmp/prog et y crive des informations vitales. Un utilisateur mal intentionn pourrait crer un lien symbolique sous /tmp/prog vers n'importe quel fichier du systme. Lorsque votre programme tente de crer le fichier, l'appel systme open n'chouera pas. Cependant, les donnes que vous crirez n'iront pas vers /tmp/prog mais seront crites dans le fichier choisi par l'attaquant. On dit de ce genre d'attaque qu'elle exploite un condition de concurrence critique. Il y a une concurrence implicite entre vous et l'attaquant. Celui qui arrive crer le fichier en premier gagne. Cette attaque est gnralement utilise pour dtruire des lments importants du systme de fichiers. En crant les liens appropris, l'attaquant peut utiliser un programme s'excutant en tant que root croyant crire dans un fichier temporaire pour craser un fichier systme important. Par exemple, en crant un lien symbolique vers /etc/passwd, l'attaquant peut effacer la base de donnes des mots de passe du systme. Il existe galement des moyens pour l'attaquant d'obtenir un accs root en utilisant cette technique. Une piste pour viter ce genre d'attaque serait d'utiliser un nom alatoire pour le fichier. Par exemple, vous pourriez utiliser /dev/random pour injecter une partie alatoire dans le nom du fichier. Cela complique bien sr la tche de l'attaquant pour deviner le nom du fichier, mais cela ne l'en empche pas. Il pourrait crer un nombre consquent de liens symboliques en utilisant beaucoup de nom potentiels. Mme s'il doit essayer 10 000 fois avant d'obtenir des conditions de concurrence critique, cette seule fois peut tre dsastreuse. Une autre approche est d'utiliser l'option O_EXCL lors de l'appel open. Cette option provoque l'chec de l'ouverture si le fichier existe dj. Malheureusement, si vous utilisez le Network File System (NFS), ou si un utilisateur de votre programme est susceptible d'utiliser NFS, cette approche n'est pas assez robuste car O_EXCL n'est pas fiable sur un systme de fichier NFS. Vous ne pouvez pas savoir avec certitude si votre code sera utilis sur un systme disposant de NFS, aussi, si vous tes paranoaque, ne vous reposez pas sur O_EXCL. Dans le Chapitre logicielsQualite, Chapitre logicielsQualite, Section fichierstemporaires, Utilisation de Fichiers temporaires , nous avons prsent mkstemp. Malheureusement, sous Linux, mkstemp ouvre le fichier avec l'option O_EXCL aprs avoir dtermin un nom suffisamment dur deviner. En d'autres termes, l'utilisation de mkstemp n'est pas sre si /tmp est mont via NFS(Bien sr, si vous tes administrateur systme, vous ne devriez pas monter un systme NFS sur /tmp.). L'utilisation de mkstemp est donc mieux que rien mais n'est pas totalement sre. Une approche qui fonctionne est d'utiliser lstat sur le nouveau fichier (lstat est prsent dans la Section B.2, stat ). La fonction lstat est similaire stat, except que si le fichier est lien symbolique, lstat vous donne des informations sur ce lien et non sur le fichier vers lequel il pointe. Si lstat vous indique que votre nouveau fichier est un fichier ordinaire, pas un lien symbolique, et que vous en tes le propritaire, alors tout devrait bien se passer.
- 161 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Le Listing tempfile2 prsente une fonction tentant d'ouvrir un fichier dans /tmp de faon scurise. Les auteurs de ce livre ne l'ont pas fait audit de faon professionnelle et ne sont pas non plus des experts en scurit, il y a donc de grandes chances qu'elle ait une faiblesse. Nous ne vous recommandons pas son utilisation sans l'avoir faite auditer, mais elle devrait vous convaincre que l'criture de code scuris est complexe. Pour vous dissuader encore plus, nous avons dlibrment dfini l'interface de faon ce qu'elle soit complexe utiliser dans un programme rel. La vrification d'erreurs tient une place importante dans l'criture de logiciels scuriss, nous avons donc inclus la logique de contrle d'erreurs dans cet exemple. Crer un Fichier Temporaire temp-file.c
#include #include #include #include <fcntl.h> <stdlib.h> <sys/stat.h> <unistd.h>

/* Renvoie le descripteur de fichier d'un nouveau fichier temporaire. Le fichier pourra tre lu ou crit par l'user ID effectif du processus courant et par personne d'autre. Renvoie -1 si le fichier temporaire ne peut pas tre cr. */

int secure_temp_file () { /* Ce descripteur de fichier pointe vers /dev/random et nous permet de disposer d'une bonne source de nombres alatoires. */ static int random_fd = -1; /* Entier alatoire. */ unsigned int random; /* Tampon utilis pour convertir random en chane de caractres. Le tampon une taille fixe, ce qui signifie que nous sommes potentiellement vulnrables un bug de dpassement de tampon si les entiers de la machine d'excution tiennent sur un nombre *consquent* de bits. */ char filename[128]; /* Descripteur de fichier du nouveau fichier temporaire. */ int fd; /* Informations sur le nouveau fichier. */ struct stat stat_buf; /* Si nous n'avons pas encore ouvert /dev/random nous le faisons maintenant. Cette faon de faire n'est pas threadsafe. */ if (random_fd == -1) { /* Ouvre /dev/random. Notez que nous supposons ici que /dev/random est effectivement une source de bits alatoire et non pas un fichier rempli de zros plac ici par l'attaquant. */ random_fd = open ("/dev/random", O_RDONLY); /* Abandonne si l'on ne peut pas ouvrir /dev/random. */ if (random_fd == -1) return -1; } /* Lit un entier partir de /dev/random. */ if (read (random_fd, &random, sizeof (random)) != sizeof (random)) return -1; /* Cre un fichier partir du nombre alatoire. */ sprintf (filename, "/tmp/%u", random); /* Tente d'ouvrir le fichier. */ fd = open (filename, /* Nous utilisons O_EXECL, mme si cela ne fonctionne pas avec NFS. */ O_RDWR | O_CREAT | O_EXCL, /* Personne ne doit pouvoir lire ou crire dans le fichier. */ S_IRUSR | S_IWUSR); if (fd == -1) return -1; /* Appelle lstat sur le fichier afin de s'assurer qu'il ne s'agit pas d'un lien symbolique. */

- 162 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Crer un Fichier Temporaire temp-file.c

if (lstat (filename, &stat_buf) == -1) return -1; /* Si le fichier n'est pas un fichier traditionnel, quelqu'un a tent de nous piger. */ if (!S_ISREG (stat_buf.st_mode)) return -1; /* Si le fichier ne nous appartient pas, quelqu'un d'autre pourrait le supprimer, le lire ou le modifier alors que nous nous en servons. */ if (stat_buf.st_uid != geteuid () || stat_buf.st_gid != getegid ()) return -1; /* Si il y a d'autres bits de permissions actifs, quelque chose cloche. */ if ((stat_buf.st_mode & ~(S_IRUSR | S_IWUSR)) != 0) return -1; return fd;

Cette fonction appelle open pour crer le fichier puis appelle lstat quelques lignes plus loin pour s'assurer que le fichier n'est pas un lien symbolique. Si vous rflchissez attentivement, vous raliserez qu'il semble y avoir une condition de concurrence critique dans ce cas. En effet, un attaquant pourrait supprimer le fichier et le remplacer par un lien symbolique entre le moment o nous appelons open et celui o nous appelons lstat. Cela n'aurait pas d'impact direct sur cette fonction car nous avons dj un descripteur de fichier ouvert pointant sur le nouveau fichier, mais nous indiquerions une erreur l'appelant. Cette attaque ne causerait pas de dommages directs mais rendrait impossible le fonctionnement de l'appelant. Une telle attaque est dite dni de service (DoS, Denial of Service). Heureusement, le sticky bit vient notre aide. Comme le sticky bit est actif sur /tmp, personne d'autre que nous ne peut supprimer les fichiers de ce rpertoire. Bien sr, root peut toujours supprimer des fichiers, mais si l'attaquant dispose dj des privilges root, il n'y a rien qui puisse protger votre programme. Si vous choisissez de supposer que l'administrateur systme est comptent, alors /tmp ne sera pas mont via NFS. Et si l'administrateur systme est suffisamment stupide pour monter /tmp en NFS, il y a de bonnes chances que le sticky bit ne soit pas actif non plus. Aussi, pour la plupart des utilisations, nous pensons qu'il est sr d'utiliser mkstemp. Mais vous devez tre conscient de ces problmes et ne devez pas vous reposer sur le fonctionne de O_EXCL pour un fonctionnement correct si le rpertoire utilis n'est pas /tmp ? pas plus que vous ne devez supposer que le sticky bit est actif ailleurs.

10-6-3 - Utilisation de system ou popen


La troisime faille de scurit que tout programme devrait avoir en tte est l'utilisation du shell pour excuter d'autres programme. Prenons l'exemple fictif d'un serveur dictionnaire. Ce programme est conu pour accepter les connexions venant d'Internet. Chaque client envoie un mot et le serveur indique s'il s'agit d'un mot anglais valide. Comme tout systme GNU/Linux dispose d'une liste d'environ 45000 mots anglais dans /usr/share/dict/word, une faon simple de crer ce serveur est d'invoquer le programme grep, comme ceci:
% grep -x word /usr/dict/words

Ici, word est le mot que souhaite valider l'utilisateur. Le code de sortie de grep nous indiquera si le mot figure dans /usr/ share/dict/words(Si vous ne connaissez pas grep, vous devriez consulter les pages de manuel. C'est un programme incoyablement utile.). Le Listing grepdictionary vous montre comment vous pourriez coder la partie du serveur invoquant grep: Cherche un Mot dans le Dictionnaire grep-dictionary.c
#include <stdio.h> #include <stdlib.h> - 163 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Cherche un Mot dans le Dictionnaire grep-dictionary.c


/* Renvoie une valeur diffrente de 0 si et seulement si WORD figure dans /usr/dict/words. */ int grep_for_word (const char* word) { size_t length; char* buffer; int exit_code; /* Construit la chane "grep -x WORD /usr/dict/words". Alloue la chane dynamiquement pour viter les dpassements de tampon. */ length = strlen ("grep -x ") + strlen (word) + strlen (" /usr/dict/words") + 1; buffer = (char*) malloc (length); sprintf (buffer, "grep -x %s /usr/dict/words", word); /* Excute la commande. */ exit_code = system (buffer); /* Libre le tampon. */ free (buffer); /* Si grep a renvoy 0, le mot tait prsent dans le dictionnaire. */ return exit_code == 0;

Remarquez qu'en calculant le nombre de caractres dont nous avons besoin et en allouant le tampon dynamiquement, nous sommes srs d'viter les dpassements de tampon. Malheureusement, l'utilisation de la fonction system (dcrite dans le Chapitre processus, Chapitre processus, Section utilisersystem, Section utilisersystem) n'est pas sr. Cette fonction invoque le shell systme standard pour lancer la commande puis renvoyer la valeur de sortie. Mais que se passe-t-il si un attaquant envoie un mot qui est fait la ligne suivante ou quelque chose du mme type?
foo /dev/null; rm -rf /

Dans ce cas, le serveur excutera cette commande :


grep -x foo /dev/null; rm -rf / /usr/dict/words

Le problme est maintenant vident. L'utilisateur a transform une commande, l'invocation de grep, en deux commandes car le shell traite le point virgule comme un sparateur de commandes. La premire commande est toujours l'invocation inoffensive de grep, mais la seconde supprime tous les fichiers du systme! Mme si le serveur ne s'excute pas en tant que root, tous les fichiers qui peuvent tre supprims par l'utilisateur sous lequel s'excute le serveur seront supprims. Le mme problme peut survenir avec popen (dcrit dans la Section popenetpclose, Section popenetpclose), qui cre un pipe entre le processus pre et le fils mais utilise quand mme le shell pour lancer la commande. Il y a deux faons d'viter ces problmes. La premire est d'utiliser les fonctions de la famille exec au lieu de system et popen. Cette solution contourne le problme car les caractres considrs comme spciaux par le shell (comme le point-virgule dans la commande prcdente) ne sont pas traits lorsqu'ils apparaissent dans les arguments d'un appel exec. Bien sr, vous abandonnez le ct pratique de system et popen. L'autre alternative est de valider la chane pour s'assurer qu'elle n'est pas dangereuse. Dans l'exemple du serveur dictionnaire, vous devriez vous assurer que le mot ne contient que des caractres alphabtiques, en utilisant la fonction isalpha. Si elle ne contient pas d'autres caractres, il n'y a aucun moyen de piger le shell en lui faisant excuter une autre commande. N'implmentez pas la vrification en recherchant des caractres dangereux ou inattendus; il est toujours plus sr de rechercher explicitement les caractres dont vous savez qu'ils sont srs que d'essayer d'anticiper sur tous les caractres pouvant tre problmatiques.

- 164 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

11 - Autres Outils de Dveloppement


Le dveloppement de programmes GNU/Linux rapides et fiables en C ou en C++ ncessite plus que la comprhension en surface du fonctionnement du systme d'exploitation et des appels systme. Dans cette annexe, nous prsenterons des outils de dveloppement permettant de dtecter les erreurs l'excution comme l'utilisation incorrecte d'une zone mmoire alloue dynamiquement et de dterminer quelles parties du programme monopolisent le plus de temps d'excution. L'analyse du code source d'un programme peut rvler certaines de ces informations; en utilisant ces outils d'analyse dynamique et en excutant effectivement le programme, vous pouvez en obtenir beaucoup plus.

11-1 - Analyse Statique de Programmes


Certaines erreurs de programmation peuvent tre dtectes en utilisant des outils d'analyse statique qui tudient le code source du programme. Si vous invoquez GCC avec l'option -Wall ou -pedantic, le compilateur affiche des avertissement sur les structures de programmation risques ou potentiellement fausses. En liminant de telles constructions, vous rduirez les risques de bugs et faciliterez la compilation de vos programmes sur d'autres variantes de GNU/Linux et mme sur d'autres systmes d'exploitation. En utilisant diverses options, vous pouvez faire en sorte que GCC mette des avertissements sur un nombre impressionnant de structures de programmation douteuses. L'option -Wall active la plupart des vrifications. Par exemple, le compilateur affichera un avertissement sur un commentaire commenant au sein d'un autre commentaire, sur un type de retour incorrect pour la fonction main ou pour une fonction non void qui ne dispose pas d'instruction return. Si vous utilisez l'option -pedantic, GCC met des avertissements pour tout ce qui ne respecte pas les normes ANSI C et ISO C++. Par exemple, l'utilisation de l'extension GNU asm provoque l'mission d'un avertissement avec cette option. Un petit nombre d'extensions GNU, comme l'utilisation des mots cls alternatifs commenant par % %__%% (deux tirets bas), ne dclencheront aucun message d'avertissement. Bien que la page info de GCC marque l'utilisation de ces options comme dprcie, nous vous recommandons de les utiliser pour viter la plupart des extensions GNU du langage car ces extensions ont tendance changer au cours du temps et interagir de faon nfaste sur l'optimisation du code. Programme Coucou hello.c
main() { printf("Coucou.\n"); }

Compilez le programme "Coucou" du Listing hello. Bien que GCC le compile sans rien dire, le code source n'obit pas aux rgles ANSI C. Si vous activez les avertissement en compilant avec les options -Wall -pedantic, GCC indique trois constructions douteuses.
% gcc -Wall -pedantic hello.c hello.c:2: warning: return type defaults to "int" hello.c: In function "main": hello.c:3: warning: implicit declaration of function "printf" hello.c:4: warning: control reaches end of non-void function

Ces avertissements indiquent les problmes suivants: Le type de retour de main n'est pas prcis; La fonction printf est dclare de faon implicite car <stdio.h> n'est pas inclus; La fonction, implicitement dclare comme renvoyant un int, ne renvoie en fait aucune valeur.

L'analyse du code source d'un programme ne permet pas de dtecter toutes les erreurs de programmation et les lacunes. Dans la section suivante, nous prsentons quatre outils aidant dtecter les erreurs commises dans

- 165 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

l'utilisation de mmoire alloue dynamiquement. Dans les sections suivantes, nous montrerons comment analyser le temps d'excution d'un programme l'aide du profiler gprof.

11-2 - Dtection des Erreurs d'Allocation Dynamique


Lorsque vous crivez un programme, vous ne savez gnralement pas quelle quantit de mmoire il ncessitera pour s'excuter. Par exemple, une ligne lue partir d'un fichier au moment de l'excution peut avoir n'importe quelle taille. Les programmes C et C++ utilisent malloc, free et leurs variantes pour allouer dynamiquement de la mmoire pendant l'excution du programme. Voici quelques rgles sur l'utilisation de mmoire alloue dynamiquement: Le nombre d'allocations (appels malloc) doit correspondre exactement au nombre de librations (appels free); Les lectures et critures doivent se faire dans l'espace allou, pas au del; La mmoire alloue ne doit pas tre utilise avant son allocation ou aprs sa libration.

Comme l'allocation et la libration dynamiques ont lieu durant l'excution, les analyses statiques ne peuvent que rarement dtecter les violations de ces rgles. C'est pourquoi il existe des programmes de vrification mmoire qui excutent le programme et collectent des informations pour dterminer si une telle violation a lieu. Voici un exemple de types de violations pouvant tre dtects: Lecture d'un emplacement mmoire avant allocation; criture dans un emplacement mmoire avant son allocation; Lecture d'une position se trouvant avant la zone alloue; criture d'une position se trouvant avant la zone alloue; Lecture d'une position se trouvant aprs la fin de la zone alloue; criture d'une position se trouvant aprs la fin de cette zone; Lecture d'un emplacement mmoire aprs sa libration; criture dans un emplacement aprs sa libration; chec de la libration de la mmoire; Double libration de mmoire; Libration de mmoire non alloue.

Il est galement utile d'tre averti lors d'une demande d'allocation de zro octet car il s'agit certainement d'une erreur de la part du programmeur. Le Tableau outilsmem indique les fonctionnalits des quatre outils de diagnostic. Malheureusement, il n'existe pas d'outil diagnostiquant toutes les erreurs d'utilisation de la mmoire. De plus, aucun outil ne dtecte l'criture ou la lecture d'une zone non alloue, toutefois, une telle opration dclencherait probablement une erreur de segmentation. Ces outils ne dtectent que les erreurs survenant durant l'excution du programme. Si vous excutez le programme avec des donnes qui ne ncessitent pas d'allocation mmoire, les outils ne dtecteront aucun problme. Pour tester un programme de faon exhaustive, vous devez l'excuter en utilisant diffrentes donnes d'entre pour vous assurer que tous les chemins possible au sein de votre programme sont parcourus. De plus, vous ne pouvez utiliser qu'un seul outil la fois, vous devez donc recommencer les mmes tests avec plusieurs outils pour vous assurer d'avoir le plus de vrifications possibles. Fonctionnalits de Diffrents Outils de Vrification Mmoire (X : prise en charge totale, O : dtection occasionnelle)

- 166 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Comportement erron Lecture avant allocation criture avant allocation Lecture avant la zone alloue criture avant la zone alloue Lecture aprs la zone alloue criture aprs la zone alloue Lecture aprs libration criture aprs libration chec de libration Double libration Libration de mmoire non alloue Allocation de taille nulle

malloc

mtrace

ccmalloc

Electric Fence

X O O X X X X X X X X X X X X X X

Dans les sections qui suivent, nous dcrirons comment utiliser la vrification fournie par malloc et mtrace, qui sont les deux plus simples, puis nous nous intresserons ccmalloc et Electric Fence.

11-2-1 - Programme de Test d'Allocation et de Libration Mmoire


Nous utiliserons les programme malloc-use du Listing mallocuse pour illustrer l'allocation, la libration et l'utilisation de la mmoire. Pour le lancer, passez-lui le nombre maximum de rgions mmoire allouer en premier argument. Par exemple, malloc-use 12 cre un tableau A avec 12 pointeurs de caractres qui ne pointent sur rien. Le programme accepte cinq commandes diffrentes: Pour allouer b octets sur lesquels pointe l'entre A[i], saisissez a i b. L'indice i peut tre n'importe quel nombre non ngatif infrieur l'argument de ligne de commande; Pour librer la mmoire se situant l'indice i, entrez d i; Pour lire le p<sup>me</sup> caractre de la mmoire alloue l'indice i (comme avec A[i][p]), saisissez r i p. Ici, p peut avoir n'importe quelle valeur entire; Pour crire un caractre la p<sup>me</sup> position de la mmoire alloue l'indice i, entrez w i p; Lorsque vous avez termin, saisissez q.

Nous prsenterons le code du programme plus loin dans la Section A.2.7 et illustrerons comment l'utiliser.

11-2-2 - Vrification par malloc


Les fonctions d'allocation mmoire fournies avec la bibliothque C GNU peut dtecter l'criture en mmoire avant une zone alloue et la double libration.

- 167 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

La dfinition de la variable d'environnement MALLOC_CHECK_ la valeur 2 provoque l'arrt d'un programme lorsqu'une telle erreur est dtecte (attention, la variable d'environnement se termine par un tiret bas). Il n'est pas ncessaire de recompiler le programme. Voici un exemple de dtection d'criture un emplacement mmoire juste avant le dbut d'une zone alloue:
% export MALLOC_CHECK_=2 % ./malloc-use 12 Please enter a command: a 0 10 Please enter a command: w 0 -1 Please enter a command: d 0 Aborted (core dumped)

la commande export active les vrifications de malloc. La valeur 2 provoque l'arrt immdiat du programme lors de la dtection d'une erreur. L'utilisation de la vrification par malloc est avantageuse car elle ne ncessite aucune recompilation, mais l'tendue des vrifications est assez limite. Fondamentalement, il s'agit de s'assurer que les structures de donnes utilises pour l'allocation mmoire ne sont pas corrompues. C'est pourquoi il est possible de dtecter une double libration du mme emplacement mmoire. De plus, l'criture juste avant le dbut d'une zone alloue peut tre dtecte facilement car le dispositif d'allocation mmoire stocke la taille de chaque zone alloue juste avant celle-ci. Aussi, une criture juste avant la zone corrompt cette taille. Malheureusement, la vrification de cohrence ne peut avoir lieu que lorsque le programme appelle des routines d'allocation, pas lorsqu'il accde la mmoire, aussi, un nombre important de lectures et d'critures peuvent avoir lieu avant qu'une erreur ne soit dtecte. Dans l'exemple prcdent, l'criture illgale n'a t dtecte que lorsque la mmoire a t libre.

11-2-3 - Recherche de Fuites Mmoire avec mtrace


L'outil mtrace aide diagnostiquer les erreurs les plus courantes lors de l'allocation dynamique de mmoire: la non concordance entre le nombre d'allocations et de librations. L'utilisation de mtrace se fait en quatre tapes, mtrace est fourni avec la bibliothque C GNU: - Modifier le code source pour inclure <mcheck.h> et invoquer mtrace() ds le dbut du programme, au dbut de main. L'appel mtrace active la surveillance des allocations et librations mmoire; - Indiquer le nom d'un fichier pour stocker les informations sur les allocations et librations mmoire: % export MALLOC_TRACE=memory.log - Excuter le programme. Toutes les allocations et librations sont stockes dans le fichier journal. - Utiliser la commande mtrace pour analyser les allocations et librations mmoire pour s'assurer que leur nombre concorde. % mtrace mon_programme $MALLOC_TRACE Les messages mis par mtrace sont relativement simples comprendre. Par exemple, avec notre excution de malloc-use, ils seraient de ce type:
- 0000000000 Free 3 was never alloc'd malloc-use.c:39 Memory not freed: ----------------Address Size Caller 0x08049d48 0xc at malloc-use.c:30

Ces messages indiquent une libration de mmoire qui n'a jamais t alloue la ligne 39 de malloc-use.c et une zone de mmoire alloue la ligne 30 non libre.

- 168 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

mtrace dtecte les erreurs grace l'analyse du fichier spcifi par la variable d'environnement MALLOC_TRACE, celui-ci contient la liste de toutes les allocations et librations mmoire du programme. L'excutable doit se terminer normalement pour que les donnes soient crites. La commande mtrace analyse le fichier et dresse la liste des allocations et librations qui n'ont pas de rciproque.

11-2-4 - Utiliser ccmalloc


La bibliothque ccmalloc dtecte les erreurs mmoire en remplaant les appels malloc et free avec des instructions traant leur utilisation. Si le programme se termine correctement, elle cre un rapport concernant les fuites mmoire et d'autres erreurs. La bibliothque ccmalloc a t crite par Armin Bierce. Vous devrez probablement tlcharger et installer la bibliothque ccmalloc vous-mme. Elle est disponible sur http:// www.inf.ethz.ch/personal/biere/projects/ccmalloc/, dcompressez les sources et lancez configure. Excutez make et make install, copiez le fichier ccmalloc.cfg dans le rpertoire d'o vous lancerez le programme que vous voulez contrler et renommez la copie en .ccmalloc. Vous tes maintenant prt utiliser ccmalloc. Les fichiers objets du programme doivent tre lis avec la bibliothque ccmalloc et la bibliothque de liaison dynamique. Ajoutez -lccmalloc et -ldl votre commande d'dition de liens, par exemple:
% gcc -g -Wall -pedantic malloc-use.o -o ccmalloc-use -lccmalloc -ldl

Lancez votre programme pour crer un rapport. Par exemple, l'excution de notre programme malloc-use de faon ce qu'il ne libre pas une zone mmoire produit le rapport suivant:
% ./ccmalloc-use 12 file-name=a.out does not contain valid symbols trying to find executable in current directory ... using symbols from "ccmalloc-use" (to speed up this search specify "file ccmalloc-use" in the startup file ".ccmalloc") Please enter a command: a 0 12 Please enter a command: q. ---------------. |ccmalloc report| ======================================================== | total # of| allocated | deallocated | garbage | +-----------+-------------+-------------+--------------+ | bytes | 60 | 48 | 12 | +-----------+-------------+-------------+--------------+ |allocations| 2| 1| 1| +------------------------------------------------------+ | number of checks: 1 | | number of counts: 3 | | retrieving function names for addresses ... done. | | reading file info from gdb ... done. | | sorting by number of not reclaimed bytes ... done. | | number of call chains: 1 | | number of ignored call chains: 0 | | number of reported call chains: 1 | | number of internal call chains: 1 | | number of library call chains: 0 | ======================================================== | *100.0% = 12 Bytes of garbage allocated in 1 allocation | | | | 0x400389cb in <???> | | | | 0x08049198 in <main> | | at malloc-use.c:89 | | | | 0x08048fdc in <allocate> | | at malloc-use.c:30 - 169 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) | | | +-----> 0x08049647 in <malloc> | at src/wrapper.c:284 | +------------------------------------------------------

Les dernires lignes donnent la liste des appels de fonctions qui ont allou de la mmoire sans la librer. Pour utiliser ccmalloc afin de diagnostiquer des criture avant le dbut ou aprs la fin d'une zone mmoire alloue, vous devrez modifier le fichier .ccmalloc dans le rpertoire du programme. Ce fichier est lu au dmarrage du programme.

11-2-5 - Electric Fence


crit par Bruce Perens, Electric Fence stoppe l'excution du programme la ligne exacte de la lecture ou de l'criture en dehors d'une zone alloue. Il s'agit du seul outil qui dtecte les lectures illgales. Il est inclus dans la plupart des distributions GNU/Linux, le code source est tout de mme disponible sur http://www.perens.com/FreeSoftware/. Comme pour ccmalloc, les fichiers objets de votre programme doivent tre lis la bibliothque Electric Fence en ajoutant -lefence la commande d'dition de liens, par exemple:

Electric Fence vrifie la validit des utilisations de la mmoire au cours de l'excution du programme. Une mauvaise utilisation provoque une erreur de segmentation:
% ./emalloc-use 12 Electric Fence 2.0.5 Copyright (C) 1987-1998 Bruce Perens. Please enter a command: a 0 12 Please enter a command: r 0 12 Segmentation fault

En utilisant un dbogueur, vous pouvez dterminer l'emplacement de l'action illgale. Par dfaut, Electric Fence ne diagnostique que les accs des emplacements situs aprs la zone alloue. Pour activer la dtection des accs des emplacements situs avant la zone alloue la place des accs des zones situes aprs, utilisez cette commande:
% export EF_PROTECT_BELOW=1

Pour dtecter les accs des emplacements mmoire librs, positionnez EF_PROTECT_FREE 1. Des fonctionnalits supplmentaires sont dcrites dans la page de manuel de libefence. Electric Fence peut diagnostiquer des accs illgaux en mobilisant au moins deux pages mmoire pour toute allocation. Il place la zone alloue la fin de la premire page, ainsi, tout accs aprs la fin de la zone alloue provoque une erreur de segmentation. Si vous positionnez EF_PROTECT_BELOW 1, il place la zone alloue au dbut de la seconde page. Comme chaque appel malloc utilise deux pages mmoire, Electric Fence peut consommer une quantit importante de mmoire. N'utilisez cette bibliothque qu' des fins de dbogage.

11-2-6 - Choisir Parmi les Diffrents Outils de Dbogage Mmoire


Nous avons prsent quatre outils distincts, incompatibles destins diagnostiquer de mauvaises utilisations de mmoire alloue dynamiquement. Alors comment un programmeur GNU/Linux peut-il s'assurer que la mmoire est utilise correctement? Aucun outil ne garantit la dtection de toutes les erreurs, mais l'utilisation de n'importe lequel d'entre eux augmente la probabilit d'en dcouvrir. Pour faciliter la dtection d'erreurs concernant la mmoire alloue
- 170 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

dynamiquement, dveloppez et testez le code l'utilisant sparment du reste. Cela rduit le volume de code analyser lors de la recherche de telles erreurs. Si vous crivez vos programmes en C++, ddiez une classe la gestion de la mmoire dynamique. Si vous programmez en C, minimisez le nombre de fonctions utilisant l'allocation et la libration. Lors des tests de ce code, assurez-vous qu'un seul outil s'excute la fois car ils sont incompatibles. Lorsque vous testez le programme, assurez-vous de varier la faon dont vous l'utilisez afin de tester toutes les portions de code. Lequel de ces outils devriez-vous utiliser? Comme l'absence d'quilibre entre les allocations et les librations est l'erreur la plus courante en matire de gestion dynamique de la mmoire, utilisez mtrace au dbut du processus de dveloppement. Ce programme est disponible pour tous les systme GNU/Linux et a t prouv. Aprs vous tre assur que les nombres d'allocations et de librations sont identiques, utilisez Electric Fence pour dtecter les accs mmoire invalides. Vous liminerez ainsi presque toutes les erreurs mmoire. Lorsque vous utilisez Electric Fence, vous devez tre attentif ne pas effectuer trop d'allocations et de librations car chaque allocation ncessite au moins deux pages mmoire. L'utilisation de ces deux outils vous permettra de dtecter la plupart des erreurs.

11-2-7 - Code Source du Programme d'Allocation Dynamique de Mmoire


Le Listing mallocuse prsente le code source d'un programme illustrant l'allocation dynamique, la libration et l'utilisation de mmoire. Consultez la Section progtestalloc, Section progtestalloc, pour une description de son utilisation. Dynamic Memory Allocation Checking Example malloc-use.c
/* Utilisation des fonctions C d'allocation mmoire. */ /* Invoquez le programme en utilisant un argument prcisant la taille du tableau. Ce tableau est compos de pointeurs sur des tableaux pouvant tre allous par la suite. Le programme accepte les commandes suivantes : o allouer de la mmoire : a <indice> <taille> o librer de la mmoire : d <indice> o lire un emplacement mmoire : r <indice> <position-dans-la-mmoire-alloue> o crire un emplacement : w <indice> <position-dans-la-mmoire-alloue> o quitter : q L'utilisateur a la responsabilit de respecter les rgles de l'allocation dynamique de la mmoire (ou non). */ #ifdef MTRACE #include <mcheck.h> #endif /* MTRACE */ #include <stdio.h> #include <stdlib.h> #include <assert.h> /* Alloue de la mmoire pour la taille spcifie, renvoie une valeur diffrentes de zro en cas de succs. */ void allocate (char** array, size_t size) { *array = malloc (size); } /* Libre la mmoire. */ void deallocate (char** array) { free ((void*) *array); } /* Lit un emplacement mmoire. */ void read_from_memory (char* array, int position) { char character = array[position]; } /* crit un emplacement mmoire. */ void write_to_memory (char* array, int position) { array[position] = 'a'; } int main (int argc, char* argv[]) { char** array; unsigned array_size; - 171 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Dynamic Memory Allocation Checking Example malloc-use.c

char command[32]; unsigned array_index; char command_letter; int size_or_position; int error = 0; #ifdef MTRACE mtrace (); #endif /* MTRACE */ if (argc != 2) { fprintf (stderr, "%s: taille-du-tableau\n", argv[0]); return 1; } array_size = strtoul (argv[1], 0, 0); array = (char **) calloc (array_size, sizeof (char *)); assert (array != 0); /* Effectue ce que l'utilisateur demande. */ while (!error) { printf ("Entrez une commande : "); command_letter = getchar (); assert (command_letter != EOF); switch (command_letter) { case 'a': fgets (command, sizeof (command), stdin); if (sscanf (command, "%u %i", &array_index, &size_or_position) == 2 && array_index < array_size) allocate (&(array[array_index]), size_or_position); else error = 1; break; case 'd': fgets (command, sizeof (command), stdin); if (sscanf (command, "%u", &array_index) == 1 && array_index < array_size) deallocate (&(array[array_index])); else error = 1; break; case 'r': fgets (command, sizeof (command), stdin); if (sscanf (command, "%u %i", &array_index, &size_or_position) == 2 && array_index < array_size) read_from_memory (array[array_index], size_or_position); else error = 1; break; case 'w': fgets (command, sizeof (command), stdin); if (sscanf (command, "%u %i", &array_index, &size_or_position) == 2 && array_index < array_size) write_to_memory (array[array_index], size_or_position); else error = 1; break; case 'q': free ((void *) array); return 0; default: error = 1; } } free ((void *) array); return 1; }

- 172 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

11-3 - Profilage
Maintenant que votre programme est correct (esprons-le), nous allons voir comment amliorer ses performances. Avec l'aide du profileur gprof, vous pouvez dterminer les fonctions qui monopolisent le plus de temps d'excution. Cela peut vous aider dterminer les portions du programme optimiser ou rcrire pour qu'elles s'excutent plus rapidement. Cela peut galement vous aider trouver des erreurs. Par exemple, vous pourriez dtecter qu'une fonction est appele beaucoup plus souvent que vous ne le supposiez. Dans cette sections, nous dcrirons comment utiliser gprof. La rcriture de code pour acclerer son excution ncessite de la crativit et un certain soin dans le choix des algorithmes. L'obtention d'informations de profilage demande de suivre trois tapes: - Compiler et lier votre programme de faon activer le profilage; - Excuter votre programme de faon gnrer les donnes de profilage; - Utiliser gprof pour analyser et afficher les donnes de profilage. Avant d'illustrer ces diffrentes tapes, nous allons prsenter une programme suffisamment important pour qu'il soit possible de le profiler.

11-3-1 - Une Calculatrice Simplifie


Pour illustrer le profilage, nous allons utiliser un programme faisant office de calculatrice. Pour nous assurer que son excution prend suffisamment de temps, nous utiliserons des nombres monadiques pour les calculs, chose que nous ne ferions jamais dans un programme rel. Le code de ce programme est prsent la fin de ce chapitre. Un nombre monadique (ou unaire) est reprsent par autant de symboles que la valeur qu'il reprsente. Par exemple, le nombre 1 est reprsent par "x", 2 par "xx" et 3 par "xxx". Au lieu d'utiliser des x, notre programme reprsentera un nombre positif en utilisant une liste chane constitue d'autant d'lments que la valeur du nombre. Le fichier number.c contient les fonctions pour crer le nombre 0, ajouter 1 un chiffre, soustraire 1 d'un nombre et ajouter, soustraire et multiplier des nombres. Une autre fonction convertit une chaine reprsentant une nombre dcimal positif en un nombre unaire et enfin, une dernire fonction permet de passer d'un nombre unaire un int. L'addition est implante en effectuant des additions successives du nombre un tandis que la soustraction utilise des soustractions rptitives du nombre 1. La multiplication utilise une rptition d'addtions. Les prdicats even et odd renvoient le nombre unaire 1 si et seulement si leur oprande est paire ou impaire (respectivement); sinon, ils renvoient le nombre unaire reprsentant 0. Ces deux prdicats s'appellent l'un l'autre, par exemple, un nombre est pair s'il vaut zro ou si ce nombre moins un est impair. La calculatrice accepte des expression postfixes(Dans la notation postfixe, un oprateur binaire est plac aprs ses oprandes plutt qu'entre elles. Ainsi, pour multiplier 6 par 8, vous utiliseriez 6 8 x. Pour multiplier 6 et 8 puis ajouter 5 au rsultat, vous utiliseriez 6 8 x 5 +.) sur une ligne et affiche la valeur de chaque expression -- par exemple:
% ./calculator Veuillez saisir une expression postfixe : 2 3 + 5 Veuillez saisir une expression postfixe : 2 3 + 4 1

La calculatrice, dfinie dans calculator.c, lit chaque expression et stocke les valeurs intermdiaires sur une pile de nombres unaires, dfinie dans stack.c. La pile stocke ses nombres unaires dans une liste chane.

- 173 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

11-3-2 - Collecter des Informations de Profilage


La premire tape dans le profilage d'un programme est de marquer son excutable de faon ce qu'il collecte des informations de profilage. Pour cela, utilisez l'option de compilation -pg lors de la compilation et de l'dition de liens. Par exemple:
% % % % gcc gcc gcc gcc -pg -pg -pg -pg -c -o calculator.o calculator.c -c -o stack.o stack.c -c -o number.o number.c calculator.o stack.o number.o -o calculator

Cette squence de commandes active la collecte d'informations sur les appels de fonction et l'horodatage. Pour collecter des informations d'utilisation ligne par ligne, utilisez l'option de dbogage -g. Pour compter le nombre d'excutions de blocs de base, comme le nombre d'itrations des boubles do, utilisez -a. La seconde tape est l'excution du programme. Pendant son excution, les donnes sont collectes dans un fichier nomm gmon.out, uniquement pour les portions de code qui ont t traverses. Vous devez varier les entres du programme ou les commandes pour excuter les sections du code que vous souhaitez profiler. Le programme doit se terminer normalement pour que le fichier de donnes de profilage soient crites correctement.

11-3-3 - Affichage des Donnes de Profilage


partir du nom d'un excutable, gprof analyse le fichier gmon.out pour afficher des informations sur le temps pass dans chaque fonction. Par exemple, examinons les donnes de profilage "brutes" pour le calcul de 1787 x 13 - 1918 en utilisant notre programme, elles sont fournies par la commande grpof ./calculator:
Flat profile: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 26.07 1.76 1.76 20795463 0.00 0.00 decrement_number 24.44 3.41 1.65 1787 0.92 1.72 add 19.85 4.75 1.34 62413059 0.00 0.00 zerop 15.11 5.77 1.02 1792 0.57 2.05 destroy_number 14.37 6.74 0.97 20795463 0.00 0.00 add_one 0.15 6.75 0.01 1788 0.01 0.01 copy_number 0.00 6.75 0.00 1792 0.00 0.00 make_zero 0.00 6.75 0.00 11 0.00 0.00 empty_stack

Le parcours de la fonction decrement_number et des sous fonctions qu'elle appelle occupe 26,07% du temps d'excution total du programme. Elle a t appel 20795463 fois. Chaque excution a ncessit 0,0 seconde -autrement dit, un temps trop faible pour tre mesur. La fonction add a t invoque 1787 fois, certainement pour calculer le produit. Chaque appel a demand 0.92 secondes. La fonction copy_number a t invoque 1788 fois, tandis que son excution n'a ncessit que 0,15% du temps total d'excution. Parfois, les fonctions mcount et profil utilises pour le profilage apparaissent dans les donnes. En plus des donnes brutes de profilage, qui indique le temps pass dans chacune des fonctions, gprof produit des graphes d'appel indiquant le temps pass dans chaque fonction et les fonctions qu'elle appelle dans le cadre d'une chane d'appels de fonctions:
index % time [1] 100.0 self 0.00 0.00 0.00 0.00 0.00 children 6.75 6.75 0.00 0.00 0.00 called 2/2 1/1792 1/1 3/3 name

<spontaneous> main [1] apply_binary_function [2] destroy_number [4] number_to_unsigned_int [10] string_to_number [12]

- 174 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) 0.00 0.00 3/5 push_stack [16] 0.00 0.00 1/1 create_stack [18] 0.00 0.00 1/11 empty_stack [14] 0.00 0.00 1/5 pop_stack [15] 0.00 0.00 1/1 clear_stack [17] ----------------------------------------------0.00 6.75 2/2 main [1] [2] 100.0 0.00 6.75 2 apply_binary_function [2] 0.00 6.74 1/1 product [3] 0.00 0.01 4/1792 destroy_number [4] 0.00 0.00 1/1 subtract [11] 0.00 0.00 4/11 empty_stack [14] 0.00 0.00 4/5 pop_stack [15] 0.00 0.00 2/5 push_stack [16] ----------------------------------------------0.00 6.74 1/1 apply_binary_function [2] [3] 99.8 0.00 6.74 1 product [3] 1.02 2.65 1787/1792 destroy_number [4] 1.65 1.43 1787/1787 add [5] 0.00 0.00 1788/62413059 zerop [7] 0.00 0.00 1/1792 make_zero [13]

Le premier cadre indique que l'excution de main a ncessit 100% des 6,75 secondes qu'a dur l'excution du programme. Elle a appel apply_binary_function deux fois, celle-ci ayant t appel deux fois pendant toute la dure d'excution du programme. L'appelant de main tait <spontaneous>, ce qui signifie que le profileur n'a pas t capable de le dterminer. Le premier cadre montre galement que string_to_number a appel push_stack trois fois sur les cinq fois o celle-ci a t appele. Le troisime cadre montre que l'excution de product et des fonctions qu'il appelle a ncessit 99,8% du temps d'excution total. Elle a t invoque une seule fois depuis apply_binary_function. Le graphe d'appel indique le temps total d'excution d'une fonction et de ses fils. Si le graphe d'appel est un arbre, ce temps est simple calculer, mais les fonction rcursives doivent tre traites d'une manire spciale. Par exemple, la fonction even appelle odd qui appelle even son tour. Chaque cycle d'appel de ce genre bnficie de son propre horodatage et est affich individuellement dans le graphe d'appel. Considrons ces donnes venant du profilage de la squence visant dterminer si 1787 x 13 x 3 est pair:
----------------------------------------------0.00 0.02 1/1 main [1] [9] 0.1 0.00 0.02 1 apply_unary_function [9] 0.01 0.00 1/1 even <cycle 1> [13] 0.00 0.00 1/1806 destroy_number [5] 0.00 0.00 1/13 empty_stack [17] 0.00 0.00 1/6 pop_stack [18] 0.00 0.00 1/6 push_stack [19] ----------------------------------------------[10] 0.1 0.01 0.00 1+69693 <cycle 1 as a whole> [10] 0.00 0.00 34847 even <cycle 1> [13] ----------------------------------------------34847 even <cycle 1> [13] [11] 0.1 0.01 0.00 34847 odd <cycle 1> [11] 0.00 0.00 34847/186997954 zerop [7] 0.00 0.00 1/1806 make_zero [16] 34846 even <cycle 1> [13]

La valeur 1+69693 dans le cadre [10] indique que le cycle 1 a t appel une fois, tandis qu'au sein de ce cycle il y a eu 69693 appels de fonction. Le cycle a appel la fonction even. L'entre suivante montre que la fonction odd a t appele 34847 par even. Dans cette section, nous avons brivement prsent une partie des fonctionalits de gprof. Les pages info donnent des informations sur d'autres fonctionalits utiles: L'option -s affiche la somme des rsultats pour plusieurs excutions conscutives; L'option -c permet d'identifier les fils qui auraient pu tre appels mais ne l'ont pas t; L'option -l pour afficher des informations de profilage ligne par ligne.

- 175 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

L'option -A pour afficher le code source annot avec les pourcentages de temps d'excution.

Les pages info donnent galement plus d'informations sur la faon d'interprter les rsultats de l'analyse.

11-3-4 - Comment gprof Collecte les Donnes


Lors du profilage d'un excutable, chaque fois qu'une fonction est appele, le compteur qui y est associ est incrment. De plus, gprof interrompt rgulirement l'excution pour dterminer la fonction en cours. Ces exemples montrent comment le temps d'excution est dtermin. Comme les interruptions d'horloge de Linux surviennent toutes les 0,01 secondes, l'arrt de l'excution ne peut avoir lieu qu'au maximum toutes les 0,01 secondes. Ainsi, le profilage de programmes s'excutant trs rapidement ou appelant peu souvent des fonctions qui s'excutent rapidement pourrait tre imprcis. Pour viter cela, excutez le programme plus longtemps ou additionnez les donnes de profilage de plusieurs excutions. Reportez-vous la documentation concernant l'option -s dans les pages info de groff pour plus d'informations.

11-3-5 - Code Source de la Calculatrice


Le Listing calculator prsente un programme qui calcule la valeur d'expressions postfixes. Programme Principal de la calculatrice calculator.c
/* Effectue des calculs en utilisant des nombres unaires. */ /* Saisissez des expressions utilisant la notation postfixe sur une ligne, par exemple : 602 7 5 - 3 * + Les nombres positifs sont saisis en utilisant une notation dcimal. Les oprateurs "+", "-" et "*" sont accepts. Les oprateurs unaires "even" et "odd" renvoient le nombre 1 si leur oprande est pair ou impair, respectivement. Les diffrents lments doivent tre spars par des espaces. Les nombres ngatifs ne sont pas pris en charge. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "definitions.h" /* Applique la fonction binaire demande aux oprandes obtenues depuis la pile et place le rsultat sur la pile. Renvoie une valeur diffrente de zro si tout se passe bien. */ int apply_binary_function (number (*function) (number, number), Stack* stack) { number operand1, operand2; if (empty_stack (*stack)) return 0; operand2 = pop_stack (stack); if (empty_stack (*stack)) return 0; operand1 = pop_stack (stack); push_stack (stack, (*function) (operand1, operand2)); destroy_number (operand1); destroy_number (operand2); return 1; } /* Applique la fonction unaire demande aux oprandes obtenues depuis la pile et place le rsultat sur la pile. Renvoie une valeur diffrente de zro si tout se passe bien. */ int apply_unary_function (number (*function) (number), Stack* stack) { number operand; if (empty_stack (*stack)) return 0; operand = pop_stack (stack); push_stack (stack, (*function) (operand)); destroy_number (operand);

- 176 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Programme Principal de la calculatrice calculator.c

return 1; } int main () { char command_line[1000]; char* command_to_parse; char* token; Stack number_stack = create_stack (); while (1) { printf ("Veuillez saisir une opration postfixe :\n"); command_to_parse = fgets (command_line, sizeof (command_line), stdin); if (command_to_parse == NULL) return 0; token = strtok (command_to_parse, " \t\n"); command_to_parse = 0; while (token != 0) { if (isdigit (token[0])) push_stack (&number_stack, string_to_number (token)); else if (((strcmp (token, "+") == 0) && !apply_binary_function (&add, &number_stack)) || ((strcmp (token, "-") == 0) && !apply_binary_function (&subtract, &number_stack)) || ((strcmp (token, "*") == 0) && !apply_binary_function (&product, &number_stack)) || ((strcmp (token, "even") == 0) && !apply_unary_function (&even, &number_stack)) || ((strcmp (token, "odd") == 0) && !apply_unary_function (&odd, &number_stack))) return 1; token = strtok (command_to_parse, " \t\n"); } if (empty_stack (number_stack)) return 1; else { number answer = pop_stack (&number_stack); printf ("%u\n", number_to_unsigned_int (answer)); destroy_number (answer); clear_stack (&number_stack); } } return 0; }

Les fonctions du Listing number implante les nombres unaires en utilisant des listes chanes vides. Implantation d'un Nombre Unaire number.c
/* Oprations sur les nombres unaires. */ #include <assert.h> #include <stdlib.h> #include <limits.h> #include "definitions.h" /* Cre un nombre reprsentant zro. */ number make_zero () { return 0; } /* Renvoie une valeur diffrente de zro si le nombre reprsente un zro. int zerop (number n) { return n == 0; } /* Soustrait 1 un nombre positif. */ number decrement_number (number n) { number answer; assert (!zerop (n)); answer = n->one_less_; free (n);

*/

- 177 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Implantation d'un Nombre Unaire number.c

return answer; } /* Ajoute 1 un nombre. */ number add_one (number n) { number answer = malloc (sizeof (struct LinkedListNumber)); answer->one_less_ = n; return answer; } /* Dtruit un nombre. */ void destroy_number (number n) { while (!zerop (n)) n = decrement_number (n); } /* Copie un nombre. Cette fonction n'est ncessaire qu' cause de l'allocation mmoire. */ number copy_number (number n) { number answer = make_zero (); while (!zerop (n)) { answer = add_one (answer); n = n->one_less_; } return answer; } /* Additionne deux nombres. */ number add (number n1, number n2) { number answer = copy_number (n2); number addend = n1; while (!zerop (addend)) { answer = add_one (answer); addend = addend->one_less_; } return answer; } /* Soustrait un nombre d'un autre. */ number subtract (number n1, number n2) { number answer = copy_number (n1); number subtrahend = n2; while (!zerop (subtrahend)) { assert (!zerop (answer)); answer = decrement_number (answer); subtrahend = subtrahend->one_less_; } return answer; } /* Renvoie le produit de deux nombres. */ number product (number n1, number n2) { number answer = make_zero (); number multiplicand = n1; while (!zerop (multiplicand)) { number answer2 = add (answer, n2); destroy_number (answer); answer = answer2; multiplicand = multiplicand->one_less_; } return answer; } /* Renvoie une valeur diffrente de zro si un nombre est pair. */ number even (number n) { if (zerop (n)) return add_one (make_zero ()); else return odd (n->one_less_); }

- 178 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Implantation d'un Nombre Unaire number.c

/* Renvoie une valeur diffrente de zro si un nombre est impair. */ number odd (number n) { if (zerop (n)) return make_zero (); else return even (n->one_less_); } /* Convertit une chane reprsentant un entier dcimal en un "number". */ number string_to_number (char * char_number) { number answer = make_zero (); int num = strtoul (char_number, (char **) 0, 0); while (num != 0) { answer = add_one (answer); --num; } return answer; } /* Convertit un "number" en "unsigned int". */ unsigned number_to_unsigned_int (number n) { unsigned answer = 0; while (!zerop (n)) { n = n->one_less_; ++answer; } return answer; }

La fonction du Listing stack implante une pile de nombre unaires en utilisant une liste chane. Pile de Nombres Unaires stack.c
/* Implante une pile de "number"s. */ #include <assert.h> #include <stdlib.h> #include "definitions.h" /* Cre une pile vide. */ Stack create_stack () { return 0; } /* Renvoie une valeur diffrente de zro si la pile est vide. int empty_stack (Stack stack) { return stack == 0; } /* Supprime le number situ au sommet d'une pile non vide. choue si la pile est vide. */ number pop_stack (Stack* stack) { number answer; Stack rest_of_stack; assert (!empty_stack (*stack)); answer = (*stack)->element_; rest_of_stack = (*stack)->next_; free (*stack); *stack = rest_of_stack; return answer; } /* Ajoute un number au dbut de la pile. */ void push_stack (Stack* stack, number n) { Stack new_stack = malloc (sizeof (struct StackElement)); new_stack->element_ = n; new_stack->next_ = *stack; *stack = new_stack;

*/

- 179 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Pile de Nombres Unaires stack.c

} /* Supprime tous les lments de la pile. void clear_stack (Stack* stack) { while (!empty_stack (*stack)) { number top = pop_stack (stack); destroy_number (top); } }

*/

Le Listing definitions contient les dclaration de la pile et des nombres. Fichier d'En-Tte pour number.c et stack.c definitions.h
#ifndef DEFINITIONS_H #define DEFINITIONS_H 1 /* Implante un number en utilisant une liste chane. */ struct LinkedListNumber { struct LinkedListNumber* one_less_; }; typedef struct LinkedListNumber* number; /* Implante une pile de numbers sous forme de liste chane. Utilise 0 pour reprsenter une pile vide. */ struct StackElement { number element_; struct StackElement* next_; }; typedef struct StackElement* Stack; /* Oprations sur les piles de numbers. */ Stack create_stack (); int empty_stack (Stack stack); number pop_stack (Stack* stack); void push_stack (Stack* stack, number n); void clear_stack (Stack* stack); /* Operations sur les numbers. */ number make_zero (); void destroy_number (number n); number add (number n1, number n2); number subtract (number n1, number n2); number product (number n1, number n2); number even (number n); number odd (number n); number string_to_number (char* char_number); unsigned number_to_unsigned_int (number n); #endif /* DEFINITIONS_H */

- 180 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

12 - E/S de Bas Niveau


Les programmeurs C sous GNU/Linux ont deux jeux de fonctions d'entres/sorties leur disposition. La bibliothque C standard fournit des fonctions d'E/S: printf, fopen, etc(La bibliothque C++ stndard fournit les flux d'E/S (iostreams) qui proposent des fonctionnalits similaires. La bibliothque C standard est galement disponible avec le langage C++.). Le noyau Linux fournit un autre ensemble d'oprations d'E/S qui oprent un niveau infrieur celui des fonctions de la bibliothque C. Comme ce livre est destin des gens connaissant dj le langage C, nous supposerons que vous avez dj rencontr et savez comment utiliser les fonctions d'E/S de la bibliothque C. Il y a souvent de bonnes raisons d'utiliser les fonctions d'E/S de bas niveau de Linux. Elles sont pour la plupart des appels systmes au noyau(Consultez le Chapitre appelssysteme, Chapitre appelssysteme pour des explications concernant la diffrence entre un appel systme et un appel de fonction traditionnelle.) et fournissent un accs direct aux possibilits sous-jacentes offertes par le systme aux applications. En fait, les fonctions d'E/S de la bibliothque C standard sont implantes par dessus les appels systmes d'E/S de bas niveau de Linux. L'utilisation de cette dernire est gnralement la faon la plus efficace d'effectuer des oprations d'entre/sortie -- et est parfois galement plus pratique. Tout au long de ce livre, nous supposons que vous tes familier avec les appels dcrits dans cette annexe. Vous tes peut tre dj familiers avec eux car ils sont trs proches de ceux fournis avec d'autres systme d'exploitation de type unique (ainsi qu'avec la plateforme Win32). Si vous n'tes pas coutumier de ces appels, cependant, continuez votre lecture; le reste du livre n'en sera que plus simple comprendre si vous commencez par prendre connaissance de ce chapitre.

12-1 - Lire et crire des Donnes


La premire fonction d'E/S que vous avez rencontr lorsque vous avez commenc apprendre le langage C tait certainement printf. Elle formate une chane de texte puis l'affiche sur la sortie standard. La version gnrique, fprintf, peut afficher le texte sur un autre flux que la sortie standard. Un flus est reprsent par un pointeur FILE*. Vous obtenez un tel pointeur en ouvrant un fichier avec fopen. Lorsque vous en avez fini, vous pouvez le fermer avec fclose. En plus de fprintf, vous pouvez utiliser d'autres fonctions comme fputc, fputs ou fwrite pour crire des donnes dans un flux, ou fscanf, fgetc, fgets ou fread pour lire des donnes. Avec les oprations d'E/S de bas niveau de Linux, vous utilisez descripteur de fichier au lieu d'un pointeur FILE*. Un descripteur de fichier est un entier qui fait rfrence une instance donne d'un fichier ouvert au sein d'un processus. Il peut tre ouvert en lecture, en criture ou en lecture/criture. Un descripteur de fichier ne fait pas forcment rfrence un fichier ouvert; il peut reprsenter une connexion vers un composant d'un autre systme qui est capable d'envoyer ou de recevoir des donnes. Par exemple, une connexion vers un dispositif matriel est reprsente par un descripteur de fichier (voir Chapitre peripheriques, Chapitre preripheriques), tout comme l'est un socket ouvert (voir Chapitre IPC, Chapitre IPC, Section sockets, Section sockets) ou l'extrmit d'un tube (voir Section tubes, Section tubes). Incluez les fichiers d'en-tte <fcntl.h>, <sys/types.h>, <sys/stat.h> et <unistd.h> si vous utilisez l'une des fonctions d'E/S de bas niveau dcrites ici.

12-1-1 - Ouvrir un Fichier


Pour ouvrir un fichier et obtenir un descripteur de fichier pouvant y accder, utilisez l'appel open. Il prend le chemin du fichier ouvrir sous forme d'une chane de caractres et des indicateurs spcifiant comment il doit l'tre. Vous pouvez utiliser open pour crer un nouveau fichier; pour cela, passez un troisime argument dcrivant les droits d'accs appliquer au nouveau fichier.

- 181 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si le second argument est O_RDONLY, le fichier est ouvert en lecture seul; un erreur sera signale si vous essayez d'y crire. De mme, O_WRONLY ouvre le descripteur de fichier en criture seule. Passer O_RDWR cre un descripteur de fichier pouvant tre utilis la fois en lecture et en criture. Notez que tous les fichiers ne peuvent pas tre ouverts dans les trois modes. Par exemple, les permissions d'un fichier peuvent interdire un processus de l'ouvrir en lecture ou en criture; un fichier sur un priphrique en lecture seule, comme un lecteur CD-ROM ne peut pas tre ouvert en lecture. Vous pouvez passer des options supplmentaires en utilisant un OU binaire de ces valeurs avec d'autres indicateurs. Voici les valeurs les plus courantes: Passez O_TRUNC pour tronquer le fichier ouvert, s'il existait auparavant. Les donnes crites remplaceront le contenu du fichier; Passez O_APPEND pour ajouter les donnes au contenu d'un fichier existant. Elles sont crites la fin du fichier; Passez O_CREAT pour crer un nouveau fichier. Si le nom de fichier que vous passez open correspond un fichier inexistant, un nouveau fichier sera cr, si tant est que le rpertoire le contenant existe et que le processus a les permissions ncessaires pour crer des fichiers dans ce rpertoire. Si le fichier existe dj, il est ouvert; Passez O_EXCL et O_CREATE pour forcer la cration d'un nouveau fichier. Si le fichier existe dj, l'appel open chouera.

Si vous appelez open en lui passant O_CREATE, fournissez un troisime argument indiquand les permissions applicable au nouveau fichier. Consultez le Chapitre securite, Chapitre securite, Section permissionsfs, Section permissionsfs, pour une description des bits de permission et de la faon de les utiliser. Par exemple, le programme du Listing createfile cre un nouveau fichier avec le nom de fichier indiqu sur la ligne de commande. Il utilise l'indicateur O_EXCL avec open afin de signaler une erreur si le fichier existe dj. Le nouveau fichier dispose des autorisations en lecture/criture pour l'utilisateur et le groupe propritaireset est en lecture seule pour les autres utilisateurs (si votre umask est positionn une valeur diffrente de zro, les permissions effectives pourraient tre plus restrictives). Lorsque vous crez un nouveau fichier avec open, certains des bits de permissions peuvent tre dsacrivs. Cela survient lorsque votre umask est diffrent de zro. L'umask d'un processus spcifie les bits de permissions qui sont masqus lors de n'importe quelle cration de fichier. Les permissions effectives sont obtenues en appliquant un ET binaire entre les permissions que vous passez open et le complment un du umask. Pour changer la valeur de votre umask partir d'un shell, utilisez la commande umask et indiquez la valeur numrique du masque en notation octale. Pour changer le umask d'un processus en cours d'excution, utilisez l'appel umask en lui passant la valeur du masque utiliser pour les appels suivants. Par exemple, cette ligne: umask (S_IRWXO S_IWGRP); dans un programme ou l'invocation de cette commande: % umask 027 indiquent que les permissions en criture pour le groupe et toutes les permissions pour les autres seront toujours masque pour les crations de fichiers. Create a New File create-file.c
#include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int main (int argc, char* argv[]) { /* Chemin vers le nouveau fichier. */ char* path = argv[1]; /* Permissions du nouveau fichier. */ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; /* Cre le fichier. */ int fd = open (path, O_WRONLY | O_EXCL | O_CREAT, mode); if (fd == -1) { - 182 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Create a New File create-file.c

} return 0;

/* Une erreur est survenue, affiche un message et quitte. perror ("open"); return 1;

*/

Voici le programme en action:


% ./create-file testfile % ls -l testfile -rw-rw-r-1 samuel users 0 Feb 1 22:47 testfile % ./create-file testfile open: Le fichier existe

Notez que le fichier fait zro octet car le programme n'y a crit aucune donne.

12-1-2 - Fermer un fichier


Lorsque vous en avez fini avec un descripteur de fichier, fermez-le avec close. Dans certains cas, comme dans le cas du programme du Listing createfile il n'est pas ncessaire d'appeler close explicitement car Linux ferme tous les descripteurs de fichiers lorsqu'un processus se termine (c'est--dire la fin du programme). Bien sr, une fois que vous avez ferm un descripteur de fichier, vous ne pouvez plus l'utiliser. La fermeture d'un descripteur de fichier peut dclencher des actions spcifiques de la part de Linux, selon la nature du descripteur de fichier. Par exemple, lorsque vous fermez un descripteur correspondant un socket rseau, Linux ferme la connexion entre les deux ordinateurs communicant via le socket. Linux limite le nombre de descripteurs de fichiers qu'un processus peut maintenir ouverts en mme temps. Les descripteurs de fichiers ouverts utilisent des ressources noyau, il est donc conseill de fermer les descripteurs de fichiers ds que vous avez terminer de les utiliser. La limite classique est de 1024 descripteurs par processus. Vous pouvez l'ajuster avec l'appel systme setrlimit; consultez la Section getsetrlimit, Section getsetrlimit, pour plus d'informations.

12-1-3 - crire des donnes


crire des donnes dans un fichier se fait par le biais de l'appel write. Passz lui un descripteur de fichier, un pointeur vers un tampon de donnes et le nombre d'octets crire. Les donnes crites n'ont pas besoin d'tre une chaine de caractres; write copie des octets quelconques depuis le tampon vers le descripteur de fichier. Le programme du Listing timestamp crit l'heure courante la fin du fichier pass sur la ligne de commande. Si le fichier n'existe pas, il est cr. Ce programme utilise galement les fonctions time, localtime et asctime pour obtenir et formater l'heure courante; consultez leurs pages de manuel respectives pour plus d'informations. Ajoute l'Heure Courante un Fichier timestamp.c
#include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.h> /* Renvoie une chaine reprsentant l'heure courante. */ char* get_timestamp () { time_t now = time (NULL);

- 183 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Ajoute l'Heure Courante un Fichier timestamp.c

return asctime (localtime (&now)); } int main (int argc, char* argv[]) { /* Fichier auquel ajouter l'horodatage. */ char* filename = argv[1]; /* Rcupre l'heure courante. */ char* timestamp = get_timestamp (); /* Ouvre le fichier en criture. S'il existe, ajoute les donnes la fin, sinon, un nouveau fichier est cr. */ int fd = open (filename, O_WRONLY | O_CREAT | O_APPEND, 0666); /* Calcule la longueur de la chane d'horodatage. */ size_t length = strlen (timestamp); /* L'crit dans le fichier. */ write (fd, timestamp, length); /* Fini. */ close (fd); return 0; }

Voici comment fonctionne ce programme:


% ./timestamp tsfile % cat tsfile Thu Feb 1 23:25:20 2001 % ./timestamp tsfile % cat tsfile Thu Feb 1 23:25:20 2001 Thu Feb 1 23:25:47 2001

Notez que la premire fois que nous invoquons timestamp, il cre le fichier tsfile, alors que la seconde fois, les donnes sont ajoutes la fin. L'appel write renvoie le nombre d'octets effectivement crits ou -1 si une erreur survient. Pour certains types de descripteurs de fichiers, le nombre d'octets crits peut tre infrieur au nombre d'octets demands. Dans ce cas, c'est vous d'appeler write encore une fois pour crire le reste des donnes. La fonction du Listing writeall montre une faon de le faire. Notez que pour certaines applications, vous pourriez avoir effectuer des contrles supplmentaires avant la reprise de l'criture. Par exemple, si vous utilisez un socket rseau, il faudrait ajouter le code permettant de dtecter si la connexion a t ferme pendant l'criture et, si c'est le cas, prendre les mesures adquates. crit tout le Contenu d'un Tampon write-all.c
/* crit l'intgralit des COUNT octets de BUFFER vers le descripteur de fichier FD. Renvoie -1 en cas d'erreur ou le nombre d'octets crits. */ ssize_t write_all (int fd, const void* buffer, size_t count) { size_t left_to_write = count; while (left_to_write > 0) { size_t written = write (fd, buffer, count); if (written == -1) /* Une erreur est survenue. Termin. */ return -1; else /* Mmorise le nombre d'octets restant crire. */ left_to_write -= written; } /* Nous ne devons pas avoir crit plus de COUNT octets ! */ assert (left_to_write == 0); /* Le nombre d'octets crits est exactement COUNT. */ return count; }

- 184 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

12-1-4 - Lecture de Donnes


L'appel permettant de lire des donnes est read. Tout comme write, il prend en arguments un descripteur de fichier, un pointeur vers un tampon et un nombre d'octets. Ce dernier indique le nombre d'octets lire partir du descripteur. L'appel read renvoie -1 en cas d'erreur ou le nombre d'octets effectivement lus. Il peut tre infrieur au nombre d'octets demand, par exemple, s'il ne reste pas suffisamment d'octets lire dans le fichier. Une fois que vous aurez lu ce livre, nous sommes convaincus que vous dciderez d'crire tous vos programmes pour GNU/Linux. Cependant, il pourrait vous arriver d'avoir lire des fichiers texte gnrs par des programmes DOS ou Windows. Il est important d'anticiper une diffrence majeure entre les deux plateformes dans la faon de structurer les fichiers texte. Dans les fichiers texte GNU/Linux, chaque ligne est spare de la suivante par un caractre de nouvelle ligne. Ce dernier est reprsent par la constante de caractre 'n' dont le code ASCII est 10. Sou Windows, par contre, les lignes sont spares par une squence de deux caractres: un retour chariot (le caractre 'r', dont le code ASCII est 13), suivi d'un caractre de nouvelle ligne. Certains diteurs de texte GNU/Linux affichent un ^M la fin de chaque ligne lorsqu'ils affichent le contenu d'un fichier texte Windows -- il s'agit du caractre de retour chariot. Emacs affiche les fichiers texte Windows correctement mais les signale en affichant (DOS) dans la barre de mode en bas du tampon. Certains diteurs Windows, comme Notepad, affichent tout le texte des fichiers GNU/Linux sur une seule ligne car ils ne trouvent pas le caractre de retour chariot la fin de chaque ligne. D'autres programmes, que ce soit sous GNU/Linux ou Windows, peuvent signaler des erreurs tranges lorsque les fichiers qui leur sont fournis en entre ne sont pas au bon format. Si votre programme lit des fichiers texte gnrs par des programmes Windows, vous voudrez probablement remplacer la squence 'rn' par un seul caractre de nouvelle ligne. De mme, si votre programme crit des fichiers texte qui doivent tre lus par des programmes Windows, remplacez le caractre de nouvelle ligne par la squence 'rn'. Vous devrez le faire, que vous utilisiez les appels d'E/S de bas niveau prsents dans cette annexe ou les fonctions de la bibliothque C standard. Le Listing hexdump prsente un programme utilisant l'appel read. Il affiche une image hexadcimale du contenu du fichier pass sur la ligne de commande. Chaque ligne affiche le dplacement dans le fichier et les 16 octets suivants. Affiche l'Image Hexadcimale d'un Fichier hexdump.c
#include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int main (int argc, char* argv[]) { unsigned char buffer[16]; size_t offset = 0; size_t bytes_read; int i; /* Ouvre le fichier en lecture. */ int fd = open (argv[1], O_RDONLY); /* Lit le fichier morceau par morceau, jusqu' ce que la lecture soit "trop courte", c'est--dire que l'on lise moins que ce que l'on a demand, ce qui indique que la fin du fichier est atteinte. */ do { /* Lis la "ligne" suivante. */ bytes_read = read (fd, buffer, sizeof (buffer)); /* Affiche le dplacement dans le fichier, suivi des octets correspondants. */ printf ("0x%06x : ", offset); for (i = 0; i < bytes_read; ++i) printf ("%02x ", buffer[i]); printf ("\n"); /* Conserve notre position dans le fichier. */ offset += bytes_read; } - 185 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Affiche l'Image Hexadcimale d'un Fichier hexdump.c


while (bytes_read == sizeof (buffer)); /* Termin. */ close (fd); return 0;

Voici hexdump en action. Il affiche l'image de son propre excutable:


% ./hexdump hexdump 0x000000 : 7f 45 4c 0x000010 : 02 00 03 0x000020 : e8 23 00 0x000030 : 1d 00 1a ...

46 00 00 00

01 01 00 06

01 00 00 00

01 00 00 00

00 00 00 00

00 c0 34 34

00 83 00 00

00 04 20 00

00 08 00 00

00 34 06 34

00 00 00 80

00 00 28 04

00 00 00 08

La sortie peut tre diffrente chez vous, selon le compilateur utilis pour construire hexdump est les options de compilation.

12-1-5 - Se Dplacer dans un Fichier


Un descripteur de fichier connait sa position dans le fichier. Lorsque vous y crivez ou que vous y lisez, sa position est modifie selon le nombre d'octets lus ou crits. Parfois, cependant, vous pouvez avoir besoin de vous dplacer dans un fichier sans lire ou crire de donnes. Par exemple, vous pouvez vouloir crire au milieu d'un fichier sans en modifier le dbut, ou vous pouvez avoir besoin de retourner au dbut d'un fichier et de le relire sans avoir le rouvrir. L'appel lseek vous permet de modifier votre position dans un fichier. Passez lui le descripteur deux fichier et deux autres arguments indiquand la nouvelle position. Si le troisime argument est SEEK_SET, lseek interprte le second argument comme une position, en octets, depusi le dbut du fichier; Si le troisime argument est SEEK_CUR, lseek interprte le second argument comme un dplacement, positif ou ngatif, depuis la position courante; Si le troisime argument est SEEK_END, lseek interprte le second argument comme un dplacement partir de la fin du fichier. Une valeur positive indique une position au-del de la fin du fichier.

L'appel lseek renvoie la nouvelle position, sous forme d'un dplacement par rapport au dbut du fichier.Le type de ce dplacement est off_t. Si une erreur survient, lseek renvoie -1. Vous ne pouvez pas utiliser lseek avec certains types de descripteurs de fichiers comme les sockets. Si vous voulez obtenir votre position dans un fichier sans la modifier, indiquez un dplacement de 0 par rapport votre position actuelle -- par exemple:
off_t position = lseek (file_descriptor, 0, SEEK_CUR);

Linux autorise l'utilisation de lseek pour spcifier une position au-del de la fin du fichier. Normalement, si un descripteur de fichier est positionn la fin d'un fichier et que vous y crivez, Linux augmente automatiquement la taille du fichier pour faire de la place pour les nouvelles donnes. Si vous indiquez une position au-del de la fin du fichier puis que vous y crivez, Linux commence par agrandir le fichier de la taille du "trou" que vous avez cr en appelant lseek puis crit la fin de celui-ci. Ce trou n'occupe toutefois pas de place sur le disque; Linux note simplement sa taille. Si vous essayez de lire le fichier, il apparait comme s'il tait rempli d'octets nuls. En utilisant cette proprit de lseek, il est possible de crer des fichier extrmement grands qui n'occupent pratiquement aucun espace disque. Le programme lseek-huge du Listing lseekhuge le fait. Il prend en arguments de ligne de commande un nom de fichier et sa taille, en mgaoctets. Le programme ouvre un nouveau fichier, se place aprs la fin de celui-ci en utilisant lseek puis crit un octet 0 avant de fermer le fichier.
- 186 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Crer de Gros Fichiers avec lseek lseek-huge.c


#include <fcntl.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int main (int argc, char* argv[]) { int zero = 0; const int megabyte = 1024 * 1024; char* filename = argv[1]; size_t length = (size_t) atoi (argv[2]) * megabyte; /* Ouvre un nouveau fichier. */ int fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, 0666); /* Se place un octet avant la fin dsire du fichier. */ lseek (fd, length - 1, SEEK_SET); /* crit un octet nul. */ write (fd, &zero, 1); /* Termin. */ close (fd); return 0; }

Voici comment crer un fichier d'un gigaoctet (1024 Mo) en utilisant lseek-huge. Notez l'espace libre avant et aprs l'opration.
% df -h . Filesystem Tail. Occ. Disp. %Occ. Mont sur /dev/hda5 2.9G 2.1G 655M 76% / % ./lseek-huge grosfichier 1024 % ls -l grosfichier -rw-r----1 samuel samuel 1073741824 Feb 5 16:29 grosfichier % df -h . Filesystem Tail. Occ. Disp. %Occ. Mont sur /dev/hda5 2.9G 2.1G 655M 76% /

Aucun espace disque significatif n'est utilis en dpit de la taille consquente de grosfichier. Si nous ouvrons grosfichier et que nous y lisons des connes, il apparait comme tant rempli de 1 Go d'octets nuls. Par exemple, nous pouvons inspecter son contenu avec le programme hexdump du Listing hexdump.
% ./hexdump grosfichier 0x000000 : 00 00 00 00 0x000010 : 00 00 00 00 0x000020 : 00 00 00 00 0x000030 : 00 00 00 00 0x000040 : 00 00 00 00 0x000050 : 00 00 00 00 ... | head -10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

00 00 00 00 00 00

Si vous excutez cette commande vous-mme, vous devrez probabelement la tuer avec Ctrl+C plutt de la regarder affichier 2<sup>30</sup> octets nuls. Notez que ces trous magiques dans les fichiers sont une fonctionalit spciale du systme de fichier ext2 qui est traditionnelement utilis pour les disques GNU/Linux. Si vous utilisez lseek-huge pour crer un fichier sur un autre type de systme de fichiers, comme fat ou vfat qui sont utiliss sur certaines partitions DOS et Windows, vous constaterez que le fichier occupe effectivement autant d'espace que sa taille. Linux n'autorise pas le positionnement avant le dbut du fichier avec lseek.

- 187 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

12-2 - stat
En utilisant open et read, vous pouvez extraire le contenu d'un fichier. Mais qu'en est-il des autres informations? Par exemple, la commande ls -l affiche des informations comme la taille, la date de dernire modification, les permissions ou le propritaire pour les fichiers du rpertoire courant. L'appel stat rcupre ces informations pour un fichier. Appelez stat en lui passant le chemin du fichier sur lequel vous voulez des informations et un pointeur sur une variable de type struct stat. Si l'appel stat se droule correctement, il revoie 0 et renseigne les champs de la structure avec des informations sur le fichier; sinon, il renvoie -1. Voici les champs les plus intressant d'une struct stat: st_mode contient les permissions du fichier. Ces permissions sont dcrites dans la Section permissionsfs, Section permissionsfs; En plus des permissions clasiques, le champ st_mode encode le type de fichier dans les bits de poids fort. Consultez les explication ci-dessous pour savoir comment le dcoder; st_uid et st_gid contiennent les identifiants de l'utilisateur et du groupe auxquels le fichier appartient, respectivement. Les identifiants de groupe et d'utilisateur sont dtaills dans la Section utilisateursgroupes, Section utilisateursgroupes; st_size contient la taille du fichier, en octets; st_atime contient la date de dernier accs au fichier (en lecture ou criture); st_mtime contient la date de dernire modification du fichier.

Un certain nombre de macros analysent la valeur du champ st_mode pour dterminer le type de fichier sur lequel stat a t invoqu. Une macro vaut vrai si le fichier est de ce type: $_ISBLK (mode) $_ISCHR (mode) $_ISDIR (mode) $_ISFIFO (mode) $_ISLNK (mode) $_ISREG (mode) $_ISSOCK (mode) priphrique bloc priphrique caractre rpertoire fifo (canal nomm) lien symbolique fichier classique socket

Le champ st_dev contient les numros de priphrique majeur et mineur du dispositif matriel sur lequel le fichier se situe. Les numros de priphriques sont traits dans le Chapitre peripheriques. Le numro majeur de priphrique est dcal gauche de 8 bits, tandis que le numro mineur occupe les 8 bits de poids faible. Le champ st_ino contient le numro d'inode du fichier. Cela situe le fichier sur le systme de fichiers. Si vous appelez stat sur un lien symbolique, stat suit le lien et vous renvoie les informations sur le fichier sur lequel il pointe, pas sur le lien symbolique. Cela signifie que S_ISLNK ne sera jamais vrai pour une valeur renvoye par stat. Utilisez la fonction lstat si vous ne voulez pas suivre les liens symboliques; cette fonction renvoie les informations sur le lien et non pas sur le fichier cible. Si vous appelez lstat sur un fichier qui n'est pas un lien symbolique, elle a le mme comportement que stat. Appeler stat sur un lien invalide (un lien qui pointe vers un fichier inexistant ou inaccessible) provoque une erreur, alors que l'appel de lstat sur le mme fichier n'a aucune incidence. Si vous disposez dj d'un fichier ouvert en lecture/criture, appelez fstat au lieu de stat. Il prend un descripteur de fichier en premier argument la place du chemin vers le fichier. Le Listing readfile prsente une fonction qui alloue un tampon suffisamment long pour recevoir le contenu du fichier et charge les donnes l'intrieur. Elle utilise fstat pour dterminer la taille du tampon allouer et vrifier que le fichier est bien un fichier classique. Charge un Fichier dans un Tampon read-file.c

- 188 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Charge un Fichier dans un Tampon read-file.c

#include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /* Charge le contenu de FILENAME dans un tampon nouvellement allou. La taille du tampon est stocke dans *LENGTH. Renvoie le tampon, qui devra tre libr par l'appelant. Renvoie NULL si FILENAME ne correspond pas un fichier rgulier. */ char* read_file (const char* filename, size_t* length) { int fd; struct stat file_info; char* buffer; /* Ouvre le fichier. */ fd = open (filename, O_RDONLY); /* Rcupre les informations sur le fichier. */ fstat (fd, &file_info); *length = file_info.st_size; /* S'assure que le fichier est un fichier ordinaire. */ if (!S_ISREG (file_info.st_mode)) { /* Ce n'en est pas un, abandonne. */ close (fd); return NULL; } /* Alloue un tampon suffisamment grand pour recevoir le contenu du fichier. */ buffer = (char*) malloc (*length); /* Charge le fichier dans le tampon. */ read (fd, buffer, *length); /* Termin. */ close (fd); return buffer; }

12-3 - criture et Lecture Vectorielles


L'appel write prend en arguments un pointeur vers le dbut d'un tampon de donnes et la taille de ce tampon. Il crit le contenu d'une rgion contige de mmoire vers un descripteur de fichier. Cependant, un programme a souvent besoin d'crir plusieurs lments de donnes, chacun se trouvant un endroit diffrent. Pour utiliser write, un tel programme devrait soit copier tous les objets dans une rgion contige, ce qui gaspillerait des cycles CPU et de la mmoire, soit effectuer de multiples appels write. Pour certaines applications, appeler write plusieurs fois peut tre inefficace ou peu souhaitable. Par exemple, lors de l'criture vers un socket rseau, deux appels write peut provoquer l'envoi de deux paquets sur le rseau, alors que les mmes donnes auraient pu tre envoyes en une fois si un seul appel write avait t possible. L'appel writev vous permet d'envoyer le contenu de plusieurs zones mmoire non-contiges vers un descripteur de fichier en une seul opration. C'est ce que l'on appelle l'criture vectorielle. La contrepartie de l'utilisation de writev est que vous devez crer une structure de donnes indiquand le dbut et la taille de chaque rgion mmoire. Cette structure est un tableau d'lments struct iovec. Chaque lment correspond un emplacement mmoire crire; les champs iov_base et iov_len correspondent respectivement l'adresse du dbut de la rgion et sa taille. Si vous connaissez l'avance le nombre de zones mmoire crire, vous pouvez vous contenter de dclarer un tableau de struct iovec; si le nombre de rgions peut varier, vous devez allouer le tableau dynamiquement. Appelez writev en lui passant le descripteur de fichier vers lequel envoyer les donnes, le tableau de struct iovec et le nombre d'lments contenus dans ce tableau. La valeur de retour correspond au nombre d'octets crits. Le programme du Listing writeargs crit ses arguments de ligne de commande dans un fichier en utilisant un unique appel writev. Le premier argument est le nom du fichier dans lequel crire les arguments qui suivent. Le programme alloue un tableau de struct iovec qui fait le double du nombre d'arguments crire -- pour chaque argument, le texte de l'argument proprement dit est crit, suivit d'un caractre de nouvelle ligne. Comme nous ne savons pas l'avance le nombre d'arguments, le tableau est cr en utilisant malloc.
- 189 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

crit la Liste d'Arguments dans un Fichier avec writev write-args.c


#include <fcntl.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> int main (int argc, char* argv[]) { int fd; struct iovec* vec; struct iovec* vec_next; int i; /* Nous aurons besoin d'un "tampon" contenant un caractre de nouvelle ligne. Nous utilisons une variable char normale pour cela. */ char newline = '\n'; /* Le premier argument est le nom du fichier de sortie. */ char* filename = argv[1]; /* Ignore les deux premiers lments de la liste d'arguments. L'lment l'indice 0 est le nom du programme et celui l'indice 1 est le nom du fichier de sortie. */ argc -= 2; argv += 2; /* Alloue un tableau d'lments iovec. Nous aurons besoin de deux lements pour chaque argument, un pour le texte proprement dit et un pour la nouvelle ligne. */ vec = (struct iovec*) malloc (2 * argc * sizeof (struct iovec)); /* Boucle sur la liste d'arguments afin de construire les lments iovec. */ vec_next = vec; for (i = 0; i < argc; ++i) { /* Le premier lment est le texte de l'argument. */ vec_next->iov_base = argv[i]; vec_next->iov_len = strlen (argv[i]); ++vec_next; /* Le second lement est un caractre de nouvelle ligne. Il est possible de faire pointer plusieurs lments du tableau de struct iovec vers la mme rgion mmoire. */ vec_next->iov_base = &newline; vec_next->iov_len = 1; ++vec_next; } /* crit les arguments dans le fichier. */ fd = open (filename, O_WRONLY | O_CREAT); writev (fd, vec, 2 * argc); close (fd); free (vec); return 0;

Voici un exemple d'excution de write-args.


% ./write-args fichiersortie "premier arg" "deuxime arg" "troisme arg" % cat outputfile premier arg deuxime arg troisime arg

Linux propose une fonction quivalente pour la lecture, readv qui permet de charger des donnes dans des zones mmoire non-contiges en une seule fois. Comme pour writev, un tableau d'lments struct iovec indique les zones mmoire dans lesquelles charger les donnes partir du descripteur de fichier.

- 190 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

12-4 - Lien avec les Functions d'E/S Standards du C


Nous avons voqu le fait que les fonctions d'E/S standards du C sont implmentes comme une surcouche de ces fonctions d'E/S de bas niveau. Parfois, cependant, il peut tre pratique d'utiliser les fonctions de la bibliothque standard avec des descripteurs de fichiers ou les fonctions de bas niveau sur un flux FILE*. GNU/Linux autorise ces deux pratiques. Si vous avez ouvert un fichier en utilisant fopen, vous pouvez obtenir le descripteur de fichier sous-jacent par le biais de la fonction fileno. Elle prend un paramtre FILE* et renvoie le descripteur de fichier. Par exemple, pour ouvrir un fichier avec l'appel standard fopen mais y crire avec writev, vous pourriez utiliser une squencence telle que:
FILE* flux = fopen (nomfichier, "w"); int descripteur = fileno (flux); writev (descripteur, tableau, taille_tableau);

Notez que flux et descripteur correspondent tous deux au mme fichier. Si vous appelez fclose comme ceci, vous ne pourrez plus crire dans descripteur:
fclose (flux);

De mme, si vous appelez close, vous ne pourrez plus crire dans flux:
close (descripteur);

Pour effectuer l'opration inverse, obtenir un flux partir d'un descripteur, utilisez la fonction fdopen. Elle produit un pointeur FILE* correspondant un descripteur de fichier. La fonction fdopen prend en paramtres un descripteur de fichier et une chane correspondant au mode dans lequel le flux doit tre ouvert. La syntaxe de l'argument de mode est la mme que pour le second argument de fopen et il doit tre compatbile avec le descripteur de fichier. Par exemple, passez la chaine de mode r pour un descripteur de fichier en lecture ou w pour un descripteur de fichier en criture. Comme pour fileno, le flux et le descripteur de fichier font rfrence au mme fichier, ainsi, si vous en fermez l'un des deux, vous ne pouvez plus utiliser l'autre.

12-5 - Autres Oprations sur les Fichiers


Un petit nombre d'oprations supplmentaires sur les fichiers et rpertoires peut s'avrer utile: getcwd renvoie le rpertoire de travail courant. Il prend deux argument, un tampon de char et sa longueur. Il copie le chemin du rpertoire de travail courant dans le tampon; chdir change le rpertoire de travail courant pour qu'il corresponde au chemin pass en paramtre; mkdir cre un nouveau rpertoire. Son premier argument est le chemin de celui-ci, le second les permissions y appliquer. L'interprtation des permissions est la mme que celle du troisime argument de open, elles sont affectes par l'umask du processus; rmdir supprime le rpertoire dont le chemin est pass en paramtre; unlink supprime le fichier dont le chemin est pass en paramtre. Cet appel peut galement tre utilis pour supprimer d'autres objets du systme de fichiers, comme les canaux nomms (rfrez-vous la Section FIFO, Section FIFO) ou les priphriques (voir le Chapitre peripheriques); En fait, unlink ne supprime pas forcment le contenu du fichier. Comme son nom l'indique, il rompt le lien entre le fichier et le rpertoire qui le contient. Le fichier n'apparait plus dans le listing du rpertoire mais si un processus dtient un descripteur de fichier ouvert sur ce fichier, le contenu n'est pas effac du disque. Cela n'arrive que lorsque qu'aucun processus ne dtient de descripteur de fichier ouvert. Ainsi, si un priocessus ouvre un fichier pour y crire ou y lire et qu'un second processus supprime le fichier avec unlink et cre un nouveau fichier avec le mme nom, le premier processus "voit" l'ancien contenu du fichier et non pas le nouveau ( moins qu'il ne ferme le fichier et le r-ouvre);

- 191 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

rename renomme ou dplace un fichier. Le premier argument correspond au chemin courant vers le fichier, le second au nouveau chemin. Si les deux chemins sont dans des rpertoires diffrents, rename dplace le fichier, si tant est que le nouveau chemin est sur le mme systme de fichiers que l'ancien. Vous pouvez utiliser rename pour dplacer des rpertoires ou d'autres objets du systme de fichiers.

12-6 - Lire le Contenu d'un Rpertoire


GNU/Linux dispose de fonctions pour lire le contenu des rpertoires. Bien qu'elles ne soient pas directement lies aux fonctions de bas niveau dcrites dans cet appendice, nous les prsentons car elles sont souvent utiles. Pour lire le contenu d'un rpertoire, les tapes suivantes sont ncessaires: - Appelez opendir en lui passant le chemin du rpertoire que vous souhaitez explorer. opendir renvoie un descripteur DIR*, dont vous aurez besoin pour accder au contenu du rpertoire. Si une erreur survient, l'appel renvoie NULL; - Appelez readdir en lui passant le descripteur DIR* que vous a renvoy opendir. chaque appel, readdir renvoie un pointeur vers une instance de struct dirent correspondant l'entre suivante dans le rpertoire. Lorsque vous atteignez la fin du contenu du rpertoire, readdir renvoie NULL. La struct dirent que vous optenez via readdir dispose d'un champ d_name qui contient le nom de l'entre. - Appelez closedir en lui passant le descripteur DIR* la fin du parcours. Incluez <sys/types.h> et <dirent.h> si vous utilisez ces fonctions dans votre programme. Notez que si vous avez besoin de trier les entres dans un ordre particulier, c'est vous de le faire. Le programme du Listing listdir affiche le contenu d'un rpertoire. Celui-ci peut tre spcifi sur la ligne de commande mais, si ce n'est pas le cas, le rpertoire courant est utilis. Pour chaque entre, son type et son chemin est affich. La fonction get_file_type utilise lstat pour dterminer le type d'une entre. Affiche le Contenu d'un Rpertoire listdir.c
#include <assert.h> #include <dirent.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /* Renvoie une chaine qui dcrit le type du fichier PATH. */ const char* get_file_type (const char* path) { struct stat st; lstat (path, &st); if (S_ISLNK (st.st_mode)) return "lien symbolique"; else if (S_ISDIR (st.st_mode)) return "rpertoire"; else if (S_ISCHR (st.st_mode)) return "priphrique caractre"; else if (S_ISBLK (st.st_mode)) return "priphrique bloc"; else if (S_ISFIFO (st.st_mode)) return "fifo"; else if (S_ISSOCK (st.st_mode)) return "socket"; else if (S_ISREG (st.st_mode)) return "fichier ordinaire"; else /* Impossible. Toute entre doit tre de l'un des type ci-dessus. */ assert (0); - 192 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Affiche le Contenu d'un Rpertoire listdir.c

} int main (int argc, char* argv[]) { char* dir_path; DIR* dir; struct dirent* entry; char entry_path[PATH_MAX + 1]; size_t path_len; if (argc >= 2) /* Utilise le rpertoire spcifi sur la ligne de commande s'il y a lieu. */ dir_path = argv[1]; else /* Sinon, utilise le rpertoire courant. */ dir_path = "."; /* Copie le chemin du rpertoire dans entry_path. */ strncpy (entry_path, dir_path, sizeof (entry_path)); path_len = strlen (dir_path); /* Si le rpertoire ne se termine pas par un slash, l'ajoute. */ if (entry_path[path_len - 1] != '/') { entry_path[path_len] = '/'; entry_path[path_len + 1] = '\0'; ++path_len; } /* Dmarre l'affichage du contenu du rpertoire. */ dir = opendir (dir_path); /* Boucle sur les entres du rpertoire. */ while ((entry = readdir (dir)) != NULL) { const char* type; /* Construit le chemin complet en concatnant le chemin du rpertoire et le nom de l'entre. */ strncpy (entry_path + path_len, entry->d_name, sizeof (entry_path) - path_len); /* Dtermine le type de l'entre. */ type = get_file_type (entry_path); /* Affiche le type et l'emplacement de l'entre. */ printf ("%-18s: %s\n", type, entry_path); } /* Fini. */ closedir (dir); return 0; }

Voici les premires lignes affiches lors de l'affichage du contenu de /dev (elles peuvent tre diffrentes chez vous):
% ./listdir /dev directory directory socket character device regular file fifo character device ...

: : : : : : :

/dev/. /dev/.. /dev/log /dev/null /dev/MAKEDEV /dev/initctl /dev/agpgart

Pour vrifier les rsultats, vous pouvez utiliser la commande ls sur le mme rpertoire. Passez l'indicateur -U pour demander ls de ne pas trier les entres et passez l'indicateur -a pour inclure le rpertoire courant (.) et le rpertoire parent (..).
% ls -lUa /dev total 124 drwxr-xr-x 7 drwxr-xr-x 22 srw-rw-rw1 crw-rw-rw1 -rwxr-xr-x 1 prw------1

root root root root root root

root root root root root root

36864 4096 0 1, 3 26689 0

Feb Oct Dec May Mar Dec

1 15:14 . 11 16:39 .. 18 01:31 log 5 1998 null 2 2000 MAKEDEV 11 18:37 initctl

- 193 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog) crw-rw-r-... 1 root root 10, 175 Feb 3 2000 agpgart

Le premier caractre de chaque ligne affiche par ls indique le type de l'entre.

- 194 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

13 - Tableau des Signaux


Le Tableau signaux prsente une partie des signaux Linux que vous avez le plus de chances de rencontrer. Notez que certains signaux peuvent tre interprts de diffrentes faons selon l'endroit o ils surviennent. Les noms des signaux prsents ici sont dfinis sous forme de macros prprocesseur. Pour les utiliser dans votre programme utilisez <signal.h>. Les dfinitions proprement dites se trouvent dans le fichier /usr/include/sys/signum.h, qui est lui-mme inclus par <signal.h>. Pour une liste complte des signaux Linux, accompagns d'une courte description et du comportement associ par dfaut leur rception, consultez la page de manuel de signal de la Section 7 par le biais de la commande suivante:
% man 7 signal

Nom SIGHUP

SIGINT SIGILL

SIGABRT SIGFPE

SIGKILL SIGUSR1 SIGUSR2 SIGSEGV

SIGPIPE SIGALRM

Description Linux envoie ce signal un processus lorsqu?il est dconnect d?un terminal. La plupart des programmes Linux utilisent SIGHUP pour tout autre chose: indiquer un programme en cours d?excution qu?il doit recharger ses fichiers de configuration. Ce signal est envoy lorsque l?utilisateur tente d?arrter le programme en utilisant Ctrl+C. Reu par un processus lorsqu? il tente d?excuter une instruction illgale, cela peut indiquer une corruption de la pile du programme. Reu lors d?un appel abort. Reu lorsque le processus excute une instruction en virgule flottante invalide. Selon la manire dont le CPU est configur, une opration en virgule flottante peut renvoyer une valeur non-numrique spciale comme inf (infini) ou NaN (not a number) au lieu de lever un SIGFPE Ce signal termine un processus immdiatement et ne peut pas tre intercept. Rserv l?usage par l?application. Rserv l?usage par l?application. Le programme a effectu un accs invalide la mmoire. Il peut s?agir d?un accs une adresse invalide dans l?espace d? adressage du processus ou l?accs peut tre interdit par les permissions appliques la mmoire. Librer un ?pointeur sauvage? peut provoquer un SIGSEGV. Le programme a tent d?accder un flux de donnes invalide, comme une connexion qui a t ferme par l?autre protagoniste. L?appel systme alarm programme l? envoi de ce signal. Consultez la Section setitimer dans le Chapitre Appels

- 195 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

SIGTERM SIGCHLD

SIGXCPU

SIGVTALRM

systeme, pour plus d?informations sur setitimer, une version gnrique de alarm. Ce signal demande un processus de se terminer. Il s?agit du signal envoy par dfaut par la commande kill. Linux envoie ce signal un processus lorsqu?un processus fils se termine. Consultez la Section Liberer async du Chapitre processus. Linux envoie ce signal un processus lorsqu?il dpasse la limite de temps CPU qui lui a t alloue. Consultez la Section Getsetrlimit, du Chapitre Appels systeme pour plus d? informations sur les limites de temps CPU. La fonction setitimer programme l?envoi de ce signal. Consultez la Section setitimer.

- 196 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

14 - Ressources en Ligne
Cette annexe dresse une liste de ressources prsentes sur Internet permettant d'en savoir plus sur la programmation sur le systme GNU/Linux.

14-1 - Informations Gnrales


http://www.advancedlinuxprogramming.com est le site de ce livre. Vous pourrez y tlcharger l'intgrale de ce livre et les sources des programmes, trouver des liens vers d'autres ressources et obtenir plus d'informations sur la programmation GNU/Linux. Ces informations sont galement disponibles sur http:// www.newriders.com; http://www.tldp.org hberge le Linux Documentation Project. Ce site regroupe une grande varit de documents, des listes de FAQ, HOWTO et autres concernant les systmes et logiciels GNU/Linux; http://www.advancedlinuxprogramming-fr.org est le site de la version franaise de ce livre, vous y trouverez un wiki reprenant son contenu amlior par les lecteurs.

14-2 - Informations sur les Logiciels GNU/Linux


http://www.gnu.org est le site officiel du projet GNU. partir de ce site, vous pouvez tlcharger une quantit impressionnante d'applications libres sophistiques. On y trouve entre autre la bibliothque~C~GNU, qui fait partie de tout systme GNU/Linux et une part importante des fonctions prsentes dans ce livre. Le site du Projet GNU fournit galement des informations sur la faon de contribuer au dveloppement du systme GNU/Linux en crivant du code ou de la documentation, en utilisant des logiciels libres et en faisant passer le message des logiciels libres; http://www.kernel.org est le site principal de distribution du code source du noyau Linux. Pour les questions les plus tordues et les plus techniques sur le fonctionnement de Linux, il s'agit d'une mine. Consultez galement le rpertoire Documentation pour plus d'explications sur le fonctionnement interne du noyau; http://www.linuxhq.com distribue galement des sources patch et informations sur le noyau Linux; http://gcc.gnu.org hbere le projet de la GNU Compiler Collection (GCC). GCC est le principal compilateur utilis sur les systme GNU/Linux et inclut des compilateurs C, C++, l'Objective C, Java, Chill, Fortran, etc.; http://www.gnome.org et http://www.kde.org hbergent les deux environnements de bureau les plus populaires sous Linux, Gnome et KDE. Si vous prvoyez d'crire une application avec une interface graphique, vous devriez commencer par vous familiariser avec l'un des deux (ou les deux).

14-3 - Autres Sites


http://developer.intel.com fournit des informations sur l'architecture des processeurs Intel, y compris pour les x86 (IA32). Si vous dveloppez pour Linux sur x86 et que vous utilisez des instructions assembleur en ligne, les manuels techniques qui y sont disponibles peuvent vous tre trs utiles; http://developer.amd.com/ fournit le mme genre d'information sur les processeurs d'AMD et leurs fonctionnalits spcifiques; http://freshmeat.net recense des projets open source, gnralement pour GNU/Linux. Ce site est l'un des meilleurs endroits pour se tenir au courant des nouvelles version de logiciels GNU/Linux, depuis le systme de base des applications plus obscures et spcialises; http://www.linuxsecurity.com donne des informations, des techniques et des liens concernant des logiciels lis la scurit sous GNU/Linux. Ce site peut tre intressant pour les utilisateurs, les administrateurs systme et les dveloppeurs.

- 197 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

15 - Open Publication License Version 1.0


Cette traduction de l'Open Publication License n'est pas officielle. Seule la version (en anglais) disponible sur http:// www.opencontent.org/openpub/ fait foi. L'auteur ne pourra tre tenu responsable d'erreurs de traduction.

15-1 - Conditions Applicables aux Versions Modifies ou Non


Les travaux sous Open Publication License peuvent tre reproduits et distribus, entirement ou partiellement, sous quelle que forme que ce soit, physique ou lectronique, la condition que les termes de cette licence soient respects et que cette licence ou une rfrence celle-ci (mentionnant les options xeces par les auteurs et/ou l'diteur) soit attache la reproduction. La forme de l'incorporation doit tre la suivante:
Copyright <anne> <auteur ou mandataire>. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l'Open Poublication License, vX.Y ou ultrieure (la dernire version est disponible sur [[http://www.opencontent.org/openpub/]]).

La rfrence doit tre immdiatement suivie de la mention des options exerces par les auteurs ou l'diteur du document (voir Section OPLoptions, Section OPLoptions). La redistribution de documents sous Open Publication License est autorise. Toute publication sous une forme classique (papier) impose la citation de l'diteur et de l'auteur original. Les noms de l'diteur et des auteurs doivent apparatre sur toutes les couvertures du livre. Sur les couvertures, le nom de l'diteur original devrait tre aussi grand que le titre de l'ouvrage et adopter une forme possessive vis vis du titre.

15-2 - Copyright
Le copyright d'un travail sous Open Publication License est dtenu par ses auteurs ou mandataires.

15-3 - Porte de cette Licence


Les termes de cette licence s'appeliquent tous les travaux sous licence Open Publication sauf mention contraire explicite dans le document. La mise en commun de travaux sous licence Open Publication ou d'un document sous licence Open Publication avec des travaux sous une autre licence sur le mme support ne provoque pas l'application de la licence Open Publication aux autres travaux. Les documents rsultants doivent contenir une note stipulant l'inclusion de documents sous licence Open Publication et la mention de copyright adquate. Divisibilit. Si une partie de cette licence s'avre inapplicable en raison des lois en vigueur, le reste de la licence reste en vigueur; Absence de Garantie. Les travaux sous licence Open Publication sont fournis "en l'tat" sans aucune garantie d'aucune sort, explicite ou implicite, y compris, mais ne se limitant pas , les garanties de commercialisation ou d'adaptation un but particulier ou une garantie de non violation de copyright.

15-4 - Conditions Applicables aux Travaux Modifis


Toute version modifie des documents couverts par cette licence, y compris les traduction, les anthologies, les compilations et les reproductions partielles doivent rpondre aux conditions suivantes:

- 198 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

- La version modifie doit tre indique comme telle; - La personne effectuant les modifications doit tre identifie et les modifications dates; - Des remerciements vis vis des auteurs auteurs et de l'diteur originaux, s'il y a lieu, doivent tre maintenus en accord avec les pratiques habituelles en matire de citations acadmiques; - L'emplacement du document original doit tre clairement identifi; - Les noms des auteurs originaux ne doit pas tre utilis pour cautionner le document modifi sans leur permission.

15-5 - Bonnes Pratiques


En plus des contraintes imposes par cette licence, il est demand et fortement recommand aux ditributeurs: - Si vous distribuez des travaux sous licence Open Publication sous forme papier ou numrique, prvenez les auteurs de vos intention au moins trente jours avant le gel de la maquette, afin de donner aux auteur le temps de fournir des documents jour. Cette notification doit mentionner, s'il y a lieu, les modifications apportes au document; - Toute modification substancielle (y compris les suppressions) doit tre clairement signale dans le document ou dcrite dans une note attache au document; - Enfin, bien que cela ne soit pas impos par cette licence, il est de bon ton d'offrir une copie gratuite de toute version papier ou numrique d'un travail sous licence Open Publication ses auteurs.

15-6 - Options Possibles


Les auteurs ou l'diteur d'un document sous licence Open Publication peut choisir d'apporter des modifications la licence sous forme d'options signales la suite de la rfrence ou de la copie de cette licence. Ces options font partie de la licence et doivent tre incluses avec celle-ci (ou avec ses rfrences) dans les travaux drivs. - Interdire la distribution de version modifies de faon substancielle sans l'autorisation explicite des auteurs. Le terme "modifications substancielles" est dfinie comme tant une modification du contenu smantique du document et exclut les modifications ne touchant que le formatage ou les corrections typographiques. Pour cela, ajoutez la mention "La distribution de versions de ce document modifies de manire substancielle est interdite sans l'autorisation explicite du dtenteur du copyright" la suite de la rfrence la licence ou de sa copie; - Interdire toute distribution des travaux, drivs ou non, dans leur intgralit ou non au format papier dans un but commercial est interdit sauf autorisation pralable du dtenteur du copyright. Pour appliquer cette option, ajoutez la mention "La distribution de ce document ou de travaux drivs s'y rapportant sous forme papier est interdite sauf autorisation pralable du dtenteur du copyright" la suite de la rfrence la licence ou de sa copie.

15-7 - Annexe la Licence Open Publication


(Cette section n'est pas considr comme faisant partie de la licence). Les sources de travaux sous licence Open Publication sont disponibles sur la page d'accueil d'Open Publication sur http://works.opencontent.org/. Les auteurs voulant proposer leurs propres licences appliques des travaux Open Publication peuvent le faire la condition que les termes ne soient pas plus restrictifs que ceux de la licence Open Publication.

- 199 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si vous avez des questions concernant la Licence Open Publication, veuillez contacter David Wiley ou la Liste des Auteurs Open Publication par email l'adresse mailto:opal@opencontent.org. Pour souscrire cette liste, envoyez un email contenant le mot "subscribe" l'adresse mailto:opalrequest@opencontent.org. Pour envoyer un email la liste, envoyez un email mailto:opal@opencontent.org ou rpondez simplement un email. Pour rsilier votre abonnement cette liste, envoyez un email contenant le mot "unsubscribe" l'adresse mailto:opalrequest@opencontent.org.

- 200 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

16 - Licence Publique Gnrale GNU


La version originale de cette licence est disponible sur http://www.gnu.org/copyleft/gpl.html, cette traduction est disponible sur http://fsffrance.org/gpl/gpl-fr.fr.html. >Ceci est une traduction non officielle de la GNU General Public License en franais. Elle n'a pas t publie par la Free Software Foundation, et ne dtermine pas les termes de distribution pour les logiciels qui utilisent la GNU GPL, seul le texte anglais original de la GNU GPL dterminent ces termes. Cependant, nous esprons que cette traduction aidera les francophones mieux comprendre la GNU GPL.

16-1 - Prambule
Les licences de la plupart des logiciels sont conues pour vous enlever toute libert de les partager et de les modifier. A contrario, la Licence Publique Gnrale est destine garantir votre libert de partager et de modifier les logiciels libres, et assurer que ces logiciels soient libres pour tous leurs utilisateurs. La prsente Licence Publique Gnrale s'applique la plupart des logiciels de la Free Software Foundation, ainsi qu' tout autre programme pour lequel ses auteurs s'engagent l'utiliser. (Certains autres logiciels de la Free Software Foundation sont couverts par la GNU Lesser General Public License la place.) Vous pouvez aussi l'appliquer aux programmes qui sont les vtres. Quand nous parlons de logiciels libres, nous parlons de libert, non de prix. Nos licences publiques gnrales sont conues pour vous donner l'assurance d'tre libres de distribuer des copies des logiciels libres (et de facturer ce service, si vous le souhaitez), de recevoir le code source ou de pouvoir l'obtenir si vous le souhaitez, de pouvoir modifier les logiciels ou en utiliser des lments dans de nouveaux programmes libres et de savoir que vous pouvez le faire. Pour protger vos droits, il nous est ncessaire d'imposer des limitations qui interdisent quiconque de vous refuser ces droits ou de vous demander d'y renoncer. Certaines responsabilits vous incombent en raison de ces limitations si vous distribuez des copies de ces logiciels, ou si vous les modifiez. Par exemple, si vous distribuez des copies d'un tel programme, titre gratuit ou contre une rmunration, vous devez accorder aux destinataires tous les droits dont vous disposez. Vous devez vous assurer qu'eux aussi reoivent ou puissent disposer du code source. Et vous devez leur montrer les prsentes conditions afin qu'ils aient connaissance de leurs droits. Nous protgeons vos droits en deux tapes : (1) nous sommes titulaires des droits d'auteur du logiciel, et (2) nous vous dlivrons cette licence, qui vous donne l'autorisation lgale de copier, distribuer et/ou modifier le logiciel. En outre, pour la protection de chaque auteur ainsi que la ntre, nous voulons nous assurer que chacun comprenne que ce logiciel libre ne fait l'objet d'aucune garantie. Si le logiciel est modifi par quelqu'un d'autre puis transmis des tiers, nous voulons que les destinataires soient mis au courant que ce qu'ils ont reu n'est pas le logiciel d'origine, de sorte que tout problme introduit par d'autres ne puisse entacher la rputation de l'auteur originel. En dfinitive, un programme libre restera la merci des brevets de logiciels. Nous souhaitons viter le risque que les redistributeurs d'un programme libre fassent des demandes individuelles de licence de brevet, ceci ayant pour effet de rendre le programme propritaire. Pour viter cela, nous tablissons clairement que toute licence de brevet doit tre concde de faon ce que l'usage en soit libre pour tous ou bien qu'aucune licence ne soit concde. Les termes exacts et les conditions de copie, distribution et modification sont les suivants:

- 201 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

16-2 - Conditions de copie, distribution et modification de la Licence Publique Gnrale GNU.


0. La prsente Licence s'applique tout programme ou tout autre ouvrage contenant un avis, appos par le titulaire des droits d'auteur, stipulant qu'il peut tre distribu au titre des conditions de la prsente Licence Publique Gnrale. Ci-aprs, le "Programme" dsigne l'un quelconque de ces programmes ou ouvrages, et un "ouvrage fond sur le Programme" dsigne soit le Programme, soit un ouvrage qui en drive au titre des lois sur le droit d'auteur : en d'autres termes, un ouvrage contenant le Programme ou une partie de ce dernier, soit l'identique, soit avec des modifications et/ou traduit dans un autre langage. (Ci-aprs, le terme "modification" implique, sans s'y rduire, le terme traduction) Chaque concessionaire sera dsign par "vous". Les activits autres que la copie, la distribution et la modification ne sont pas couvertes par la prsente Licence ; elles sont hors de son champ d'application. L'opration consistant excuter le Programme n'est soumise aucune limitation et les sorties du programme ne sont couvertes que si leur contenu constitue un ouvrage fond sur le Programme (indpendamment du fait qu'il ait t ralis par l'excution du Programme). La validit de ce qui prcde dpend de ce que fait le Programme. 1. Vous pouvez copier et distribuer des copies l'identique du code source du Programme tel que vous l'avez reu, sur n'importe quel support, du moment que vous apposiez sur chaque copie, de manire ad hoc et parfaitement visible, l'avis de droit d'auteur adquat et une exonration de garantie ; que vous gardiez intacts tous les avis faisant rfrence la prsente Licence et l'absence de toute garantie ; et que vous fournissiez tout destinataire du Programme autre que vous-mme un exemplaire de la prsente Licence en mme temps que le Programme. Vous pouvez faire payer l'acte physique de transmission d'une copie, et vous pouvez, votre discrtion, proposer une garantie contre rmunration. 2. Vous pouvez modifier votre copie ou des copies du Programme ou n'importe quelle partie de celui-ci, crant ainsi un ouvrage fond sur le Programme, et copier et distribuer de telles modifications ou ouvrage selon les termes de l'Article 1 ci-dessus, condition de vous conformer galement chacune des obligations suivantes : a) Vous devez munir les fichiers modifis d'avis bien visibles stipulants que vous avez modifi ces fichiers, ainsi que la date de chaque modification ; b) Vous devez prendre les dispositions ncessaires pour que tout ouvrage que vous distribuez ou publiez, et qui, en totalit ou en partie, contient ou est fond sur le Programme - ou une partie quelconque de ce dernier - soit concd comme un tout, titre gratuit, n'importe quel tiers, au titre des conditions de la prsente Licence. c) Si le programme modifi lit habituellement des instructions de faon interactive lorsqu'on l'excute, vous devez, quand il commence son excution pour ladite utilisation interactive de la manire la plus usuelle, faire en sorte qu'il imprime ou affiche une annonce comprenant un avis de droit d'auteur ad hoc, et un avis stipulant qu'il n'y a pas de garantie (ou bien indiquant que c'est vous qui fournissez la garantie), et que les utilisateurs peuvent redistribuer le programme en respectant les prsentes obligations, et expliquant l'utilisateur comment voir une copie de la prsente Licence. (Exception : si le Programme est lui-mme interactif mais n'imprime pas habituellement une telle annonce, votre ouvrage fond sur le Programme n'est pas oblig d'imprimer une annonce).

Ces obligations s'appliquent l'ouvrage modifi pris comme un tout. Si des lments identifiables de cet ouvrage ne sont pas fonds sur le Programme et peuvent raisonnablement tre considrs comme des ouvrages indpendants distincts en eux mmes, alors la prsente Licence et ses conditions ne s'appliquent pas ces lments lorsque vous les distribuez en tant qu'ouvrages distincts. Mais lorsque vous distribuez ces mmes lments comme partie d'un tout, lequel constitue un ouvrage fond sur le Programme, la distribution de ce tout doit tre soumise aux conditions de la prsente Licence, et les autorisations qu'elle octroie aux autres concessionnaires s'tendent l'ensemble de l'ouvrage et par consquent chaque et toute partie indiffrement de qui l'a crite. Par consquent, l'objet du prsent article n'est pas de revendiquer des droits ou de contester vos droits sur un ouvrage entirement crit par vous; son objet est plutt d'exercer le droit de contrler la distribution d'ouvrages drivs ou d'ouvrages collectifs fonds sur le Programme. De plus, la simple proximit du Programme avec un autre ouvrage qui n'est pas fond sur le Programme (ou un ouvrage fond sur le Programme) sur une partition d'un espace de stockage ou un support de distribution ne place pas cet autre ouvrage dans le champ d'application de la prsente Licence.

- 202 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

3. Vous pouvez copier et distribuer le Programme (ou un ouvrage fond sur lui, selon l'Article 2) sous forme de code objet ou d'excutable, selon les termes des Articles 1 et 2 ci-dessus, condition que vous accomplissiez l'un des points suivants : a) L'accompagner de l'intgralit du code source correspondant, sous une forme lisible par un ordinateur, lequel doit tre distribu au titre des termes des Articles 1 et 2 ci-dessus, sur un support habituellement utilis pour l'change de logiciels; ou, b) L'accompagner d'une proposition crite, valable pendant au moins trois ans, de fournir tout tiers, un tarif qui ne soit pas suprieur ce que vous cote l'acte physique de raliser une distribution source, une copie intgrale du code source correspondant sous une forme lisible par un ordinateur, qui sera distribue au titre des termes des Articles 1 et 2 ci-dessus, sur un support habituellement utilis pour l'change de logiciels; ou, c) L'accompagner des informations reues par vous concernant la proposition de distribution du code source correspondant. (Cette solution n'est autorise que dans le cas d'une distribution non commerciale et seulement si vous avez reu le programme sous forme de code objet ou d'excutable accompagn d'une telle proposition - en conformit avec le sous-Article b ci-dessus.)

Le code source d'un ouvrage dsigne la forme favorite pour travailler des modifications de cet ouvrage. Pour un ouvrage excutable, le code source intgral dsigne la totalit du code source de la totalit des modules qu'il contient, ainsi que les ventuels fichiers de dfinition des interfaces qui y sont associs, ainsi que les scripts utiliss pour contrler la compilation et l'installation de l'excutable. Cependant, par exception spciale, le code source distribu n'est pas cens inclure quoi que ce soit de normalement distribu (que ce soit sous forme source ou binaire) avec les composants principaux (compilateur, noyau, et autre) du systme d'exploitation sur lequel l'excutable tourne, moins que ce composant lui-mme n'accompagne l'excutable. Si distribuer un excutable ou un code objet consiste offrir un accs permettant leur copie depuis un endroit particulier, alors l'offre d'un accs quivalent pour copier le code source depuis le mme endroit compte comme une distribution du code source - mme si les tiers ne sont pas contraints de copier le source en mme temps que le code objet. 4. Vous ne pouvez copier, modifier, concder en sous-licence, ou distribuer le Programme, sauf tel qu'expressment prvu par la prsente Licence. Toute tentative de copier, modifier, concder en sous-licence, ou distribuer le Programme d'une autre manire est rpute non valable, et met immdiatement fin vos droits au titre de la prsente Licence. Toutefois, les tiers ayant reu de vous des copies, ou des droits, au titre de la prsente Licence ne verront pas leurs autorisations rsilies aussi longtemps que ledits tiers se conforment pleinement elle. 5. Vous n'tes pas oblig d'accepter la prsente Licence tant donn que vous ne l'avez pas signe. Cependant, rien d'autre ne vous accorde l'autorisation de modifier ou distribuer le Programme ou les ouvrages fonds sur lui. Ces actions sont interdites par la loi si vous n'acceptez pas la prsente Licence. En consquence, en modifiant ou distribuant le Programme (ou un ouvrage quelconque fond sur le Programme), vous signifiez votre acceptation de la prsente Licence en le faisant, et de toutes ses conditions concernant la copie, la distribution ou la modification du Programme ou d'ouvrages fonds sur lui. 6. Chaque fois que vous redistribuez le Programme (ou n'importe quel ouvrage fond sur le Programme), une licence est automatiquement concde au destinataire par le concdant originel de la licence, l'autorisant copier, distribuer ou modifier le Programme, sous rserve des prsentes conditions. Vous ne pouvez imposer une quelconque limitation supplmentaire l'exercice des droits octroys au titre des prsentes par le destinataire. Vous n'avez pas la responsabilit d'imposer le respect de la prsente Licence des tiers. 7. Si, consquement une dcision de justice ou l'allgation d'une transgression de brevet ou pour toute autre raison (non limite un probleme de brevet), des obligations vous sont imposes (que ce soit par jugement, conciliation ou autre) qui contredisent les conditions de la prsente Licence, elles ne vous excusent pas des conditions de la prsente Licence. Si vous ne pouvez distribuer de manire satisfaire simultanment vos obligations au titre de la prsente Licence et toute autre obligation pertinente, alors il en dcoule que vous ne pouvez pas du tout distribuer le Programme. Par exemple, si une licence de brevet ne permettait pas une redistribution sans redevance du Programme par tous ceux qui reoivent une copie directement ou indirectement par votre intermdiaire, alors la seule faon pour vous de satisfaire la fois la licence du brevet et la prsente Licence serait de vous abstenir totalement de toute distribution du Programme.

- 203 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

Si une partie quelconque de cet article est tenue pour nulle ou inopposable dans une circonstance particulire quelconque, l'intention est que le reste de l'article s'applique. La totalit de la section s'appliquera dans toutes les autres circonstances. Cet article n'a pas pour but de vous induire transgresser un quelconque brevet ou d'autres revendications un droit de proprit ou contester la validit de la moindre de ces revendications ; cet article a pour seul objectif de protger l'intgrit du systme de distribution du logiciel libre, qui est mis en oeuvre par la pratique des licenses publiques. De nombreuses personnes ont fait de gnreuses contributions au large spectre de logiciels distribus par ce systme en se fiant l'application cohrente de ce systme ; il appartient chaque auteur/donateur de dcider si il ou elle veut distribuer du logiciel par l'intermdiaire d'un quelconque autre systme et un concessionaire ne peut imposer ce choix. Cet article a pour but de rendre totalement limpide ce que l'on pense tre une consquence du reste de la prsente Licence. 8. Si la distribution et/ou l'utilisation du Programme est limite dans certains pays que ce soit par des brevets ou par des interfaces soumises au droit d'auteur, le titulaire originel des droits d'auteur qui dcide de couvrir le Programme par la prsente Licence peut ajouter une limitation gographique de distribution explicite qui exclue ces pays afin que la distribution soit permise seulement dans ou entre les pays qui ne sont pas ainsi exclus. Dans ce cas, la prsente Licence incorpore la limitation comme si elle tait crite dans le corps de la prsente Licence. 9. La Free Software Foundation peut, de temps autre, publier des versions rvises et/ou nouvelles de la Licence Publique Gnrale. De telles nouvelles versions seront similaires la prsente version dans l'esprit mais pourront diffrer dans le dtail pour prendre en compte de nouvelles problmatiques ou inquitudes. Chaque version possde un numro de version la distinguant. Si le Programme prcise le numro de version de la prsente Licence qui s'y applique et "une version ultrieure quelconque", vous avez le choix de suivre les conditions de la prsente version ou de toute autre version ultrieure publie par la Free Software Foundation. Si le Programme ne spcifie aucun numro de version de la prsente Licence, vous pouvez choisir une version quelconque publie par la Free Software Foundation quelque moment que ce soit. 10. Si vous souhaitez incorporer des parties du Programme dans d'autres programmes libres dont les conditions de distribution sont diffrentes, crivez l'auteur pour lui en demander l'autorisation. Pour les logiciels dont la Free Software Foundation est titulaire des droits d'auteur, crivez la Free Software Foundation ; nous faisons parfois des exceptions dans ce sens. Notre dcision sera guide par le double objectif de prserver le statut libre de tous les drivs de nos logiciels libres et de promouvoir le partage et la rutilisation des logiciels en gnral.

16-2-1 - ABSENCE DE GARANTIE


11. COMME LA LICENCE DU PROGRAMME EST CONCEDEE A TITRE GRATUIT, AUCUNE GARANTIE NE S'APPLIQUE AU PROGRAMME, DANS LES LIMITES AUTORISEES PAR LA LOI APPLICABLE. SAUF MENTION CONTRAIRE ECRITE, LES TITULAIRES DU DROIT D'AUTEUR ET/OU LES AUTRES PARTIES FOURNISSENT LE PROGRAMME "EN L'ETAT", SANS AUCUNE GARANTIE DE QUELQUE NATURE QUE CE SOIT, EXPRESSE OU IMPLICITE, Y COMPRIS, MAIS SANS Y ETRE LIMITE, LES GARANTIES IMPLICITES DE COMMERCIABILITE ET DE LA CONFORMITE A UNE UTILISATION PARTICULIERE. VOUS ASSUMEZ LA TOTALITE DES RISQUES LIES A LA QUALITE ET AUX PERFORMANCES DU PROGRAMME. SI LE PROGRAMME SE REVELAIT DEFECTUEUX, LE COUT DE L'ENTRETIEN, DES REPARATIONS OU DES CORRECTIONS NECESSAIRES VOUS INCOMBENT INTEGRALEMENT. 12. EN AUCUN CAS, SAUF LORSQUE LA LOI APPLICABLE OU UNE CONVENTION ECRITE L'EXIGE, UN TITULAIRE DE DROIT D'AUTEUR QUEL QU'IL SOIT, OU TOUTE PARTIE QUI POURRAIT MODIFIER ET/OU REDISTRIBUER LE PROGRAMME COMME PERMIS CI-DESSUS, NE POURRAIT ETRE TENU POUR RESPONSABLE A VOTRE EGARD DES DOMMAGES, INCLUANT LES DOMMAGES GENERIQUES, SPECIFIQUES, SECONDAIRES OU CONSECUTIFS, RESULTANT DE L'UTILISATION OU DE L'INCAPACITE D'UTILISER LE PROGRAMME (Y COMPRIS, MAIS SANS Y ETRE LIMITE, LA PERTE DE DONNEES, OU LE FAIT QUE DES DONNEES SOIENT RENDUES IMPRECISES, OU LES PERTES EPROUVEES PAR VOUS OU PAR DES TIERS, OU LE FAIT QUE LE PROGRAMME ECHOUE A INTEROPERER AVEC UN AUTRE PROGRAMME QUEL

- 204 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

QU'IL SOIT) MEME SI LE DIT TITULAIRE DU DROIT D'AUTEUR OU LE PARTIE CONCERNEE A ETE AVERTI DE L'EVENTUALITE DE TELS DOMMAGES. FIN DES CONDITIONS

16-3 - Comment appliquer ces conditions vos nouveaux programmes


Si vous dveloppez un nouveau programme, et si vous voulez qu'il soit de la plus grande utilit possible pour le public, le meilleur moyen d'y parvenir est d'en faire un logiciel libre que chacun peut redistribuer et modifier au titre des prsentes conditions. Pour ce faire, munissez le programme des avis qui suivent. Le plus sr est de les ajouter au dbut de chaque fichier source pour vhiculer le plus efficacement possible l'absence de toute garantie ; chaque fichier devrait aussi contenir au moins la ligne "copyright" et une indication de l'endroit o se trouve l'avis complet. [Une ligne donnant le nom du programme et une courte ide de ce qu'il fait.] Copyright (C) [anne] [nom de l'auteur] Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier au titre des clauses de la Licence Publique Gnrale GNU, telle que publie par la Free Software Foundation ; soit la version 2 de la Licence, ou ( votre discrtion) une version ultrieure quelconque. Ce programme est distribu dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans mme une garantie implicite de COMMERCIABILITE ou DE CONFORMITE A UNE UTILISATION PARTICULIERE. Voir la Licence Publique Gnrale GNU pour plus de dtails. Vous devriez avoir reu un exemplaire de la Licence Publique Gnrale GNU avec ce programme ; si ce n'est pas le cas, crivez la Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Ajoutez aussi des informations sur la manire de vous contacter par courrier lectronique et courrier postal. Si le programme est interactif, faites en sorte qu'il affiche un court avis tel que celui-ci lorsqu'il dmarre en mode interactif : Gnomovision version 69, Copyright (C) anne nom de l'auteur Gnomovision n'est accompagn d'ABSOLUMENT AUCUNE GARANTIE ; pour plus de dtails tapez "show w". Ceci est un logiciel libre et vous tes invit le redistribuer en respectant certaines obligations ; pour plus de dtails tapez "show c". Les commandes hypothtiques "show w" et "show c" sont supposes montrer les parties ad hoc de la Licence Publique Gnrale. Bien entendu, les instructions que vous utilisez peuvent porter d'autres noms que "show w" et "show c" ; elles peuvent mme tre des clics de souris ou des lments d'un menu -- ou tout ce qui convient votre programme. Vous devriez aussi obtenir de votre employeur (si vous travaillez en tant que dveloppeur) ou de votre cole, si c'est le cas, qu'il (ou elle) signe une "renonciation aux droits d'auteur" concernant le programme, si ncessaire. Voici un exemple (changez les noms): Yoyodyne, Inc., dclare par la prsente renoncer toute prtention sur les droits d'auteur du programme "Gnomovision" (qui fait des avances aux compilateurs) crit par James Hacker.

- 205 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Programmation avance sous Linux par Michal Todorovic (Autres articles) (Blog)

[signature de Ty Coon], 1er avril 1989 Ty Coon, Prsident du Vice La prsente Licence Publique Gnrale n'autorise pas l'incorporation de votre programme dans des programmes propritaires. Si votre programme est une bibliothque de sous-programmes, vous pouvez considrer plus utile d'autoriser l'dition de liens d'applications propritaires avec la bibliothque. Si c'est ce que vous voulez faire, utilisez la GNU Lesser General Public License au lieu de la prsente Licence.

- 206 Copyright 2007 Mark Mitchell, Jeffrey Oldham et Alex Samuel. Ce document ne peut tre distribu que dans le respect des termes et conditions dfinies par l?Open Poublication License, v1.0 ou ultrieure (la dernire version est disponible sur http://www.opencontent.org/openpub/).
http://mtodorovic.ftp-developpez.com/linux/programmation-avancee/

Vous aimerez peut-être aussi