<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
    <meta name="generator" content="HTML Tidy for Linux/x86 (vers 1st February 2002), see www.w3.org">
    <!-- this stylesheet will later on be added by lfparser automatically: -->
<style type="text/css">
<!--
  pre { font-family:monospace,Courier }
  pre.code { font-family:monospace,Courier;background-color:#aedbe8;border-color:#aedbe8 }

  p.code { width:80%; alignment:center; background-color:#aedbe8;
        border-style:none; border-width:medium; border-color:#aedbe8;
        padding:0.1cm ; text-align:left }
-->
</style>
<title></title>
</head>


  <body>
    <h1>Programmation en temps partag� - Files d'attente de messages (2)</h1>

    <h4>ArticleCategory:</h4>
    SoftwareDevelopment 

    <h4>AuthorImage:</h4>
    <img src="../../common/images/LeonardoGiordani.jpg" width="85" height="109" alt="[Leonardo]"> 

    <h4>TranslationInfo:</h4>

    <p>original in en: <a href="mailto:leo.giordani(at)libero.it">Leonardo Giordani</a></p>
    <p>en to fr: <a href="http://tontonpol.dyndns.org">Paul Delannoy</a></p>

    <h4>AboutTheAuthor:</h4>
<P> Etudiant ing�nieur en t�l�communications � l'�cole Polytechnique
de Milan, il travaille comme administrateur r�seau et s'int�resse � la 
programmation (surtout en langage assembleur et en C/C++).
Depuis 1999 il ne travaille que sous Linux/Unix.
    
    <h4>Abstract:</h4>
Cette s�rie d'articles se propose d'initier le lecteur au concept de <i>multit�che</i>
et � sa mise en oeuvre dans le syst�me d'exploitation Linux. Nous partirons des concepts
th�oriques de base concernant le <i>multit�che</i> pour aboutir � l'�criture compl�te d'une
application illustrant la communication entre processus, avec un protocole simple mais efficace.
<p>
Pour comprendre l'article il faudrait avoir :
<ul>
<li>Une connaissance minimale du shell
<li>Une connaissance standard du langage C (syntaxe, boucles, librairies)
</ul>
    Toute r�f�rence � des pages de manuel est plac�e entre parenth�ses
    apr�s le nom de la commande concern�e. Toute fonction de la <i>glibc</i> est document�e
    par la commande "info Libc". 

<h4>ArticleIllustration:</h4>
<img src="../../common/images/illustration272.jpg" width="300" height="180" alt="[run in paralell]">



    <h4>ArticleBody:</h4>

    <h3>Introduction</h3>
    Nous avons appris dans l'article pr�c�dent � synchroniser et � faire
    travailler ensemble deux processus (ou plus) en utilisant des files d'attente
    de messages. Nous allons maintenant un peu plus loin, en commen�ant � cr�er notre propre 
    protocole d'�change de messages. 

    <p>Nous avons pr�c�demment d�fini un protocole comme un ensemble de r�gles 
    qui permettent le dialogue entre deux personnes ou machines, m�me si elles 
    sont diff�rentes. L'usage de la langue anglaise par exemple est un 
    protocole, puisqu'il me permet de parler � mes lecteurs indiens (qui ont 
    toujours �t� tr�s int�ress�s par ce que j'�cris). Pour parler de choses 
    plus proches de Linux, si vous recompilez votre noyau (pas de panique, 
    ce n'est pas si compliqu�), vous remarquerez sans doute la section 
    Networking (R�seau), qui permet de faire comprendre � votre noyau diff�rents 
    protocoles r�seau, comme TCP/IP.</p>

    <p>Afin de cr�er un protocole, nous allons commencer par d�finir le type 
    d'application que nous envisageons. Notre exemple sera un simulateur de 
    commutateur t�l�phonique. Un processus principal simulera le commutateur 
    lui-m�me, et ses processus fils simuleront les actions des utilisateurs : chaque 
    utilisateur devra pouvoir �changer des messages avec un autre au travers du 
    commutateur.</p>

    <p>Le protocole doit g�rer trois situations diff�rentes : la naissance 
    d'un utilisateur (i.e. l'utilisateur existe et se connecte), le travail courant d'un 
    utilisateur, et la disparition d'un utilisateur (il n'est plus connect�).
    Abordons les trois cas :</p>

    <p>Lorsqu'un utilisateur se connecte au syst�me, il cr�e sa propre file 
    d'attente de messages (n'oublions pas : c'est un processus), dont les
    identifiants doivent �tre envoy�s au commutateur afin que celui-ci sache comment atteindre cet 
    utilisateur. Il a le temps d'initialiser des structures de donn�es 
    si n�cessaire. Il re�oit du commutateur 
    l'identifiant d'une file d'attente de messages, dans laquelle il pourra �crire les 
    messages d�livr�s � d'autres utilisateurs par le commutateur lui-m�me.</p>

    <p>L'utilisateur peut donc envoyer et recevoir des messages. Lorsqu'il �met un 
    message vers un autre utilisateur, deux cas sont possibles : le destinataire est 
    connect� ou non. Nous d�cidons que dans les deux cas, un accus� de r�ception
    sera envoy� � l'exp�diteur, afin qu'il sache ce qu'il advient de son message. Ceci peut 
    �tre accompli par le commutateur lui-m�me, sans que le destinataire n'ait 
    � faire quoi que ce soit.</p>

    <p>Lorsqu'un utilisateur se d�connecte, il doit en informer le commutateur, mais 
    aucune autre action n'est n�cessaire. Le m�tacode d�crivant cette fa�on de
    faire est le suivant</p>
    
