Home | News | Hacking | Sciences | Technology | Ti 92 | Programming | Free articles | Links | Webmaster

Coding

Homepage / TS / Coding

|       |- /Langages
|       |- /Networking & Middleware
|       |-
/
XML & Java
|       |-
/
C/C++
|       |- /Makefile
|       |- /Multitâche
|       |- /Documentation PERL en français
|       |- /Active Server Pages+
|       |-
/
Introduction to PERL
|       |- /Sather 1.1 : Language Essentials
|       |- /Scripting : Higher Level Programming for the 21st Century
|       |- /Comment reconnaître la religion d'un logiciel ?
|       |- /Parallel Programming : Basic Theory for the Unwary
|       |- /PERL Regular Expression Tutorial
|- /Hacking & Network
|-
/
System
|- /Virus & Artificial Life
|- /Cracking & Reverse Engineering
|-
/Phreaking & Waves

 

Langages de Programmation

La programmation est un langage informatique composé d'une série d'instructions pouvant être traitées, interprétées et exécutées par un ordinateur. Ces instructions, qui sont frappées au clavier, se composent de caractères, de symboles, et de règles permettant de les assembler.

Traduction d'un langage

Un ordinateur ne comprend qu'un seul langage, le langage machine qui se présente comme une suite de "0" et de "1" (forme binaire). C'est pourquoi tout programme écrit dans un langage de haut niveau doit être décodé et traduit en langage machine, avant de pouvoir être exécuté. Ce processus de conversion du code source (frappé au clavier) au code objet (assimilable par l'ordinateur) est assuré par un programme capable de traduire un jeu de symboles en un autre jeu, par application de règles de syntaxe et de sémantique. Suivant la nature du langage de programmation employé, ce programme s'appelle un compilateur ou un interpréteur.

Langages compilés

Les langages compilés sont des langages où toutes les instructions sont traduites en code objet avant d'être exécutées. Cette conversion s'effectue au moyen d'un compilateur.

Langages interprétés

Les langages interprétés sont des langages décodés et exécutés instruction par instruction à l'aide d'un programme appelé interpréteur. Le plus connu des langages interprétés est le langage BASIC, bien que la plupart des versions actuelles en permettent ou en imposent la compilation.

 

Niveau d'un langage 

Il existe différents types de langages, allant du plus rudimentaire au plus complexe, que l'on classe généralement en deux familles: les langages de bas niveau et les langages évolués. On y ajoute parfois une autre catégorie, les langages de quatrième génération.

Langages de bas niveau

Les langages de bas niveau sont des langages proches de la machine, ou des langages offrant peu d'instructions et de types de données. En général, chaque instruction écrite dans un langage de bas niveau correspond à une instruction machine. Le langage machine et le langage assembleur sont considérés comme des langages de bas niveau.

Langages évolués

Les langages évolués, dits aussi de haut niveau, sont des langages informatiques offrant un certain niveau d'abstraction par rapport au langage de la machine, et manipulant des structures syntaxiques telles que les déclarations, les instructions de contrôle, etc. Usuellement, le terme désigne tout langage de niveau supérieur à celui du langage assembleur.
Les langages évolués sont classés en trois grandes familles: les langages procéduraux, les langages orientés-objets et les langages orientés-listes. On retrouve ainsi dans la famille des langages procéduraux le FORTRAN, le COBOL, le BASIC, l'Ada, le PASCAL et le C, dans la famille des langages orientés-objets, le C++ et le Java, et dans la famille des langages orientés-listes, le LISP.

Langages de quatrième génération

Les langages de quatrième génération (L4G en abrégé), conçus pour l'interaction avec le programmeur, qualifient souvent les langages propres aux bases de données relationnelles. Se situant un cran au-dessus de langages tels que le Pascal ou le COBOL, ils se composent d'un jeu d'instructions s'apparentant à des macro-instructions, séquences d'instructions prédéfinies auxquelles on accède par une commande très simple. Toutefois, ces langages conservent un aspect hybride, dérivant le plus souvent des langages évolués.

 

Type de langage  

Langages orientés-objets

Les langages orientés-objets sont des langages adaptés à la programmation orientée-objet, type de programmation où chaque programme est considéré comme un ensemble d'objets distincts, ces objets constituant eux-mêmes des ensembles de structures de données et de procédures intégrées. Dans de tels langages, chaque objet appartient à une classe qui définit les structures de données et les procédures associées à cet objet.

Langages orientés-listes

Les langages orientés-listes peuvent être apparentés aux langages orientés-objets, à la différence près qu'ils manipulent non pas des objets mais des listes, c'est-à-dire des structures de données multi-éléments à organisation linéaire.

Langage machine

Le langage machine représente le langage dans lequel s'exprime le résultat final d'une compilation de langage assembleur ou d'un langage de haut niveau quelconque. Constitué de "!0!" et de "!1!", ce langage est chargé et exécuté par le microprocesseur. Appelé également code machine, il constitue le seul langage réellement "!compris!" par l'ordinateur, tous les autres langages correspondant à des formes de structuration du langage humain.

Langages procéduraux

Les langages procéduraux sont des langages où la procédure (suite d'instructions) constitue l'élément de base. La plupart des langages évolués sont des langages procéduraux.

 

Langage de programmation

Langage Assembleur

Le langage assembleur est un langage de programmation de très bas niveau, où chaque instruction correspond à une instruction machine unique. Le jeu d'instructions d'un tel langage est donc associé à un certain type de processeur. Ainsi, les programmes écrits en langage assembleur pour un processeur particulier doivent être réécrits pour tourner sur un ordinateur équipé d'un processeur différent. Après écriture d'un programme en langage assembleur, le programmeur fait alors appel à l'assembleur spécifique du processeur, qui traduit ce programme en instructions machine. Le langage assembleur peut être préféré à un langage de haut niveau lorsque le programmeur recherche une vitesse d'exécution élevée ou un contrôle étroit de la machine. En effet, les programmes écrits dans ce type de langage tournent plus vite et occupent moins de place que ceux produits par un compilateur. En outre, ils donnent au programmeur la possibilité d'agir directement sur le matériel (processeur, mémoire, affichage et connexion d'entrées!/!sorties).

Langage FORTRAN

Premier langage de programmation de haut niveau, le FORTRAN (acronyme de FORmula TRANslation) fut développé entre 1954 et 1958 par Jim Backus. Il est à l'origine de nombreux concepts avancés, tels que les variables, les expressions, les instructions, les sous-programmes compilés séparément et les entrées!/!sorties formatées. Langage compilé et structuré, le FORTRAN fut créé en vue d'être appliqué aux domaines scientifiques et techniques: il est de ce fait encore très utilisé en ingénierie. Il a été révisé et amélioré au cours des trente-cinq dernières années, si bien qu'il s'avère désormais employé dans tous les domaines.

Langage COBOL

Acronyme de COmmon Business-Oriented Language, le COBOL est un langage compilé proche de l'anglais, développé entre 1959 et 1961. Officialisé par le Département américain de la Défense dont il fut un temps le langage obligatoire, il demeure universellement employé, tout particulièrement dans les applications de gestions, de par sa structuration et sa syntaxe proches de l'anglais. Les programmes écrits en COBOL comportent quatre sections: le champ Identification qui fournit le nom du programme et celui de l'auteur, ainsi que toutes les informations annexes que ce dernier estime nécessaires, le champ Environment qui indique le modèle d'ordinateur cible ainsi que les fichiers utilisés en entrée comme en sortie, le champ Data qui décrit les données traitées par le programme et enfin, le champ Procédure qui contient l'ensemble des sous-programmes définissant les actions du programme.

Langage BASIC

Acronyme de Beginners' All-purpose Symbolic Instruction Code, le langage BASIC est un langage de programmation de haut niveau, développé vers le milieu des années 1960 au Dartmouth College par John Kemeny et Thomas Kurtz. L'énorme succès de ce langage est dû à deux de ses versions, Tiny BASIC et Microsoft BASIC, grâce auxquelles BASIC est devenu la lingua franca de la micro-informatique. Mais il existe également d'autres versions importantes de ce langage : comme integrer BASIC et Applesoft BASIC (pour Apple), GW-BASIC (pour l'IBM PC) ou QuickBASIC (Microsoft). Le langage BASIC a ainsi évolué au fil des années, passant d'une forme non structurée et interprétée à des versions structurées et compilées. Il est souvent enseigné aux débutants en raison de sa facilité d'emploi et de sa simplicité, mais également parce qu'il manipule les mêmes concepts que d'autres langages plus complexes, tels que le langage Pascal ou le langage C. Néanmoins, les instructions du langage BASIC demeurent plus limitatives que celles associées aux deux langages précités.

