Gestion de l’horloge Linux

 Trucs et astuces techniques  Commentaires fermés sur Gestion de l’horloge Linux
Sep 272012
 

Il y a deux horloges principales dans un système Linux :

L’horloge machine : Il s’agit de l’horloge utilisant un quartz dédié (généralement identique à ceux d’une montre bracelet) Cette horloge fonctionne d’une manière indépendante des programmes et même lorsque la machine est éteinte. elle a une précision de l’ordre de la seconde.
A divers endroits cette horloge est communément appelée l’horloge machine (« hardware clock »), l’heure temps réelle (« real time clock »), le RTC, l’horloge BIOS ou l’horloge CMOS.

L’horloge système : C’est l’horloge gérée par le noyau Linux et contrôlée par un timer. (Attention, Linux peut utiliser plusieurs clocksource différentes selon la configuration)
L’heure système est le nombre de secondes écoulées depuis Epoch (le 1er janvier 1970 00:00:00 UTC)
L’heure système est initialisée avec la valeur de l’horloge machine au démarrage de Linux, l’horloge machine n’est ensuite plus utilisée.
Le timestamp unix a la structure suivante :

struct timeval {
   time_t      tv_sec;     /* secondes */
   suseconds_t tv_usec;    /* microsecondes */
};

On peut remarquer que le timestamp est toujours UTC. C’est seulement au moment de l’affichage que l’on calcule la date et l’heure locale en tenant compte de la timezone (fuseau horaire plus ajustement heure d’été/d’hiver). Attention la timezone est une variable d’environnement et un ensemble de règles du paquet tzdata. Il existe une structure timezone historique qui est obsolète

struct timezone {
int tz_minuteswest; /* minutes à l'ouest de Greenwich  */
int tz_dsttime;     /* type de changement horaire      */
};

l’argument tz doit en général être NULL. Le champ tz_dsttime n’a jamais été utilisé sous Linux, il n’a jamais été géré, et ne le sera jamais par la libc ou glibc.

la fonction gettimeofday() retourne le timestamp.

Historiquement le timestamp est 32bits () mais sur debian squeeze 64 bits

sizeof time_t = 8
sizeof suseconds_t = 8

Ce sont donc « déjà » des entiers 64 bits signés.

Pour corriger la dérive de l’horloge, il existe une fonction adjtime() qui ajuste graduellement l’horloge système en l’accélérant ou la ralentissant à un rythme constant de 0.5ms/s.

int adjtime(const struct timeval *delta, struct timeval *olddelta);

si delta est NULL olddelta est peuplé avec le temps à ajuster restant.

La commande ntpdate utilise adjtime() si l’écart entre l’horloge locale et l’horloge de référence est inférieur à +-128 ms. Cela permet de recaler l’horloge tout en gardant un timestamp monotone. (qui ne reviens jamais en arrière et sans saut dans le futur) Au delà, comme il faudrait plus de 5 minutes pour recaler l’horloge , celle ci est réglée de façon abrupte.
Tout nouvel appel à adjtime avec un décalage non NULL conserve le décalage déjà effectué mais fait perdre le décalage restant à faire.
L’utilisation de ntpdate pendant la période d’ajustement induit de légères erreurs de mesure du delta à cause du fait que l’horloge est déjà en cours d’accélération ou de ralentissement. Il vaut donc mieux attendre que adjtime ait fini avant de faire à nouveau appel à ntpdate.

Enfin il exite une fonction permettant de modifier de façon permanente la derive de l’horloge soft.
by adjusting the rate at which the system time is advanced with each timer interrupt, using adjtimex(8).

Keywords for Requirement Levels

 Sans catégorie  Commentaires fermés sur Keywords for Requirement Levels
Août 292012
 

Il est parfois bon de rappeler des définitions évidentes histoire que les choses soient bien claires

Extrait de la RFC 2119

  • MUST This word, or the terms « REQUIRED » or « SHALL », mean that the definition is an absolute requirement of the specification.
  • MUST NOT This phrase, or the phrase « SHALL NOT », mean that the definition is an absolute prohibition of the specification.
  • SHOULD This word, or the adjective « RECOMMENDED », mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
  • SHOULD NOT This phrase, or the phrase « NOT RECOMMENDED » mean that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
  • MAY This word, or the adjective « OPTIONAL », mean that an item is truly optional. One vendor may choose to include the item because a particular marketplace requires it or because the vendor feels that it enhances the product while another vendor may omit the same item. An implementation which does not include a particular option MUST be prepared to interoperate with another implementation which does include the option, though perhaps with reduced functionality. In the same vein an implementation which does include a particular option MUST be prepared to interoperate with another implementation which does not include the option (except, of course, for the feature the option provides.)

