Sharky

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 19 octobre 2005

L'informatique c'est quand même un métier

Dans cette profession, on a (je suppose) tous le souvenir d'avoir dû répondre diplomatiquement au moins une fois : « Euh, non ça ne fonctionne pas vraiment comme ça. ».

Mais quand l'attaque vient de l'intérieur, quelqu'un qui est supposé au moins en connaître un peu sur les réseaux (vu qu'il héberge des sites ce serait bien d'avoir une vague idée de TCP/IP, des services réseaux, etc.), ça fait froid dans le dos.

Imaginons une appli web PHP qui pour obtenir des informations passe par un serveur applicatif, rien d'original et non je ne vais pas parler de « Web 2.0 », ça semble logique de spécifier dans le fichier de configuration de cette application l'adresse IP et le port à utiliser pour contacter le serveur applicatif (avec un port et 127.0.0.1 pour l'adresse comme défaut), bref rien d'extraordinaire.

Pourtant le sublime est toujours là où on ne l'attend pas :

Si je change l'adresse IP ou le port dans le fichier de configuration PHP le serveur applicatif continue à écouter sur 127.0.0.1:port ; comment faire pour modifier l'adresse et le port par défaut du serveur applicatif ?

  1. lire « les réseaux pour les nuls »
  2. assimiler la différence entre un client et un serveur
  3. taper nom-du-programme-serveur --help
  4. changer de métier et laisser l'informatique en paix ?

Bisounours d'or avec les félicitations du jury, mais le bisounours de platine me semble assez accessible avec un peu d'effort (courage !).

Crédit : merci MC pour ton excellente suggestion.

mardi 11 octobre 2005

Des threads mais pour quoi faire ?

Quand je suis tombé sur ce bout de code (qui était quand même un petit peu plus compliqué que celui qui figure ici) ma première réaction a été « Où est l'astuce ? » donc vérification soignée car la méfiance est de guise quand on ne comprend pas l'intérêt du code, souvent cela cache des effets de bord terrifiants ;-) . Et puis non je ne rêve pas... j'arrête le teasing là, admirez :

int done ;

void doSomethingUseful (void * scratch)
{
  // accès à une base de données, pas d'intérêt direct avec l'exemple
  ...
  done = 1 ;
}

void doSomethingUseless (void)
{
  done = 0 ;
  BeginThread (doSomethingUseful,NULL) ;
  while (! done)
    ;
}

int main (int argC, char * * argV)
{
  doSomethingUseless () ;

  return (0) ;
}

Oui vous lisez bien il s'agit de l'émulation d'un appel de fonction en passant par un thread ; mais le plus innovant réside dans l'attente active (while (! done)) qui permet de consommer pour rien du CPU mais bon aujourd'hui la puissance CPU... Les puristes (ou les méchants) ajouteront qu'il aurait été de bon goût de déclarer la variable done avec l'attribut volatile mais à ce stade il ne faut peut être pas être trop exigeant.

lundi 10 octobre 2005

Il est libre max

Tout le monde connaît l'histoire du polytechnicien et de l'eau froide ? Nan ? Bon....

Un polytechnicien dispose d'un réchaud à gaz, d'allumettes, d'une casserole et d'un robinet alimenté en eau. On lui demande de produire de l'eau chaude.

Le polytechnicien explique sa façon de procéder :

  • Je remplis la casserole avec de l'eau,
  • j'allume le réchaud à gaz avec les allumettes,
  • je pose la casserole sur le réchaud.

Au bout de quelques minutes l'eau sera chaude.

On lui demande maintenant de produire de l'eau froide et sa réponse est :

Facile ! On se ramène au problème précédent et on attend que l'eau refroidisse.

Après cette introduction, qui je pense illustre bien le manque d'innovation de la plupart des entreprises françaises du logiciel, voici quelques exemples (vécus) de perversion de code MS-SQL :