Langage Ada

Le langage Ada tire son nom d'Augusta Ada Byron, fille de lord Byron et assistante de Charles Babbage, figurant à ce titre parmi les précurseurs de l'informatique. Ce langage procédural, conçu selon les normes édictées par le Département américain de la Défense dans les années 1970, était destiné à devenir le langage de développement principal des applications de cet organisme. Dérivé du Pascal, il possède d'importantes extensions sémantiques et syntaxiques, comprenant en particulier l'exécution concurrente de tâches, les opérateurs à fonction multiple et le découpage en modules. Bien que manipulant des objets, le langage Ada n'est pas considéré comme un langage orienté-objet.

Langage Pascal

Mis au point entre 1967 et 1971 par Niklaus Wirth, le langage Pascal est un langage structuré et compilé, dérivé du langage ALGOL. Destiné à traiter des données informatiques, il simplifie la syntaxe d'ALGOL tout en y ajoutant des types et des structures de données supplémentaires, tels que les types énumérés, les fichiers, les enregistrements et les ensembles. Le langage Pascal connut un grand succès à partir de 1984, date de lancement sur le marché du Turbo Pascal par la société Borland International. En effet, ce compilateur à vitesse élevée et d'un prix modique, adapté aux systèmes d'exploitation MS-DOS, fut vendu à plus d'un million d'exemplaires dans ses différentes versions. Malgré ce succès, le langage Pascal semble aujourd'hui céder le pas au langage C, voire au C++, qui s'impose peu à peu comme le langage de développement standard sur micro-ordinateur.
Bien que le PASCAL soit un langage résolument procédural qui excelle dans la réalisation d'applications mathématiques, il existe également une version de Pascal orientée-objet, utilisée aussi dans le développement d'applications.

Langage C