<pre class="code">
/* Naissance */
create_queue
init
send_alive
send_queue_id
get_switch_queue_id

/* Travail courant */
while(!leaving){
 receive_all
 if(&lt;send condition&gt;){
  send_message
 }
 if(&lt;leave condition&gt;){
  leaving = 1
 }
}

/* Disparition */
send_dead
</pre>

    <p>Maintenant il faut d�finir le comportement de notre commutateur 
    t�l�phonique : lorsqu'un utilisateur se connecte il nous envoie l'identifiant 
    de sa file d'attente de messages; nous devons conserver cet identifiant, afin de 
    faire parvenir � cet utilisateur les messages qui lui sont destin�s, et 
    nous devons r�pondre en lui fournissant l'identifiant d'une file d'attente dans 
    laquelle il peut �crire les messages destin�s � d'autres 
    utilisateurs. Nous devons ensuite analyser les messages en attente et
    v�rifier que les destinataires soient vivants : si le 
    destinataire est connect� nous d�livrons le message, sinon
    nous l'ignorons; dans les deux 
    cas nous informons l'exp�diteur. Lors de la disparition d'un utilisateur, nous supprimons 
    simplement l'identifiant de sa file d'attente, il devient ainsi injoignable.</p>

    <p>A nouveau, le m�tacode est le suivant :</p>
<pre class="code">
while(1){
 /* Nouvel utilisateur */
 if (&lt;birth of a user&gt;){
  get_queue_id
  send switch_queue_id  
 }

 /* Disparition utilisateur */
 if (&lt;death of a user&gt;){
  remove_user
 }

 /* Distribution des messages */
 check_message
 if (&lt;user alive&gt;){
  send_message
  ack_sender_ok
 }
 else{
  ack_sender_error
 }
}
</pre>

    <h3>Gestion des erreurs</h3>
    G�rer les erreurs est une des choses les plus importantes et
    difficiles, dans la conduite d'un projet. De plus, un bon 
    ensemble de routines de gestion d'erreurs, peut 
    repr�senter la moiti� des lignes de code n�cessaires au projet. 
    Nous n'allons pas expliquer ici comment d�velopper un bon 
    syst�me d'analyse des erreurs, c'est un sujet trop cons�quent, mais � 
    partir de maintenant je vais toujours tester les conditions 
    d'erreur. Une bonne introduction � ces questions d'analyse d'erreurs est 
    la lecture du manuel de la biblioth�que glibc (www.gnu.org) mais, si vous �tes 
    int�ress�s, j'�crirai un article sur le sujet. 

    <h3>Mise en oeuvre du protocole - Couche 1</h3>
    Notre petit protocole aura deux couches : la premi�re (la plus basse) est 
    constitu�e des fonctions de gestion des files d'attente, de pr�paration et d'envoi 
    des messages, alors que la plus haute met en oeuvre le protocole gr�ce � des 
    fonctions ressemblant au m�tacode utilis� pour d�crire le comportement du 
    commutateur et des utilisateurs. 

    <p>La toute premi�re chose � faire est de d�finir la structure des 
    messages, en utilisant le prototype msgbuf fourni par le noyau</p>
<pre class="code">
typedef struct
{
  int service;
  int sender;
  int receiver;
  int data;
} messg_t;

typedef struct
{
  long mtype; /* Type du message */
  messg_t message;
} mymsgbuf_t;
</pre>

    <p>Il y a ici quelque chose de g�n�ral que nous pourrons �tendre plus tard : 
    les champs exp�diteur et destinataire contiennent un identifiant d'utilisateur et 
    le champ donn�es contient des donn�es quelconques, tandis que le 
    champ service permet d'adresser une demande particuli�re au commutateur. 
    Par exemple il pourrait y avoir deux services pr�vus : l'un pour la 
    distribution imm�diate d'un message, l'autre pour la distribution diff�r�e, le 
    champ data devant alors contenir le d�lai en secondes. Ce n'est qu'un exemple, 
    mais il permet de comprendre que le champ service offre de 
    nombreuses possibilit�s.</p>

    <p>Nous pouvons maintenant d�finir quelques fonctions pour traiter nos 
    structures de donn�es, particuli�rement pour lire ou �crire les valeurs 
    des champs des messages. Ce sont plus ou moins toujours les m�mes, aussi 
    je ne vous en propose que deux, vous trouverez les autres dans les fichiers *.h</p>