Vous voulez obtenir le dernier ID attribué en vue de créer une nouvelle ligne (en soi c'est déjà une mauvaise idée, mais bon) comment faire ?

select top 1 myid from mytable order by myid asc ;

Outre le fait que cette technique consistant à récupérer le dernier ID attribué soit incompatible avec un environnement multi-utilisateurs, remarquons que SQL dispose d'une construction standard et portable pour faire de même et accessoirement sans mettre à plat le serveur :

select max(myid) from mytable ;

Passer d'un sytème de type séquentiel indexé (ISAM) à une base de données relationnelle demande habituellement du travail et nécessite de repenser l'organisation des données ; mais certains considèrent que c'est une perte de temps :

clé primaire
inutile !
clé secondaire
voir ci-dessus
variables bindées dans les requêtes
c'est quoi ?
index adéquats
non surtout pas, ajouter un champ dans un index ça augmente le nombre d'I/O et ça ralentit

Non ce qui est vraiment important c'est d'émuler la logique de l'ISAM en SQL avec des perles comme :

select top 1 * from mytable where mykey > lastkey order by mykey asc ;

Qui a dit nextkey ? Et puis sur une table de 6 millions de lignes ça se sent à peine ;-) .

Edit : Le code ci-dessus est encore plus croustillant quand on sait qu'il n'y pas de clé primaire !

Ce « code » est bien sûr en « production », le jour où un DBA s'étonnera de la charge du serveur SQL et verra passer les requêtes...

dimanche 9 octobre 2005

Il me faut une section critique !

Créer des programmes utilisant des threads et des points de rendez-vous est l'une des choses les moins faciles de l'informatique. Comme les événements sont asynchrones il est très difficile de prévoir le comportement sauf à appliquer une méthodologie stricte et d'avoir aussi un peu d'expérience.

Certaines personnes n'ont pas le temps d'apprendre la théorie mais ont l'instinct de comprendre superficiellement les choses et de mal les appliquer.

Pour illustrer la théorie je vais prendre l'exemple classique de l'incrémentation d'une variable en C :


# include <stdio.h>

int var = 0 ;

void inc (void)
{
  ++ var ;
}

int main (int argC, char * * argV)
{
  int i ;

  for (i = 0 ; i < 100000 : i ++)
     inc () ;

  printf ("Total : %d\n",var) ;
  return (0) ;
}

Ce code anodin va afficher 100000. Maintenant en utilisant deux threads comme ceci (code simplifié) :


# include <stdio.h>

int var = 0 ;

void inc (void)
{
  ++ var ;
}

void thread1 (void * scratch)
{
  int i ;

  for (i = 0 ; i < 50000 : i ++)
     inc () ;
}

void thread2 (void * scratch)
{
  int i ;

  for (i = 0 ; i < 50000 : i ++)
     inc () ;
}

int main (int argC, char * * argV)
{
  CreateThread (thread1, NULL) ;
  CreateThread (thread2, NULL) ;
  Sleep (5) ;

  printf ("Total : %d\n",var) ;
  return (0) ;
}

Nous allons avoir deux threads qui vont procéder à l'incrémentation de la variable donc 2 * 50000 = 100000. Et bien non le résultat sera inférieur à 100000, pour comprendre pourquoi il faut aller voir au niveau du code en assembleur i386 :


inc:
  mov ebx,[var]
  inc ebx
  mov [var],ebx
  ret

Pour incrémenter la variable il faut (en simplifiant) trois opérations élémentaires, donc entre le moment où la variable est chargée dans le registre ebx et le moment où la valeur est réactualisée il se peut très bien que dans l'intervalle le deuxième thread ait fait les mêmes opérations mais qui seront écrasées par le mov [var],ebx du premier thread.

Il faut donc que la variable var ne puisse pas être modifiée durant les phases de chargement dans le registre, incrémentation du registre, recopie du registre dans la variable, pour ce faire il existe un objet appelé mutex ou section critique qui permet de garantir que les opérations ne seront pas interrompues pas un autre thread, ce qui donne :


CRITICAL_SECTION cs ;

void inc (void)
{
  EnterCriticalSection (& cs) ;
  ++ var ;
  LeaveCriticalSection (& cs) ;
}

Donc un objet section critique (cs) commun aux deux threads et le compte sera bien de 100000 mais comme il ne faut pas freiner l'innovation je laisse à votre sagacité le code suivant qui est en production sur des serveurs (disclaimer : je n'ai pas pondu ce code).


...
void inc (void)
{
  CRITICAL_SECTION cs ;

  InitializeCriticalSection (& cs) ;
  EnterCriticalSection (& cs) ;
  ++ var ;
  LeaveCriticalSection (& cs) ;
}
...

Pas mal, non ?

Depuis le 1/1/2005...

  requins ont été tués dans le monde. Ces prédateurs indispensables au fonctionnement de l'écosystème récifal et océanique sont menacés d'extinction.

Aidez-nous à faire des 4,5 millions de km2 de superficie maritime de la Polynésie française une réserve protégée pour les requins. Mauruuru.