Succédant au langage B (d'où son nom), le langage C fut développé en 1972 par Dennis Ritchie dans les laboratoires américains de la société Bell. Bien que le langage C s'apparente à un langage assembleur indépendant de la machine plutôt qu'à un langage de haut niveau, il peut être cependant considéré comme un langage de programmation standard en micro-informatique, en raison de sa grande popularité, de son association avec le système d'exploitation UNIX et de sa normalisation par l'ANSI (American National Standards Institute). Excepté quelques fonctions natives, le langage C est doté de fonctions indépendantes de la machine, contenues dans des bibliothèques auxquelles peuvent accéder tout programme écrit en C. Langage de programmation structuré et compilé, il s'avère relativement portable d'une machine à l'autre.

Cours de C

Langage C++

Le langage C++, version orientée-objet du langage de programmation C, fut développé par Bjarne Stroustrup dans les laboratoires Bell au début des années 1980, puis adopté par certains constructeurs tels qu'Apple Computer ou Sun Microsystems. Assimilé d'un côté à une extension du langage C, mais considéré de l'autre comme un langage doté d'une approche objet à part entière, le langage C++ s'impose aujourd'hui, grâce à ses fonctions très puissantes, comme le langage de référence de développement de programmes. Le ++ signifie incrémenter en langage C.

Vous pouvez également au sein de votre Web Service, manipuler les objets ASP+ comme l'objet Session ou Application :

Public Sub IncrementeCompteur()
     Session("Compteur") = Session("Compteur") + 1
End Sub

Visual Studio.NET

L'interface de Visual Studio.NET vous facilitera grandement l'utilisation des Web Service. En effet, dans votre environnement de développement, vous aurez accès à un explorateur de Web Service. Puis, par simple drag & drop, vous pourrez déposer vos Web Services dans votre Web Form. Simple non ?

Conclusion

ASP+, dont la version définitive n'apparaîtra que début 2001, est une refonte complète d'ASP. Microsoft devrait bientôt livrer une version bêta (incluant Visual Studio.NET) avec une diffusion plus importante. Quoi que l'on pense de cet éditeur, ce serait un erreur de ne pas essayer cette tehnologie.

 

Makefile

Références > le Système Linux (ed. O'Reilly), Make HowTo (ed. Linux Documentation Project)

La plupart des environnements de développement intégrés (IDE) sous Windows ou MacOS possèdent un gestionnaire de projets. Quelque soit son nom et sa forme, cet outil poursuit toujours le même objectif : centraliser l’ensemble des fichiers et ressources dont ce compose un projet, gérer les dépendances et assurer une compilation correcte. Ainsi, si l’on modifie l’un des fichiers source, par exemple, le gestionnaire de projet tiendra compte de cette modification ; il saura par ailleurs qu’il faudra recompiler ce fichier et procéder de nouveau à une édition de liens pour obtenir un exécutable à jour.

Que fait le Make ?

Sur les systèmes de la famille Unix, le Make remplit le même rôle, avec toutefois plusieurs différences importantes. Avant tout, il ne représente pas une fonction d’un logiciel particulier, mais un outil à part, profondément intégré au système. Ensuite, il ne constitue rien de moins qu’un puissant langage de programmation, spécialisé dans la gestion de projets. Tout ceci lui donne une universalité incomparable, au prix d’un effort considérable d’apprentissage. Commençons donc par expliquer le principe de Make, puis nous verrons quelques améliorations apportées à  la

Lors de la compilation et au cours du développement, de nombreux fichiers temporaires se trouvent générés. La règle clean détruira obligatoirement tous les fichiers inutiles (sauvegardes, modules, objets…) : ainsi, il suffit d’entrer make clean pour obtenir à nouveau une arborescence propre, contenant uniquement les fichiers source et les informations pertinentes.

clean:
  
rm –f *.o *
~

Cette règle ne dépend évidemment de rien, car il n’existe a aucun pré-requis pour nettoyer… la convention veut que l’on offre également une règle plus radicale, distclean, qui efface absolument tout sauf les fichiers fournit par l’auteur du logiciel. Ce la suppose évidemment d’avoir recours à make clean, mais aussi de détruire l’exécutable et tous les fichiers générés, tels que logs et autres core.

distclean: clean
          
rm –f monprog core

Le standard GNU définit bien d’autres règles (près d’un 20aine au total) et donne également des précisions concernant l’utilisation des variables et des commandes du shell dans les Makefiles. Si vous voulez aller plus loin, la documentation de GNU Make délivre des explications très complètes et didactiques sur tous les aspects de la manipulation de ce fantastique outil.

 

Du multitâche au temps réel…

On ne peut aborder le concept de multitâche sans parler des tâches, des processus et des threads. Bien souvent, ces termes très usités en informatique ont perdu leur signification initiale et chacun y vas de sa petite définition. Néanmoins, un processus peut être considéré comme une suite d’instruction à faire exécuter par le processeur. Une tâche a, dans la plupart des cas, un caractère répétitif : un processus répété plusieurs fois par minute est donc une tache. Parlons enfin des threads, démocratisés par Windows, car c’est ainsi que Microsoft a baptisé ses processus enfants dans son système. A l’origine, un thread désigne un morceau d’un programme destine à s’exécuter sur plusieurs processeurs. Le multi-threading ne trouve dès lors sa justification que si l’on dispose de plus d’un processeur. Dans la suite de notre exposé, nous emploierons les termes processus et tâches pour caractériser des instruction. Les puristes nous excuseront…

Le multitâche

Une fois données ces petites précisions de vocabulaire, nous pouvons enfin aborder l’essentiel.

Qu’est-ce que le multitâche ? Il peut paraître tout à fait normal pour un adepte de l’informatique d’aujourd’hui d’utiliser un traitement de texte, d’écouter un CD tout en surfant sur le ouaibe (c’est marrant, ça me rappelle qqun ;)). Cette multiplicité ne ressemble pas vraiment au fonctionnement de base d’un micro-ordinateur. Un microprocesseur ne peut en effet effectuer plus d’une instruction à la fois, même si toutes ces tâches semblent se dérouler en même temps. En fait, dans un système monoprocesseur, on parlera de quasi-simultanéité ou de quasi-parallélisme. En effet, ces tâches ne vont pas s’exécuter en même temps mais disposer du processeur chacune leur tour dans des laps de temps très courts. A l’image de la persistance rétinienne qui nous fait prendre un succession d’images fixes pour une animation, l’alternance très rapide des tâches pourra faire penser qu’elles s’exécutent en parallèle.

Mais comment stopper notre traitement de texte pour passer la main à notre browser ?

Plusieurs solutions existent...

Le multitâche coopératif

Chaque tâche informe le processeur qu’elle n’a plus besoin de lui pendant un temps t. Vous comprenez facilement le ridicule de la chose en imaginant votre lecteur CD s’arrêtant à chaque fois que vous souhaitez sauvegarder votre texte ou bien pire, le plantage d’une application qui stopperait entièrement le système, celle-ci ne pouvant  plus signaler la fin de son travail. Pour résoudre ce problème, les programmeurs ont rapidement développé des solutions beaucoup plus efficaces, à savoir un processus chef d’orchestre : le fameux Ordonnanceur ou scheduler. Ce processus, le plus prioritaire, va déterminer quel processus devra utiliser le processeur. Il interrompra votre traitement texte pour laisser votre lecteur jouer quelques minutes de musique et ce plusieurs fois par seconde.

En rester là serait trop simple. Il existe en effet plusieurs manières de gérer ces commutations de tâches.

Le time-sharing

Le time-sharing ou temps partagé consiste simplement à diviser le temps en tranches et à les affecter successivement aux différents processus du système. Le principal défaut de ce type de multitâche est que l’on en peut prendre en compte l’importance d’un processus par rapport à un autre. Que votre souris se bloque au milieu de l’écran à cause de votre anti-virus qui scanne un fichier et vous aurez vite fais de fracasser le pauvre mulot sur son tapis. Il paraît évident que le l’anti-virus aura une priorité moins importante que le processus de gestion de souris. La souris est un mauvais exemple, ses déplacements se voyant gérés par interruptions, mais ce n’est qu’une image. Afin de mettre en place ces mécanismes, un niveau de priorité sera affecte à chaque processus tournant sur le système. En supposant que 100 soit la priorité maximale (en général, on utilise la constante Max_Priority de valeur 255), un processus de valeur 10 disposera 3 fois moins souvent du processeur qu’un processus de priorité 30. Dans la réalité, l’occupation du processeur par un processus dépendra bien évidemment de la priorité mais aussi d’autres facteurs comme l’attente des ressources.

L’ordonnancement

La méthode Round-Robin

Parlons de l’ordonnancement par la méthode Round-Robin ( aussi appelé tourniquet ou ordonnancement cyclique). Le principe de ce type de multitâche consiste à lancer les tâches dans une boucle principale dit main loop. L’avantage de cette méthode est qu’elle ne nécessite quasiment aucun code de gestion de tâches (excepté quelques changements d’état : lancement, mise en sommeil, réveil et arrêt). Celle-ci ne se verront pas interrompues par un noyau car nous ne sommes pas encore dans un système préemptif.  Cependant, nous pouvons tout à fait répondre à des contraintes de temps en calculant la durée d’exécution de la main loop dans le pire des cas. L’inconvénient de ce type de multitâche est la difficulté dans la programmation des tâches qui vont s’exécuter rapidement et sans attente active. Le Round-Robin, qui laisse donc une plus grande responsabilité au programmeur, peut suffire pour du multitâche sur de petits systèmes dans lesquels la communication avec les périphériques apparaît comme secondaire. Néanmoins, le risque d’attente existe ce qui explique qu’on ne saurait qualifier cet OS de temps réel, même s’il demeure possible d’évaluer le pire temps d’exécution. 

L’ordonnancement préemptif

A l’inverse, l’ordonnancement préemptif, reposant sur la priorité des tâches, permet de déterminer leur importance et de définir celles qui devront disposer du processeur en priorité. Le time-sharing et le time-slicing utilisent un compteur de ticks (horloge temps réel) afin de commuter les différentes tâches.

Et le temps réel ?

En observant les différences entre le Round-Robin et les ordonnancement préemptif, vous aurez compris que le premier ne satisfait pas aux spécifications d’un système temps réel. En effet, le temps réel implique une contrainte de temps. Un noyau temps réel va garantir que le lancement d’une tâche prendra x secondes et pas y, et que le traitement d’une interruption se fera exactement en z secondes. La devise du temps réel : une information juste mais qui arrive en retard est fausse. Imaginons un programme de gestion du système d’injection directe d’un moteur à explosion. Les données doivent être acquises et traitées avant le prochain cycle du moteur, faute de quoi les calculs seront faussés. Un noyau multitâche temps réel va donc permettre d’implémenter la tâche d’acquisition des données et la tâche de traitement tout en donnant la certitude que le tout prendra moins d’un cycle du moteur. Cette garantie qu’offre les systèmes temps réel en fait leur caractéristique principale. Quand vous entendez « notre système informatisé gère les utilisateurs en temps réel », pensez à demander en combien de temps il effectue cette opération et si le chef est prioritaire par rapport à ses utilisateurs.

La gestion des tâches

On rencontre souvent les termes scheduler et dispatcher pour designer l’Ordonnanceur dans le monde du temps réel : ils désignent quasiment la même chose. Il existe 2 manières d’ordonnancer les tâches.

Ordonnancement offline

Il faut précisément connaître à l’avance le déroulement des processus pour créer des tables d’ordonnancement. L’intérêt de ce type d’ordonnancement réside dans sa tres grande rapidité d’exécution. L’inconvénient est évident : vous serez obligé de refaire toute votre table d’ordonnancement à chaque fois que vous rajouterez une tâche dans le système. Ce type d’ordonnancement convient de fait à la gestion des tâches périodiques dont on connaît parfaitement le déroulement et l’enchaînement.

Ordonnancement online

Elle permet de gérer des évènements non prévus ou dont l’arrivée est imprévisible. Dans ce type d’ordonnancement, le scheduler peut interrompre une tâche pour en privilégier une autre. Ce type d’ordonnancement est donc dynamique, plus souple, mais aussi plus lent. En effet, le temps du choix de la tâche à suspendre consomme du temps CPU. Il incombe au scheduler de se charger de ce choix.

Le fonctionnement du Scheduler

Le scheduler constitue une routine du noyau de l’exécutif temps réel. Cet routine est optimisée pour utiliser le moins de temps CPU possible ; Le programme chef d’orchestre va déterminer le processus qui à le droit d’utiliser le processeur. Il doit pour cela connaître tous les processus tournant sur le système ainsi que leurs états respectifs. Afin de commuter entre les différents processus, l’Ordonnanceur doit également être capable de sauvegarder et de restaurer le contexte d’un tâche. Celle-ci se constitue d’un bloc de code exécutable, d’une pile et de Task Control Block (TCB). Ce descripteur TCB contient des informations de contrôle (priorité, assignation E/S, évènements) et le contexte d’exécution (prog counter, compteur de pile, registres).

Les descripteurs sont en général stockes dans une file où ils attendent d’être élus. En effet, une tâche peut se trouver dans plusieurs états :

- endormie : temporisation
- bloquée : attente de sémaphore, file pleine, file vide
- prête : attend le CPU
- active : dispose du CPU

Chaque Ordonnanceur est associé à une politique d’ordonnancement. La politique d’ordonnancement préemptif avec priorité et les différents états possibles d’un tâche provoque le résultat suivant. Le processus du système qui possède la plus haute priorité et qui est prêt se voit alors attribué le processeur. Si une autre tâche se trouve prête 

et que sa priorité supérieur à celle déjà en cours, alors celle-ci sera suspendue et la tâche prête sera alors élue par l’Ordonnanceur. Une tâche éligible qui est élue devient donc active. Son contexte et ses informations de contrôle sont alors lus dans son TCB puis restaurés ; la tâche reprend là ou elle avait été suspendue comme s’il ne s’était rien passé. Vous aurez rapidement compris l’importance de la rapidité du changement de contexte et donc de l’optimisation du scheduler dont nous parlions précédemment. En effet, plusieurs tâches ont très fréquemment la même priorité en même temps. Le scheduler ne sera donc plus appelé pour accorder le processeur à une tâche de priorité supérieur. Pour éviter un blocage du système, il est prévu dans les algorithmes d’ordonnancement un système de Round-Robin. Toutes les tâches de même priorité s’exécuteront à tour de rôle entre 2 préemptions du scheduler. Ces préemptions s’effectuent à une fréquence réglable ; l’utilisateur du noyau peut ainsi utiliser sa propre valeur de tick. Un tick est un appel à l’ordonnanceur.

Les algorithmes d’ordonnancement

Nous introduisons ici la notion de date limite. Comme vous le savez déjà, les systèmes temps réel sont caractérisés par des contraintes de temps d’exécution à respecter. Pour schématiser, disons que la date limite représente la contrainte imposée à un processus pour qu’il respecte la notion de temps réel.

Il existe plusieurs algorithmes d’ordonnancement. Nous n’en citerons que 2, qui reflètent bien la différence de gestion des priorités. Le noyau, lors de la création de la tâche, lui associe un niveau de priorité.

Rate Monotic Scheduling : algorithme dans lequel la priorité dépend de la période de la tâche. Plus celle-ci est courte, plus la priorité sera élevée. Cette priorité reste fixe et ne sera pas réévalué dans le temps.

Earliest Deadline First : algorithme dynamique dans lequel le niveau de priorité se trouve fixé en fonction de l’approche de la date limite. Au cours du temps, plus le processus se rapproche de sa date limite, plus sa priorité sera élevée. La priorité se voit constamment recalculée au cours du temps : si une nouvelle tâches intervient et que sa date limite est plus proche qu’une autre, alors sa priorité lui sera supérieure.

La gestion des ressources

Peut-être avez vous déjà entendu parler des fameux sémaphores, des flags, des drapeaux...

Mais que représentent-ils vraiment ? Un sémaphore n’est, en fait, qu’un simple emplacement en mémoire ; on utilise les termes flags ou drapeaux pour designer le même chose. Il s’agit d’un variable globale – qui ne revêtira que des valeurs entières, positives ou nulles. Cette variable globale peut être consultée, modifiée ou servir de support à une attente. Sous UniX, les sémaphores entrent dans la catégorie des IPC (Inter Process Communication) comme les zones de mémoires partagées et les files de messages. Bien que les sémaphores permettent également de synchroniser des processus entre eux, nous allons détailler leur utilité dans le domaine du partage des ressources au sein des systèmes multitâches.

Les sémaphores binaires

On appelle binaires les sémaphores qui ne prendront que 2 valeurs : 0 ou 1. Il sont employées pour protéger l’accès au ressources lorsque celle-ci demeurent uniques. Par exemple, une seule imprimante peut servir pour plusieurs processus. La tâche utilisant l’imprimante va effectuer l’opération « prendre le sémaphore ». Cette opération empêche toute autre exploitation de l’imprimante par un autre processus. En effet, si une autre tâche tente de prendre le sémaphore, elle se bloque en attendant la vente de celui-ci. C’est encore le processus consommateur qui signale la libération de cette ressource en vendant le sémaphore. Ce système empêche l’attente active, laquelle consomme inutilement du temps CPU, ce qui se montre intolérable dans un système multitâche et encore plus dans un système temps réel.

Bien entendu, si vous utilisez un spooler d’impression, la tâche de gestion de l’imprimante reviendra à ce programme. Ainsi vous n’aurez plus à vérifier la disponibilité de la ressource imprimante.

Les sémaphores à compte

Un sémaphore à compte en fait à un sémaphore binaire, susceptible de prendre comme valeur des entiers supérieurs à 1. On l’emploiera pour restreindre l’accès au ressources de plus d’un processus. Chaque opération « prendre » effectuée sur le sémaphore à compte décrémente d’une unité son contenu. Ainsi on se vois en mesure d’autoriser 3 processus ou plus à utiliser en même temps la même fonction ; il faut pour cela que cette fonction ait été codée de manière à supporter ces appels multiples. Nous reparlerons ultérieurement de la réentrance dans les exécutifs en temps réel.

L’exclusion mutuelle

C’est tout simplement le terme employé lorsque l’on opère le verrouillage d’un ressource par le biais de sémaphores. Une telle démarche se traduit concrètement par une zone appelée « section critique », dans laquelle un seul processus utilisateur est autorisé. Dijkstra a écrit de nombreux algorithmes d’exclusion mutuelle et a défini le concept des sémaphores pour réaliser ces sections critiques. Vous pourrez retrouver le terme « sémaphore mutex » pour mutual exclusion, pour designer les sémaphores qui servent à réaliser les exclusions mutuelles.

Le Deadlock ou l’étreinte fatale

L’accès concurrent à des ressources partagées est facilite par l’utilisation des sémaphores. Néanmoins, il subsiste un problème de taille : l’étreinte fatale, l’interblocage mutuel, le Deadlock ! Celui-ci survient lorsque 2 processus attendent tous les 2 que l’autre libère une ressource. Nous citerons le problème du dîner des philosophes. 5 philosophes s’installent autour d’une table pour manger des spaghettis. On ne peut, bien sur manger ce plat qu’avec 2 fourchettes. Or, on dispose d’autant de fourchettes que de philosophes. Ceux-ci ne font que penser et manger : quand ils ne pensent pas, ils mangent. Comment résoudre ce problème ? Si chaque philosophe tient une fourchette, il attend qu’une autre se libère ; on obtient donc un interblocage mutuel et les philosophes meurent de faim. L’autres cas susceptibles de se présenter, est le suivant : la famine d’un philosophe qui n’arrive jamais à obtenir 2 fourchettes. On résoudra ce problème en mettant en section critique la fonction « manger » ; ainsi, chaque philosophe pourra manger à sa faim sans risquer de voir son autre fourchette empruntée par un voisin.

Les opérations « prendre » et « vendre » effectuées sur les sémaphores, quant à elles, sont dites atomiques. Le noyau, pour sa part, ne saurait interrompre ces opérations en cours d’exécution. Cette particularité garantit que le processus qui prend ou vend un sémaphore ne se trouvera pas en situation de Deadlock, laquelle émane du noyau et non d’une erreur de conception du programme.

Le partage des ressources dans les environnements temps réel constitue une priorité. On rencontre essentiellement des environnements temps réel sur des systèmes embarqués (voitures, avions, missiles…). Or la fonction principale du système embarqué correspond le plus souvent à l’acquisition et au traitement d’informations issues de capteurs, puis ç l’envoi d’ordres à des périphériques. Le tout doit s’effectuer de manière à ce que l’on évite au maximum un plantage car les conséquences se révéleraient catastrophiques : ainsi, il est impensable que le pilote automatique d’un avion plante à cause d’une lecture de vitesse concurrente, par exemple !

Communication et synchronisation

Les boites aux lettres, les files de messages, les signaux, les tubes, les zones de mémoire partagée… Voici autant de procédés permettant la communication inter-processus dans les environnement multitâche temps réel.

La communication

Les tubes

2 processus travaillant en semi-parallélisme peuvent avoir besoin de se communiquer des informations. Comment réaliser cette opération sachant que chacun s’exécutent dans des espaces mémoires distincts ? Les tubes de communication sont des mécanismes qui permettent par exemple à un processus père de communiquer avec son processus fils ; en effet, les fils héritent des descripteurs des tubes appartenant à leur pères. Ce tube est composé de 2 entrées (lecture/écriture) et de 2 files de te/écriture) et de 2 files de types FIFO (First In First Out, les premier arrive est le premier sortis). On utilise un tube après l’avoir créé avec la commande appropriée, puis en appelant les commandes Read, Write et Close. L’utilisation des tubes est donc assez simple. Mais limitée aux seuls processus père/fils. Pour remédier à ce problème, il existe des tubes nommées. Apres leurs création (sur le shell avec mkfifo ou dans un programme C avec mknod), on les utilise comme des fichiers avec fopen, fread, fwrite et autres…  Par défaut, à la création des tubes, les opérations de lecture/écriture sont dites bloquantes : un processus qui écrit dans le tube va attendre qu’un autre processus soit à l’écoute de l’autre coté du tube pour continuer ses traitements. Là aussi on peut lors de la création des tubes spécifier dans les paramètres des fonctions que les opérations soient non bloquantes. La lecture de la documentation est vitale pour toutes ces opérations qui dépendent en partie du système que vous utiliserez.

