\documentclass{article}
\usepackage[latin1]{inputenc}
\usepackage{a4}
\usepackage{fullpage}

\title{TD 2 d'Environnement Logiciel: le corrigé}
\date{Mercredi 15 septembre 2004}

\begin{document}
\maketitle

\textbf{Entrées-sorties en C}

Les sorties sur la sortie standard passent par un \emph{buffer},
l'impression effective de la sortie ne se fait que lorsque le buffer
est plein où qu'un caractère retour chariot est rencontré. La commande
\verb+fflush+ demande explicitement de vider le \emph{buffer}. La
sortie d'erreur \verb+stderr+ n'est pas bufferisée.

\textbf{Fichiers}

\verb+stdin+, \verb+stdout+ et \verb+stderr+ sont de type 
\verb+struct _IO_FILE *+ :
\begin{verbatim}
laurent@tortoise:~$ grep stdin /usr/include/stdio.h 
extern struct _IO_FILE *stdin;          /* Standard input stream.  */
\end{verbatim}
et \verb+struct _IO_FILE+ est un autre nom de \verb+FILE+ :
\verb+typedef struct _IO_FILE FILE;+.

\begin{itemize}

\item La fonction \verb+fopen+ avec comme mode d'ouverture \verb+w+
ouvre un fichier en mode écriture en positionnant l'\emph{offset} au
début du fichier (donc plusieurs exécutions ne font que réécrire la
même chose dans le fichier). Le mode \verb+a+ pour \emph{append}
demande à ouvrir le fichier en mode écriture en se plaçant à la fin du
fichier pour ajouter des données à la fin.

\begin{verbatim}


#include <stdio.h>

int main (void)
{
    unsigned int n;
    unsigned int i;
    FILE *f;

    printf ("Veuillez entrer un nombre: ");
    fflush (stdout);
    scanf ("%d", &n);

    f = fopen ("nombres", "w");

    fwrite (&n, sizeof (n), 1, f);

    for (i = 0; i < n; i++)
        fwrite (&i, sizeof (i), 1, f);

    fclose (f);
    return 0;
}

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    unsigned int n;
    unsigned int *p;
    unsigned int i;
    FILE *f;

    f = fopen ("nombres", "r");

    fread (&n, sizeof (n), 1, f);

    p = malloc (n * sizeof (n));
    if (p == NULL) {
        fprintf (stderr, "Achetez plus de mémoire.\n");
        exit (42);
    }

    fread (p, sizeof (n), n, f);
    fclose (f);
    for (i = 0; i < n; i++)
        printf ("%d\n", p[i]);
    free (p);

    return 0;
}
    
\end{verbatim}

\item Un implémentation de \verb+wc+ en \verb+C+:

\begin{verbatim}
#include <stdio.h>

#define BSIZE 1024

int sep_mot (char c)
{
    return ((c == '\n') || (c == '\t') || (c == ' '));
}

int main (int argc, char *argv[])
{
    FILE *f;
    unsigned int nb_mots = 0;
    unsigned int nb_lignes = 0;
    unsigned int lus = 42;
    unsigned int index;
    int dans_mot = 0;

    char buffer[BSIZE];

    if (argc > 1)
        f = fopen (argv[1], "r");
    else
        f = stdin;

    while (lus != 0) {
        lus = fread (buffer, sizeof (char), 1024, f);
        for (index = 0; index < lus; index++) {
            if (buffer[index] == '\n')
                nb_lignes++;
            if (sep_mot (buffer[index])) {
                if (dans_mot) {
                    dans_mot = 0;
                    nb_mots++;
                }
            }
            else
                dans_mot = 1;
        }
    }
    fclose (f);
    printf ("%d %d\n", nb_lignes, nb_mots);
    return 0;
}
\end{verbatim}
\end{itemize}

\textbf{Shell et redirections}

\begin{itemize}
\item Essayez :

\begin{enumerate}