Generateur de sparse-file

 Trucs et astuces techniques  Commentaires fermés sur Generateur de sparse-file
Mai 192011
 
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <ctype.h>


const char * strtera="To";
const char * strgiga="Go";
const char * strmega="Mo";
const char * strkilo="Ko";
const char * stroctets="octets";
const char * strunkwn="inconnu";

#define TERA 1099511627776
#define GIGA 1073741824
#define MEGA 1048576
#define KILO 1024

const long tera = TERA;
const long giga = GIGA;
const long mega = MEGA;
const long kilo = KILO;

long unit=0;

char buffer[4096];
unsigned long size=0;
const char * filename = NULL;
int justdoit = false;



const char* strofunit(const long u)
{   switch (u)
    {   case TERA : return strtera;
        case GIGA : return strgiga;
        case MEGA : return strgiga;
        case KILO : return strkilo;
        case 1 :    return stroctets;
        default : return strunkwn;
    }
}

void usage(void)
{
    fprintf(stderr, "\n");
    fprintf(stderr, "usage :  zerogen -f filename -s size \n");
    fprintf(stderr, "    size peut tre specit avec une unit comme '12G' or '125M' \n");
    fprintf(stderr, "    Si l'unit n'est pas spcifie avec size, elle peut l'etre avec -u (mais pas les deux)\n");
    fprintf(stderr, "    En l'absence de toute unit, la taille est considre tre en octets.\n");
    fprintf(stderr, "    La taille ne doit en aucun cas depasser 8To (comportement imprvisible dans ce cas).\n");
    fprintf(stderr, "    \n");
    fprintf(stderr, "    zerogen ne modifiera pas les fichiers qui existent dja\n");
    fprintf(stderr, "    \n");
    fprintf(stderr, "    exemple :  \n");
    fprintf(stderr, "                zerogen -f disk.img -s 20G\n");
    fprintf(stderr, "                zerogen -f disk2.img -s 20 -u G\n");
    fprintf(stderr, "    \n");

}    


int getarg(int argc, char* argv[] )
{   
    int c;
    int tmp;
    char tmp2;

    if (!argc) return false;

    while ( (c = getopt(argc, argv, "jS:s:u:f:")) != EOF )
    {   
        switch(c)
        {   case 'j' :  justdoit=true;
                        break;
    
            case 's' :
            case 'S' :  if (size)
                        {   printf("Erreur : la taille est specifie plusieurs fois\n");
                            return false;                        
                        }
                        tmp = sscanf(optarg,"%lu%c", &size, &tmp2);
                        if ( ! tmp ) return false;
                        if ( tmp == 2 )
                        switch ( toupper(tmp2) )
                        {   case 'T' : unit = tera; break;
                            case 'G' : unit = giga; break;
                            case 'M' : unit = mega; break;
                            case 'K' : unit = kilo; break;
                            case 'O' : unit = 1; break;
                        }                
                        break;

            case 'u' :  if (unit)
                        {   printf("Erreur : Conflit d'units\n");
                            return false;
                        }
                        switch ( toupper(optarg[0]) )
                        {   case 'T' : unit = tera; break;
                            case 'G' : unit = giga; break;
                            case 'M' : unit = mega; break;
                            case 'K' : unit = kilo; break;
                            case 'O' : unit = 1; break;
                        }
                        break;

            case 'f' :  filename = optarg;
                        break;

            case '?' :  perror("Option inconnue\n");
                        return false;
        }
    }

    

    if ( !filename )
    {   fprintf(stderr,"fichier non specifie : utilisez l'option -f \"fichier\"\n");
        return false;
    }

    if ( ! size )
    {   fprintf(stderr, "taille non spcifie : utiliser l'option -s taille \n");
        return false;
    }


    if ( !unit )
    {   fprintf(stderr,"unit non specifie : On considere que la taille specifiee est en octets\n");
        unit=1;
    }
    
    if ( !unit )
    {   fprintf(stderr,"unit non specifie : utilisez l'option -uO -uK -uM -uG -uT pour specifier octets, kilo, mega, giga\n");
        return false;
    }
    
    return true;
                        
}