Les files de messages

Appelées aussi « boites aux lettres » sont apparues avec l’UniX system V, mais existent aujourd’hui sur la plupart des UniX. Ces méthodes, avec la mémoire partagée et les sémaphores, font partie des IPC (InterProcess Communication) dont nous avons déjà parle précédemment. Les boites aux lettres s’utilisent d’une manière assez semblable aux tubes nommés. Tout d’abord, vous devez fixer une clé. Pour éviter que plusieurs clés identiques soient utilisées sur le système, on fait appel à la fonction ftok qui fabrique une clé IPC à partir d’un nom de fichier (en fait, sa place sur le disque = n°inode + device) et d’un caractère quelconque. Msgget vous permettra de créer la file avec cette clé et des permissions passées en paramètres. Les primitives Msgrcv et Msgsnd autorisent respectivement de lire et d’écrire des messages dans cette file. Les messages sont typés ce qui est l ‘avantage des files de messages par rapport aux tubes ; vous pouvez passer des structures contenant un ou plusieurs champs. Une fois ces messages reçus, vous pourrez les utiliser directement pour avoir accès aux champs de la structure. Msgctl permet de détruire la file, mais aussi d’en modifier certains paramètres.

La mémoire partagée

Vous allez dire « oui, c’est très bien, mais ce que je voulais c’est que 2 programmes ou processus puissent avoir accès à la même variable ». Une variable n’est qu’une zone mémoire et les IPC implémentent un mécanisme de mémoire partagée ! Là aussi, la séquence ftok puis shmget (SHared Memory GET) offre l’opportunité de créer un zone de mémoire partagée de taille variable (spécifiée en paramètre de shmget). Une nuance est toutefois à apporter concernant les files de messages : ici il faut attacher l’id envoyer par shmget à un zone mémoire. C’est le rôle de la primitive shmat qui prend en paramètre l’id renvoyé par shmget(), 0 (le système choisit l’adresse mémoire à partager) et les flags (permissions et paramètres divers).  Ce qui donne la séquence suivante pour la création (IPC_CREATE) d’une zone de 1024 octets :

