2014:03:10:my time

De wiki-prog
Aller à : navigation, rechercher

Introudction

L'objectif de ce TP est d'écrire un clone de la commande time(1) (disponible sous forme de builtins ou de vrai commande.)

 un_shell> /usr/bin/time -p /bin/ls
 tp-mytime.wiki
 real 0.00
 user 0.00
 sys 0.00
 un_shell> /usr/bin/time -p primes 1 10000000 > /dev/null
 real 0.74
 user 0.74
 sys 0.00

Dans cette exemple j'ai utilisé la vraie commande time(1) plutôt que par la builtin du shell pour avoir un affichage standard avec l'option -p.

Cette commande affiche le temps passé pour executer la commande passée en paramètre. Comme vous pouvez le voir, on peut passer des paramètres à cette commande.

On va donc écrire notre propre commande time(1) (et on en profitera pour augmenter la précision des temps affichés.)

Exécution

La première chose que vous avez à faire est une simple exécution de commande à partir de votre tableau d'argument (le second paramètre de la fonction main.) Dans un premier temps, on se contentera de l'exécution via la fonction execvp(3).


 un_shell> ./my_time ls
 my_time  my_time.c  tp-mytime.wiki
 un_shell> ./my_time ls -l
 total 16
 -rwxr-xr-x 1 feanor feanor 7234 Mar 25 09:57 my_time
 -rw-rw-r-- 1 feanor feanor  299 Mar 25 09:57 my_time.c
 -rw-rw-r-- 1 feanor feanor 1096 Mar 25 09:51 tp-mytime.wiki
 un_shell> ./my_time ls -l -a
 total 24
 drwxr-xr-x 2 feanor feanor 4096 Mar 25 09:57 .
 drwxr-xr-x 7 feanor feanor 4096 Mar 25 09:36 ..
 -rwxr-xr-x 1 feanor feanor 7234 Mar 25 09:57 my_time
 -rw-rw-r-- 1 feanor feanor  299 Mar 25 09:57 my_time.c
 -rw-rw-r-- 1 feanor feanor 1096 Mar 25 09:51 tp-mytime.wiki


On gérera les erreurs à l'aide des fonctions err(3) et errx(3). Par exemple, pour gérer le manque de paramètre, on pourra écrire:

int main(int argc, char *argv[]) {
  if (argc < 2)
    errx(EXIT_FAILURE, "missing arguments");
  // ...
}

Le temps réel

On va maintenant mesurer le temps réel d'exécution. Pour ça il nous faut faire deux choses:

  • mettre notre programme en attente de la commande passée en argument
  • mesurer la différence de temps à l'aide des horloges du systèmes.

Nous aurons donc besoin des fonctions fork(2), wait(3) et clock_gettime(2) (les structures de type timespec.)

Quelque mot sur le programme:

  • le temps sera affiché sur la sortie d'erreur et non sur la sortie standard (cela permet de faire des redirections séparées.);
  • on affichera le nombre de seconde et le nombre de micro-seconde (voir l'exemple pour le format) plutôt que la version de la commande qui se contente d'un nombre de seconde décimal avec 2 chiffres après la virgule;
  • la commande my_time devra terminer avec le même code d'erreur que la commande exécutée (il vous faut donc jouer avec WEXITSTATUS);
  • si la commande meurt avec un signal particulier, il faudra afficher un message spécifique (voir l'exemple après) contenant le numéro du signal, de son côté my_time devra mourrir avec la valeur de retour 127.

Exemples de sorties:

 un_shell> ./my_time
 my_time: missing arguments
 un_shell> ./my_time ls
 Makefile       my_time        my_time.c      tp-mytime.wiki
 real: 0s 1019us
 un_shell> ./my_time primes 1 1000000000 > /dev/null
 real: 8s 178763us
 un_shell> cat abort-only.c
 #include <stdlib.h>
 int main() {
   abort();
   return 0;
 }
 un_shell> make abort-only
 clang -Wall -Wextra -std=c99 -O2  abort-only.c  -o abort-only
 un_shell> ./my_time ./abort-only
 real: 0s 323us
 my_time: command ./abort-only died with signal 6.
 un_shell> echo $status
 127
 un_shell>

Mesurer le temps utilisateur et le temps système

On veut maintenant aller au bout de notre émulation de la commande time(1) en récupérant les temps d'exécution utilisateur et système (correspondant au temps d'activité processeur et au temps passé dans les appels systèmes.)

Pour ça, il nous faut une version plus évoluée de wait(3) qui va nous premettre de récupérer ces informations dans une structure de type struct rusage. Il nous faut donc passer par l'appel système wait3(2), par contre, nous n'auront pas besoin d'options particulière et l'appel ressemblera donc à: wait3(&status, 0, &rusage)

Pour l'affichage, on se basera sur le même principe que pour la question précédante.