int main (int argc, char* argv[])
{   FILE* f;
    int w;
    assert( sizeof(long) == 8 );
    assert( sizeof(size_t) == 8);

    memset(buffer, 0, 4096);

    if ( ! getarg(argc, argv) ) 
    {   usage();
        return 1;
    }

    f = fopen(filename, "r");
    if (f && !justdoit)
    {   fclose(f);
        fprintf(stderr,"Le fichier existe dj specifiez -j pour passer outre (-j comme just do it)\n");        
        return 1;
    }

    if (f && !justdoit) fclose(f);
    
    printf("Cration du fichier [%s] de taille %lu %s\n", filename, size, strofunit(unit));
    f = fopen(filename, "w");
    assert(f);
    
    fseek(f, (size*unit)-1 , SEEK_SET);
    w=fwrite(buffer, 1, 1, f);
    assert(w);
    fclose (f);
    return 0;
}



Mar 022010
 

pour modéliser la dispertion, on aura souvent besoin d’un generateur alléatoire gaussien qui concentre la majorité des valeurs sur la moyenne.

ci dessous une librairie de mon cru permettant d’obtennir ce resultat:

#include "pgl_random.hpp"

#include <stdlib.h>
#include <math.h>


static const int precision = 1000000000;  

double pgl_random(void)
{   return (double)(rand()%precision)/precision;
}

double pgl_random(const double max)
{   return max*pgl_random();
}

double pgl_random(const double min, const double max)
{   return min + pgl_random(max - min);
}

double pgl_random_gauss(void)
{   //cette fonction génère 2 nombres alléatoires
    //le second est stocké pour le prochain appel
        
    //les valeurs de retour sont centrées sur 0, equitablement réparties dessus dessous 
    //les valeurs sont essentiellement entre -3 et +3 

    double x1;
    double x2;
    double w;
    double y1;
    static double y2;
    static bool cache = false;

    if ( cache )
    {   cache=false;
        return y2;
    }

    do 
    {   x1 = pgl_random(-1, 1);
        x2 = pgl_random(-1, 1);
        w = x1 * x1 + x2 * x2;  //somme des carrés
    } while ( w >= 1.0 ); 

    w = sqrt( (-2.0 * log( w ) ) / w ); //attention log() est la fonction du log népérien ln. dans math.h le log classique est "log10"
    y1 = x1 * w;
    y2 = x2 * w;    //cette valeur est mise en cache pour le prochain appel
    cache = true;
    return y1;    
}



double pgl_random_gauss(const double centre, const double ecart)
{   return centre + ecart * pgl_random_gauss();
}

double pgl_random_gauss(const double ecart, const double min, const double max)
{   double centre = ( max - min ) /2 ;
    double r;
    do
    {
        r = centre + ecart * pgl_random_gauss();
    }   while ( r < min || r > max )
    return r;
}



Optimisation des sauts conditionnels

 Trucs et astuces techniques  Commentaires fermés sur Optimisation des sauts conditionnels
Mar 012010
 

Dans bien des cas, les opérations mathematiques sont plus rapides que les sauts conditionnels
exemple avec ces deux fonctions.

double foo(bool a, double b)
{   return a*b;
}

double bar(bool a, double b)
{   if ( a == true ) return b;
    else return 0;
}

la fonction foo est plus rapide que bar, ou en tout cas est plus facile à optimiser si on l’apelle un grand nombre de fois.

exemple pour 10 millions d’appels sur un processeur Intel(R) Core(TM)2 6300@1.86GHz

//sans optimisation
FOO temps  : 0.191716 sec
FOO temps  : 0.191693 sec
BAR temps  : 0.208786 sec
BAR temps  : 0.208999 sec
ecart : 8%

//optimisation O1
FOO temps  : 0.123257 sec
FOO temps  : 0.122524 sec
BAR temps  : 0.130222 sec
BAR temps  : 0.129852 sec
ecart : 5%

//optimisation O2
FOO temps  : 0.024811 sec
FOO temps  : 0.025352 sec
BAR temps  : 0.072986 sec
BAR temps  : 0.073545 sec
ecart : 66%