Key_t key ;
Int shmid ;
Char *donnees ;
Key = ftok(“/etc/password”, ‘R’) ;
shmid = shmget(key, 1024, 0644 IPC_CREAT) ;

Donnees = smart(shmid, (void*)0, 0) ;

Une fois cet attachement réalisé, l’utilisation des zones de mémoires partagées est extrêmement simple. En voici un exemple :

Printf(“ Contenu partagé : %s\n, donnees) ;
Printf(“ Entrez une chaîne : ”) ;
Gets(donnees) ;

Shmdt permet de libérer le segment de mémoire attaché. Cependant, une fois détaché, le segment n’est pas supprimé : pour le supprimer, il faut écrire shmctl

Shmctl(shmid, IPC_RMID, NULL) ;

La synchronisation

Vous pouvez maintenant utilisez la même variable dans plusieurs programmes (utilisez IPC_PRIVATE au lieu de IPC_CREAT avec shmget et la même clé). Mais vos savez dorénavant que dans les systèmes multitâches et temps réel, les processus peuvent être interrompus… Comment s’assurer alors que la zone de mémoire partagée ne se retrouvera pas dans des situations d’accès concurrents ? Là encore, les sémaphores vont prouver leur utilité : les séquence ftok et smget permettent de déclarer un sémaphore. On utilise des ensembles de sémaphores, ce qui autorise à déclarer en une seule opération tous les sémaphores nécessaires à son programme. La primitive semop utilisera ensuite le numéro du sémaphore dans cet ensemble. Semop prend en paramètre l’id de l’ensemble, une structure sembuf contenant le numéro du sémaphore affecté par l’opération (2ème paramètre de la structure) puis des flags. Semctl est nécessaire pour initialiser le sémaphore ; en effet il est placé à 0 lors de la création, ce qui empêche toute opération. Semctl est utilisé également pour supprimer le sémaphore. Vous voulez visualisez les sémaphores, les zones de mémoire partagée, les files de messages de votre système ? Tapez sur la ligne de commande du shell, ipcs, pour visualiser le statut des IPC. 