<pre class="code">
void set_sender(mymsgbuf_t * buf, int sender)
{
  buf-&gt;message.sender = sender;
}

int get_sender(mymsgbuf_t * buf)
{
  return(buf-&gt;message.sender);
}
</pre>

    <p>Le but de telles fonctions n'est certes pas de compresser le code 
    (elles ne contiennent qu'une seule ligne !) : elles ont pour but de 
    conserver la d�finition du protocole proche d'un langage humain, et donc 
    d'�tre plus simples � utiliser.</p>

    <p>Ecrivons maintenant les fonctions destin�es � g�n�rer des cl�s IPC, cr�er et 
    supprimer des files d'attente de messages, envoyer et recevoir des messages : 
    construire une cl� IPC est simplement ceci :</p>

<pre class="code">
key_t build_key(char c)
{
  key_t key;
  key = ftok(".", c);
  return(key);
}
</pre>

    <p>La fonction qui cr�e une file d'attente </p>
<pre class="code">
int create_queue(key_t key)
{
  int qid;
  
  if((qid = msgget(key, IPC_CREAT | 0660)) == -1){
    perror("msgget");
    exit(1);
  }
  
  return(qid);
}
</pre>

    <p>comme vous le voyez la gestion d'erreurs est ici extr�mement simple.
    La fonction suivante supprime une file d'attente</p>
<pre class="code">
int remove_queue(int qid)
{
  if(msgctl(qid, IPC_RMID, 0) == -1)
  {
    perror("msgctl");
    exit(1);
  }
  return(0);
}
</pre>

    <p>Et enfin les fonctions de r�ception et d'envoi de messages : pour nous, 
    �mettre un message c'est l'�crire dans une file d'attente sp�cifique, celle qui 
    nous a �t� indiqu�e par le commutateur.</p>
<pre class="code">
int send_message(int qid, mymsgbuf_t *qbuf)
{
  int result, lenght;
  lenght = sizeof(mymsgbuf_t) - sizeof(long);
  if ((result = msgsnd(qid, qbuf, lenght, 0)) == -1)
   {
     perror("msgsnd");
    exit(1);
    }
  
  return(result);
}

int receive_message(int qid, long type, mymsgbuf_t *qbuf)
{
  int result, length;
  length = sizeof(mymsgbuf_t) - sizeof(long);
  
  if((result = msgrcv(qid, (struct msgbuf *)qbuf, length, type, IPC_NOWAIT)) == -1)
   {
    if(errno == ENOMSG){
      return(0);
    }
    else
    {
      perror("msgrcv");
      exit(1);
    }
  }
  
  return(result);
}
</pre>

    <p>C'est tout. Vous trouverez des instructions compl�mentaires dans le 
    fichier
    <a href="../../common/src/article296/">layer1.h</a> : 
    essayez de cr�er un programme (par ex. celui du dernier article) avec elles. 
    Dans le prochain article nous aborderons la couche 2 du protocole et 
    sa mise en oeuvre.</p>

    <h3>Lectures recommand�es</h3>

    <ul>
      <li>Silberschatz, Galvin, Gagne, <b>Operating System Concepts
      - Sixth Edition</b>, Wiley&amp;Sons, 2001</li>

      <li>Tanenbaum, WoodHull, <b>Operating Systems: Design and
      Implementation - Second Edition</b>, Prentice Hall, 2000</li>

      <li>Stallings, <b>Operating Systems - Fourth Edition</b>,
      Prentice Hall, 2002</li>

      <li>Bovet, Cesati, <b>Understanding the Linux Kernel</b>,
      O'Reilly, 2000</li>

      <li>Le guide du programmeur Linux :
      <b>http://www.tldp.org/LDP/lpg/index.html</b></li>

      <li>Linux Kernel 2.4 Internals
      <b>http://www.tldp.org/LDP/lki/lki-5.html</b></li>

      <li>Site Web du canal IRC #kernelnewbies
      <b>http://www.kernelnewbies.org/</b></li>

      <li>
        La FAQ de la liste de diffusion "linux-kernel"
        <b>http://www.tux.org/lkml/</b> 
      </li>
    </ul>
        <p>Comme toujours vous pouvez m'adresser commentaires, corrections, questions 
	� mon adresse �lectronique (leo.giordani(at)libero.it) ou par 
	la page de discussion. S.V.P �crivez en anglais, allemand ou italien.</p>

  </body>
</html>