\item \verb+sort < fichier.txt+
    Le shell ouvre le fichier \verb+fichier.txt+ en lecture, fork le
    processus et remplace dans le fils le \emph{file descriptor} de
    l'entrée standard par celui du fichier via un appel à \verb+dup+.
    Dans le fils, on recouvre l'espace mémoire du processus par celui
    du binaire \verb+sort+ via l'appel à \verb+exec+. Du point de vue
    de l'utilisateur, le contenu du fichier est affiché dans la sortie
    standard trié ligne par ligne.
    \item \verb+echo 10 > num.txt+

    Pareil ici, mais en écriture: le fichier num.txt est ouvert en
    mode écriture (avec offset positionné au début du fichier) et la
    sortie de la commande \verb+echo 10+ est renvoyé dans le fichier.
    Résultat: le fichier \verb+num.txt+ contient la chaîne \verb+10+.

    \item \verb+echo 20 >> num.txt+

    Le fichier \verb+num.txt+ est ouvert en mode d'ajout à la fin du
    fichier. Le chaîne \verb+20+ est donc inscrite à la fin du fichier
    dont la taille a augmenté en conséquence.
    \item \verb+ls | wc -l+
    La sortie de \verb+ls+ est redirigée dans l'entrée standard de
    \verb+wc -l+. Le shell réalise cela au moyen de la structure de
    tube (\emph{pipe}) vue en cours.
    \item \verb+ps aux | grep `whoami`+.
    La commande \verb+whoami+ est lancée dans un fils du shell et sa
    sortie standard récupérée et réinjectée dans la ligne de commande
    de départ. En supposant qu'on soit loggé sous \verb+fousse+, une
    redirection normale se fait ensuite. La commande affiche les
    lignes qui contiennent le nom sous lequel on s'est loggé dans la
    sortie de \verb+ps aux+ (qui affiche la liste de tous les
    processus en précisant le nom de l'utilisateur qui les a lancés).

    \end{enumerate}

\item En redirigeant la sortie d'une commande vers \verb+/dev/null+,
on écrit les données dans un fichier spécial (de type \emph{device})
qui est géré par le système. Les données envoyées vers ce fichier sont
ignorées.

La redirection vers \verb+/dev/null+ permet donc d'ignorer la sortie
d'une commande, c'est utile par exemple dans un script lorsque seul la
valeur de sortie d'une commande nous intéresse et qu'on ne veut pas
montrer à l'utilisateur de sortie inutiles.

\item \verb+cat < /dev/null > fichier.txt+ a pour effet de copier le
contenu de \verb+/dev/null+ vers \verb+fichier.txt+. Comme une lecture
sur \verb+/dev/null+ renvoit tout de suite le code de fin de fichier,
le fichier \verb+fichier.txt+ sera remplacé par un fichier de taille
vide, et créé pour l'occasion s'il n'existait pas encore.

La commande \verb+touch+ permet aussi de créer un fichier vide.

\item 
\begin{verbatim}
#!/bin/bash

head -n $2 | tail -n $(($2 - $1 + 1))
\end{verbatim}
\end{itemize}

\textbf{Entrée sortie, encore}

\begin{itemize}

\item Un pseudo \verb+cat+ en \verb+C+ avec les fonctions de la
bibliothèque d'entrée sortie standard.
\begin{verbatim}
#include <stdio.h>

int main (void)
{
    char c;
    int lu = 1;

    while (lu > 0) {
        lu = fread (&c, sizeof(c), 1, stdin);
        fwrite (&c, sizeof (c), 1, stdout);
    }

    return 0;
}
\end{verbatim}

\item Un pseudo \verb+cat+ en \verb+C+ avec les appels systèmes
\verb+read+ et \verb+write+.
\begin{verbatim}
#include <unistd.h>

int main (void) {
    char c;

    int lus = 1;

    while (lus > 0) {
        lus = read (STDIN_FILENO, &c, sizeof (c));
        write (STDOUT_FILENO, &c, sizeof (c));
    }

    return 0;
}
\end{verbatim}

On constate que la version avec \verb+fread+ et \verb+fwrite+ est
sensiblement plus rapide. La raison est que \verb+fread+ et
\verb+fwrite+ fait moins d'appels système à \verb+read+ et
\verb+write+. Lorsqu'on demande $1$ octet à \verb+fread+, la fonction
fait un appel système à \verb+read+ en demandant une taille plus
grande. Les octets supplémentaires sont stockés dans une mémoire
temporaire gérée par ces fonctions. Et les appels successifs se
contenteront de chercher les octets dans cette mémoire (appelée tampon
ou \emph{buffer}) plutôt que de faire un appel système à chaque fois
(le même phénomène se produit en écriture). \emph{Grosso modo}, on
peut voir les \verb+FILE *+ comme des \emph{file descriptor} avec un
\emph{buffer}. \verb+fread+ et \verb+fwrite+ sont donc les fonctions
de plus haut niveau, tandis que \verb+read+ et \verb+write+ permettent
au programmeur d'accéder directement aux entrées/sorties du système.

\item Normalement, l'exécution de ce programme:

\begin{verbatim}
#include <stdio.h>
#include <unistd.h>

int main ()
{
    fwrite ("Hello ", sizeof(char), 6, stdout);
    write (STDOUT_FILENO, "World !", 7);

    return 0;
}
\end{verbatim}
affiche en sortie d'abord \verb+World !+ suivi de \verb+Hello +. Ce
résultat \emph{a priori} surprenant s'explique par ce qu'on a vu plus
haut. L'appel à \verb+fwrite+ demande à la fonction de haut niveau
d'afficher une chaîne. Cette chaîne est placée dans le buffer de la
bibliothèque d'entrées/sorties standards \verb+C+ du fichier
(\verb+FILE *+) \verb+stdout+, mais n'est pas encore envoyée au
système. L'appel à \verb+write+ en revanche place la chaine
\verb+World !+ directement dans le tampon de sortie du \emph{file
descriptor} correspondant à la sortie standard du processus. Lorsque
le programme se termine, un appel caché à des fonctions utilitaires de
la libc va nettoyer les fichiers ouverts, et en particulier vider le
tampon de \verb+stdout+ par un appel à \verb+write+. Le système voit
donc arriver les deux chaînes dans le désordre et elles s'affichent
comme ceci.

\end{itemize}

\end{document}