Vous y retrouverez l’id, le propriétaire, les permission, la taille pour les zones de mémoire partagée et le nombre de sémaphores des ensembles. Pour en supprimer depuis le shell, tapez ipcrm en précisant le type de l’IPC à supprimer puis son id.

Encore de l’aide ?

La commande man semop décrit notamment la structure sembuf et la manière de prendre et vendre les sémaphores. Man ipc vous renverra à toute la documentation nécessaire.

Comment se servir des IPC

Parmi les nombreux mécanismes de communication inter-processus disponibles sous Linux, les IPC jouent un rôle à part. Ils sont toutefois intéressants dans de nombreux cas.

Linux dispose en standard d’une multitude de mécanismes permettant aux applications de communiquer entre elles. Il existe d’une part des formes très primitive de communication (signaux, tubes, etc.) et d’autre part, une gamme d’outils de haut niveau : sockets, RPC et bus Corba. Ces 3 dernières interfaces, orientées réseau, permettent de développer des applications distribuées. Les IPC fournissent une solution intermédiaire : plus puissants que les solutions de bas niveau, ils ignorent cependant totalement le réseau (cela ne fonctionne qu’en local), ce qui leur permet d’être extrêmement économique.

Que font les IPC ?

Les IPC permettent à plusieurs programmes de partager3 types d’objets : des sémaphores, des blocs mémoires et de files de messages. Ces objets n’apparaissent normalement pas dans l’arborescence, mais ils se comportent exactement comme tout le reste : ils possèdent un propriétaire, un groupe et des droits d’accès. 2 commandes du shell s’avèrent particulièrement utiles. La commande ipcs liste les objets IPC actuellement existants selon leur type. La seconde commande, ipcrm, permet de détruire un objet IPC. Cela s’avère utile surtout en cas de crash d’une application utilisant des IPC pour supprimer les objets susceptibles de rester dans le système.

IPC et IPC

Il existe en fait 2 API pour programmer avec les IPC. La premier remonte à Unix System V ; la seconde API fait partie du standard POSIX. Cependant, la première reste largement utilisée et, paradoxalement, fait même office de standard ; aussi, nous allons l’utiliser pour écrire une application client/serveur triviale. Un programme serveur rendra publique une chaîne de caractères grâce aux IPC, qu’un autre programme client pourra lire. Celui-ci aura par ailleurs l’opportunité d’envoyer un message au serveur lui demandant de quitter.

Le serveur

On commence par le code du serveur. Celui-ci va créer 2 objets IPC : une files de messages et une zone mémoire partagée. Il copiera dans celle-ci une chaîne de caractères puis attendra de recevoir un message lui ordonnant de quitter. Dans le système, chaque objet IPC possède une clef d’accès. L’appel système ftok nous permet d’obtenir celle-ci automatiquement. Sa syntaxe s’avère particulière : il prend en paramètre un nom de fichier et un octet permettant d’identifier l’application. Il calcule alors une clef unique, en utilisant cet identificateur, le contenu du fichier et quelques autres données. Une fois que nous avons la clef, nous pouvons créer des objets IPC. Nous créons d’abord la file de messages par l’appel msgget : on fournit en argument la clef et un paramètre qui indique les droits associés à cet objet. Le drapeau IPC_CREAT que nous y ajoutons signifie que nous voulons effectivement créer l’objet et non récupérer un objet déjà existant. De même, nous créons le segment de mémoire partagée par shmget. Cet fois, nous devons indiquer de plus la taille de ce segment.

Les files de messages

Les objets étant créés, nous pouvons les utiliser. Pour communiquer à l’aide de ces messages, nous devons définir un format de messages : c’est le rôle de notre structure message. Son premier champ doit impérativement etre de type long et indique le type du message. Puis viennent les données du message ; nous n’avons que le champ foobar que nous n’utiliserons pas. Nous voulons que le serveur attende la réception d’un message sur cette file ; cela se fait par l’appel msgrcv. Il prend en paramètre la file de messages, un tampon dans lequel sera stocké le message reçu, la taille du message, le type de message attendu et enfin un paramètre fixant le mode d’attente. Dans ce programme, nous n’utilisons pas le contenu du message car seul le fait de l’avoir reçu nous intéresse.