Remarque importante concernant l’optimisation :
Dans un programme digne de ce nom, il est inutile d’optimiser ce qui n’en a pas besoin, surtout si ça nuit à la lisibilité du code.
Généralement, les gains de vitesse proviendront de la complexité de l’algorythme utilisé plutôt que de la qualité du codage.
Généralement 90% des gains de vitesse possibles peuvent être obtennus en optimisant moins de 10% du code.
Aussi, ne réservez ce genre d’astuces qu’a de très petites portions de code et vérifiez que vos modifications ont un impact positif et suffisant pour justifier leur emploi.

Monitoring CPU temps réel

 Trucs et astuces techniques  Commentaires fermés sur Monitoring CPU temps réel
Fév 082010
 

voici un petit bout de code C qui mesure l’activité CPU d’une machine et envoie les statistiques à une machine de monitoring

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE 1024


enum { CURRENT , OLD , DIFF , PERCENT , TABSIZE };
enum { USR, NICE, SYSTEM , IDLE , IOWAIT , IRQ , SOFTIRQ , TOTAL , NBCOLS };
enum { ERROR, OK };

unsigned long long int table[NBCOLS][TABSIZE];

void setcolor(int f, int b) { printf("%c[0;%d;%dm",27,30+f,40+b); }
void setfcolor(int f) { printf("%c[%d;%dm", 27, f/8, 30+(f%8) ); }
void setbcolor(int b) { printf("%c[%d;%dm", 27, b/8, 40+(b%8) ); }
void setdefault(void) { printf("%c[m",27); }
char thishostname[BUFSIZE];



int s;
struct sockaddr_in srvAddr;
struct hostent *h;

void recuphostname(void)
{	FILE * f;
	f=fopen("/etc/hostname" ,"r");
	fgets(thishostname , BUFSIZE-1 , f);
	if ( thishostname[strlen(thishostname)-1] == '\n') thishostname[strlen(thishostname)-1] ='\0';
	fclose(f);
}

int recup(void)
{   FILE * f;
    char cpuline[BUFSIZE];
    unsigned int i;
    
    f=fopen("/proc/stat" ,"r");
    if (!f) return ERROR;    
    fgets(cpuline , BUFSIZE-1 , f);
    fclose(f);

    for ( i=0 ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[USR][CURRENT] )        );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[NICE][CURRENT] )       );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[SYSTEM][CURRENT] )     );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[IDLE][CURRENT] )       );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[IOWAIT][CURRENT] )     );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[IRQ][CURRENT] )        );
    for ( i++ ; cpuline[i] != ' ' && i<BUFSIZE ; i++ ); i++; 
    sscanf(cpuline+i, "%llu", &( table[SOFTIRQ][CURRENT] )    );
    return OK;
}


void affiche(void)
{   printf("USR=%03lu ",     table[USR][PERCENT]);
    printf("NICE=%03lu ",    table[NICE][PERCENT]);
    printf("SYSTEM=%03lu ",  table[SYSTEM][PERCENT]);
    printf("IDLE=%03lu ",    table[IDLE][PERCENT]);
    printf("IOWAIT=%03lu ",  table[IOWAIT][PERCENT]);
    printf("IRQ=%03lu ",     table[IRQ][PERCENT]);
    printf("SOFTIRQ=%03lu\n", table[SOFTIRQ][PERCENT]);
}

void affiche2(void)
{   int i;
    int cpt=0;

    printf("[");   
    //setfcolor(0);
    setfcolor(2);
    for ( i=0 ; i < table[USR][PERCENT] ; i++ ) { putc('U',stdout); cpt++; }
    setfcolor(1);
    for ( i=0 ; i < table[SYSTEM][PERCENT] ; i++ ) { putc('S',stdout); cpt++; }
    setfcolor(4);
    for ( i=0 ; i < table[IOWAIT][PERCENT] ; i++ ) { putc('W',stdout); cpt++; }
    setfcolor(6);
    for ( i=0 ; i < table[IRQ][PERCENT]+table[SOFTIRQ][PERCENT] ; i++ ) { putc('I',stdout); cpt++; }
    setdefault();
    for ( i=cpt ; i < 100 ; i++ ) putc(' ',stdout);
    printf("] CHARGE CPU : %4lu%% \n", 100 - table[IDLE][PERCENT] );

   
}

