2014:03:10:my time
Sommaire
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.