La mémoire partagée

L’utilisation de la mémoire partagée est plus simple : la fonction shmat() permet d’attacher le segment, c’est-à-dire d’obtenir un pointeur dessus. Son 2e paramètre permet de fixer l’adresse à laquelle le segment sera attaché ; nous mettons NULL, ce qui fera que le système choisira une adresse lui-même. Une fois ce pointeur obtenu, nous pouvons y placer des données.

Terminaison

Une fois le message attendu reçu, le serveur doit quitter en détruisant proprement les objets IPC créés. Pour cela, il faut d’abord détacher le segment par l’appel shmdt(), puis détruire réellement les 2 objets par des appels à msgctl() et shmctl() : le paramètre IPC_RMID indique que nous voulons effectivement détruire les objets.

Le client

Le code du client reste excessivement simple. On utilise encore msgget() et shmget(), mais, cette fois, on le fait sans IPC_CREAT pour indiquer que nous cherchons à accéder à des objets existants et non pour en créer des nouveaux. Ensuite, on attache la mémoire partagée (on utilise le paramètre SHM_RDONLY pour l’attacher en lecture seule) et on peut lire le contenu. Après utilisation, il convient bien sur de détacher le segment. Si le client a reçu un paramètre, il envoie alors au serveur un message de type QUIT pour lui demander de se terminer (la fonction msgsnd() se montre bien sur similaire à msgcrv() ). Il va sans dire que le client ne cherche pas à détruire les objets !

Démonstration

Compilez les 2 petits programmes suivants :

/*-------------------------------- CUT HERE ------------------------------ */

*/ serveur.c /*
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
struct message {
  
long mtype;
  
int foobar;
};
#define QUIT 100
int main(int agrc, char **argv) {
  
int msg_queue, shm;
  
key_t key;
   
struct message msg;

  
if(argc<2) {
    
printf(“utilisation : serveur message\n”);

    
return –l ;
  
key=ftok(“/bin/ls”, ‘M’);
  
msg_queue = msgget(key, S_IRWXU|S_IRWXG|S_IRWXO|IPC_CREAT);
  
sh_m = shmget(key, 256, S_IRWXU|S_IRWXG|S_IRWXO|IPC_CREAT);
  
message = shmat(sh_m, NULL, 0);
  
strcpy(message, argv[1]);
  
msgrcv(msg_queue, &msg, sizeof(struct message), QUIT, 0);
  
msgctl(msg_queue, IPC_RMID, NULL);
  
Shmdt(message);
  
Shmctl(sh_m, IPC_RMID, NULL);
  
Return 0;
}

/*-------------------------------- CUT HERE ------------------------------ */

*/ client.c /*
#include <sys/msg.h>
#include <sys/shm.h>
struct message {
  
long type;
  
int foobar;
};
#define QUIT 100
int main(int agrc, char **argv) {
  
int msg_queue, shm;
  
key_t key;
   
struct message msg = (QUIT, 0);
  
key=ftok(“/bin/ls”, ‘M’);
  
msg_queue = msgget(key, 0);
  
sh_m = shmget(key, 256, 0);
  
message = shmat(sh_m, NULL, SHM_RDONLY);
  
printf(“message = %s\n”, message);
  
shmdt(message);
  
if(argc>1)
    
msgsnd(msg_queue, &msg, sizeof(struct message), QUIT);
  
return 0;
}

/*--------------------------------- CUT HERE ------------------------------*/

lancez le serveur, par exemple:

./serveur  using_ipc

Utilisez la commande IPC. Vous verrez alors votre mémoire partagée dans une files de messages. Vous pouvez ensuite lancer le client ; il affichera correctement le message contenu dans la mémoire partagée. Essayez de lui passer un paramètre quelconque sur sa ligne de commande ; vous constaterez alors que le serveur quittera en détruisant les 2 objets, comme il se doit.

Un exemple concret : Le multitâche sous Linux

Linux est un système d’exploitation multitâche et multi-utilisateur. Pour cette raison, il dispose d’un puissant mécanisme d’allocation des ressources processeurs aux différents applications.

Nous allons parler de l’élément central du noyau Linux, celui autour duquel s’articule tout le reste. Il s’agit du mécanisme qui affecte les capacités de calcul de la machine aux tâches, ce qu’on appelle l’ordonnancement. Dans la littérature anglophone, il a pour nom scheduler ; comme aucun terme français satisfaisant ne s’est imposé, nous emprunterons donc ce vocable à nos amis d’outre-Manche.

Qu’est-ce qu’une tâche ?

Une originalité dans sa conception distingue Linux de la quasi-totalité des OS usuels : le noyau ne fait aucune différence entre les processus et les threads. Cela ne signifie pas qu’il émulerait en fait des threads par des processus, comme c’est parfois le cas sous BSD : en réalité, Linux ne reconnaît que les threads. Cependant, lorsque 2 d’entre eux s’exécutent dans des espaces de mémoire virtuelle différents, cette opération correspond selon l’utilisateur, à 2 processus distincts. Cette architecture, inspirée de certains systèmes expérimentaux récents, rend son scheduler très facile à comprendre. Sous Linux, un thread (nous emploierons désormais uniquement ce terme) se définit donc de manière simple : la valeur du compteur ordinal (c’est-à-dire l’adresse de l’instruction en langage machine exécutée), l’état du processeur (la valeur des registres, le pointeur de la pile), l’identité du thread (compte utilisateur sous lequel il s’exécute) et un contexte d’exécution associé (espace mémoire, E/S ouvertes, gestion des signaux…) qu’il peut éventuellement partager avec d’autres threads ; l’ensemble de ces définitions se trouvent dans la structure « task_struct », que vous avez tout loisir de consulter dans le fichier /usr/src/linux/include/sched.h. Par ailleurs un question se pose : Comment sont géré les threads ? Vous examinerez le code si vous le désirez dans usr/src :linux/kernel/sched.c. Globalement, une tâche est susceptible de se trouver dans différents états. « Prêt » constitue l’état de bas : il signifie que le thread demeure disponible et prêt à prendre la main sur un processeur. Il passera alors dans l’état « Running » qui désigne l’exécution effective du thread. A partir de là, 4 hypothèse se présentent. Le système peut décider de lui reprendre la main, auquel cas il redevient Prêt en attendant à nouveau son tour. Le thread a également la faculté de se mettre en attente d’un événement : il devient alors « Suspendu » et patiente le temps que l’événement en question le réveille pour repasser dans l’état Prêt. Si le thread se termine, il atteint un état final « Zombie », en prélude à sa destruction. Enfin un thread se voit parfois interrompu : cet état correspond au passage dans l’état « Arrêté ». Maintenant que nous connaissons les différents états d’un thread, étudions la méthode suivie par le scheduler pour piloter tout ce petit monde.