void affichenet(void)
{   char outbuffer[2000];

    sprintf(outbuffer , "%s|",thishostname);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",     table[USR][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",    table[NICE][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",  table[SYSTEM][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",    table[IDLE][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",  table[IOWAIT][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu|",     table[IRQ][PERCENT]);
    sprintf(outbuffer+strlen(outbuffer) , "%lu\n", table[SOFTIRQ][PERCENT]);

    printf("%s", outbuffer);

    sendto(s, outbuffer, strlen(outbuffer)+1, 0, (struct sockaddr *) &srvAddr, sizeof(srvAddr));

}

void submain(void)
{       
    unsigned int i=0;
   
    
    if ( ! recup() ) return;
    
    for ( i=USR ; i<TOTAL ; i++)
    {       if (table[i][CURRENT]<table[i][OLD]) return;
    }

    table[TOTAL][CURRENT] = 0;
    for ( i=USR ; i<TOTAL ; i++ ) 
        table[TOTAL][CURRENT] += table[i][CURRENT];           /*genere le total       */
    for ( i=USR ; i<=TOTAL ; i++) 
        table[i][DIFF] = table[i][CURRENT] - table[i][OLD];   /*genere différences */

    if ( table[TOTAL][DIFF] == 0 ) return;
    for ( i=USR ; i<=TOTAL ; i++) 
        table[i][PERCENT] = ( table[i][DIFF] * 100 ) / table[TOTAL][DIFF];    /*calcul pourcentages */

    //affiche2();
    affichenet();
    
    for ( i=USR ; i<=TOTAL ; i++ ) 
        table[i][OLD]=table[i][CURRENT];     /*archive les anciennes valeur pour la prochaine mesure */
    
}


int main(void)
{       
    unsigned int i=0;
    unsigned int j=0;
    struct timespec ts;
    ts.tv_sec=0;
    ts.tv_nsec=100000000;
   
     
    for ( i=USR ; i<=TOTAL ; i++) for (j=CURRENT ; j<NBCOLS ; j++ ) table[i][j]=0;
    
    recuphostname();

    srvAddr.sin_family = AF_INET;
    srvAddr.sin_port = htons(31857);
    srvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    

    s=socket(AF_INET,SOCK_DGRAM,0);
    if ( s<0 ) return 1;

    while (1)
    {   nanosleep(&ts, NULL);
        submain();
    }
     return 0;
}

Les tours de hanoï

 Trucs et astuces techniques  Commentaires fermés sur Les tours de hanoï
Fév 022010
 

Ce petit bout de code C illustre la résolution du problème des
tours de hanoï par l’utilisation de fonctions récursives.

#include <stdio.h>
#include <time.h>


//variables globales
const int nb=16;
int colonne[3][100];

//prototypes
int autre(int c1, int c2);//renvoie l'autre colonne (on lui en donne 2 elle renvoie la troisième)
void affiche(void);//Affiche l'état des 3 colonnes
void deplace1(int s, int d);//Déplace un jeton d'une colonne à une autre
void deplacen(int n, int s, int d);//Déplace n jeton(s) d'une colonne à une autre

int main(void)
{  int cpt, col ;
   for (cpt=0; cpt<100 ; cpt++)
      for (col=0 ; col<3 ; col++)
         colonne[col][cpt]=0;
   for (cpt=0; cpt<nb ; cpt++)
      colonne[0][cpt]=nb-cpt;
   affiche();
   deplacen(nb,0,2);
}

void deplacen(int n, int s, int d)
{  if ( n == 1 ) { deplace1(s,d); return; }
   deplacen(n-1,s,autre(s,d));
   deplace1(s,d);
   deplacen(n-1,autre(s,d),d);
}

void deplace1(int s, int d)
{  int cpts=0, cptd=0;
   while (colonne[s][cpts]) cpts++ ; 
   while (colonne[d][cptd]) cptd++ ; 
   colonne[d][cptd]=colonne[s][cpts-1] ; 
   colonne[s][cpts-1] = 0 ;
   affiche();
}

int autre(int c1, int c2)
{  switch (c1)
   {  case 0 :  if ( c2 == 1 ) return 2 ; else return 1 ;
      case 1 :  if ( c2 == 0 ) return 2 ; else return 0 ;
      case 2 :  if ( c2 == 0 ) return 1 ; else return 0 ;
   }
}

void affiche(void)
{  int cpt, col;
   static int etape=0;
   printf("etape %09u\n",etape++);
   for (col=0 ; col<3 ; col++)
   {  printf("Colonne %c > ",'A'+col);
      cpt=0 ; while ( colonne[col][cpt] ) printf("%02u ", colonne[col][cpt++]) ; 
      printf("\n");
   }
   printf("\n");
   usleep(1000);
}