3 schedulers différents

En fait, Linux propose 3 classes de threads avec des politiques d’ordonnancement totalement différentes. La première, la classe FIFO se trouve réservée au threads qui nécessitent un ordonnancement particulier. Dans cette classe, chaque threads possèdent un niveau de priorité parmi les 256 disponibles. Le temps machine accordé aux threads n’a pas de limite : lorsqu’un thread s’exécute, il ne s’interrompt que si un autre thread FIFO de priorité supérieure devient prêt, lorsqu’il se suspend en attendant un événement ou alors lorsqu’il décide de passer volontairement la main. On voit que si un thread de cette classe possèdent la priorité maximale et ne se suspend jamais, il risque de monopoliser le processeur et personne ne pourra l’en empêcher. Pour cette raison, le lancement de threads dans cette classe reste interdit aux applications, sauf celles qui ont exécutées par le root ou expressément autorisées par celui-ci.

La classe Round-Robin

Lorsque aucun thread de la classe FIFO ne s’avère prêt, le noyau à la liberté de passer la main à un thread Round-Robin. Cette classe ressemble à la première à une différence près : la présence de quanta de temps. Lorsqu’un thread accède au processeur, il n’obtient qu’un bail à durée déterminée. Si, à l’issue de ce délai, il possède tjrs la main, il se voit interrompu manu militari par l’OS qui offre alors le processeur à un autre thread du meme niveau de priorité. Ainsi, tous les threads d’un niveau de priorité égal s’exécutent progressivement. De meme que FIFO, cette classe demeure réservée à des threads détenteurs de droits particuliers.

La classe Other

Other représente la classe par défaut qu’exploitent toutes les applications lancées par l’utilisateur ; à moins de le demander explicitement, un thread tourne systématiquement dans cette classe. Elle diffère radicalement des 2 précédentes et correspond +/- à la gestion des processus dans Unix. Les threads de cette classe auront éventuellement la main si aucun thread FIFO ni Round-Robin ne se trouvent prêt. Les quanta de temps existent ici aussi, mais les priorités deviennent ici dynamiques et le système les recalcule en permanence. Le principe s’avère simple : plus le thread passe de temps dans l’état prêt et plus sa priorité baisse. De même, plus il passe de temps en attente dans l’état suspendu, plus sa priorité s’accroît. Au final, les threads qui emploient très peu de processeur, donc qui passent leur temps à attendre, deviennent très prioritaires par rapport à ceux qui calculent intensivement. Cette situation risque de paraître étrange à première vue, mais s’explique très simplement. Unix, qui a introduit ce schéma, ainsi que Linux qui en hérite, ont été conçus comme des OS pour serveurs, chargés d’offrir des performances maximales au applications interactives et client/serveur. Ainsi une application telle que le serveur http Apache passe sont temps dans l’état suspendu, où elle attend la connexion d’un client. Lorsque celui-ci se présente enfin pour demander l’envoi d’une page Web, Apache, fort du temps passé dans l’état d’attente, sera très prioritaire ; aussi, il obtiendra immédiatement la main et pourra servir la requête extrêmement rapidement. En revanche, une application telle que POV verra progressivement sa priorité décroître : le système le traitera lorsqu’il n’aura rien de plus urgent à faire. Par conséquent, sa grande consommation de puissance de calcul ne pénalisera pas trop les autres applications.

Dans tous les cas, si vous cherchez des informations, consultez le man !!

top

Comment reconnaître la religion d’un logiciel ?

Une nouvelle guerre de religions modifie subrepticement notre monde contemporain. J’en suis convaincu depuis longtemps, et lorsque j’évoque cette idée, je m’aperçois qu’elle recueille aussitôt un consensus.

Ceci n’a pu vous échapper, le monde est aujourd’hui divisé en 2 : d’un coté les partisans du Macintosh, de l’autre ceux du PC sous DOS. Eh bien, je suis intimement persuadé que les Mac est catholique et le DOS protestant. Je dirais même plus. Le Mac est catholique contre-réformateur, empreint de la « ratio studiorum » des jésuites. Il est convivial, amical, conciliant, il explique pas à pas au fidèle la marche à suivre pour atteindre, sinon le royaume des cieux, du moins l’instant final de l’impression du document. Il est catéchistique, l’essence de la révélation est résolue en formules compréhensible et en icônes somptueuses. Tout le monde a le droit au salut.

Le DOS est protestant, voire carrément calviniste. Il prévoit une libre interprétation des Écritures, requiert des décisions tourmentées, impose une herméneutique subtile, garantit que le salut n’est pas à la portée de tous. Faire marcher le système nécessite un ensemble d’actes personnels interprétatifs du logiciel : seul, loin de la communauté baroques des joyeux drilles, l’utilisateur est enfermé dans son obsession intérieure.

On m’objectera que l’arrivée de Windows a rapproché l’univers du DOS de la tolérance contre-réformatrice du Mac. Rien de plus exact. Windows constitue un schisme de type anglican, de somptueuses cérémonies au sein des cathédrales, mais toujours la possibilité de revenir au DOS afin de modifier un tas de choses en se fondant sur d’étrange décisions : tout compte fait, les femmes et le gays pourront accéder au sacerdoce.

Naturellement catholicisme et protestantisme des 2 systèmes n’ont rien à voir avec les positions culturelles et religieuses des usagers. J’ai découvert l’autre jour que Franco Fortini, poète sévère et tourmenté, ennemi déclaré de la société du spectacle, est un adepte du Mac. Cela dit, au fil du temps, l’emploi d’un système plutôt qu’un autre ne cause pas de profonde modifications intérieures. Peut-on vraiment être à la fois adepte du DOS et catholique traditionaliste ? Par ailleurs, Céline aurait-il écrit avec Word, WordPerfect ou Wordstar ? Enfin, Descartes aurait-il programmé en Pascal ?

Et le langage machine, qui décide de notre destin en sous-main et pour n’importe quel environnement ? Eh bien, cela relève de l’Ancien Testament, du Talmud et de la Kabbale. Ah, encore et toujours le lobby juif.

Umberto ECO, Comment voyager avec un saumon ? (Texte de 1994)

Rajout d’Eberkut

Pour réactualiser ce texte qui bien qu’ironique comporte sans doute des vérités, je souhaite parler des UNIX.

On peut considérer la norme UNIX comme un système catholique corrompus digne des âges troubles et sombres tel que le Moyen-age.

Dans chaque système il y a une personne possédant des droits supérieurs aux autres simples utilisateurs et dont lui seul peut toucher ou créer de nouvelles Écritures et ainsi communiquer avec la source. Cependant, si quelqu’un trouve une faille dans ces Écritures, il peut contesté le précédant Pape et fonder un courant hérétique. Ainsi on retrouve la même situation qu’entre Pape d’Avignon, Pape de Rome, Empereur Germanique et Roi de France au 14ème siècle.

top

 


depuis le 30/09/2003