Les processus sous Unix

© D. Mathieu    mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 20/09/2000 - Dernière mise à jour : 10/10/2001

Remarques préliminaires :

Sommaire

Quelques outils nécessaires à la manipulation des processus
 
Wrapper de fork()
Wrapper de kill()
Compréhension de la fonction fork()
 
  fork_a.cxx   fork_b.cxx   fork_c.cxx 
  fork_d.cxx   fork_e.cxx   fork_f.cxx
Identification des processus père et fils
 
Gnôthi seauton  exo_01
Contrôle des naissances ou petits lapins ?  exo_02
Y a-t-il de l'écho ?
  ah_que
Processus démons
 
Mise en évidence des démons existant
Création d'un démon  exo_03
Héritage des propriétés
 
Héritage du traitement des signaux  exo_04
Héritage des descripteurs de fichiers ouverts  exo_05
Partage du pointeur de fichier  exo_06
Constructeurs/destructeurs et fork()
 
exo_07
Fonction kill()
Compréhension de la fonction kill()
 TuQuoqueFili_a  TuQuoqueFili_b
Communication par signaux  exo_08
Relations fils --> père : fonctions wait...()
 
Wrappers des fonctions système wait(), waitpid() et wait3()  
status d'un fils mort exo_09
status d'un fils stoppé exo_10
Mort de plusieurs fils
Version 1   exo_11a  
Version 2   exo_11b  
Autopsie exo_12
Nettoyage des zombies : fonction wait3() exo_13
Tri sélectif : fonction waitpid() exo_14
Lancement de commandes
Fonction execl() : héritage de la table des descripteurs exo_15
Fermeture des fichiers par exec exo_16
Emulation de la fonction system() exo_17



Quelques outils nécessaires à la manipulation des processus

Wrapper de fork()

Wrapper de kill()


Corrigés : nsSysteme.h    -    nsSysteme.hxx

 Sommaire


Compréhension de la fonction fork()

    Les programmes fork_*.cxx suivants sont dans le répertoire dirprocess des corrigés. Recopiez-les dans votre répertoire courant. Analyser le code et, avant de les lancer, essayez de prévoir ce que va donner chaque exécution. Chaque programme doit être lancé au moins 10 fois. Comparer avec le résultat effectif et, en cas de divergence, essayer de l'expliquer. En cas de symptômes prolongés, consulter un spécialiste.
fork_a.cxx
// fork_a.cxx

#include <iostream>
#include <string>
#include <exception>

#include <sys/types.h>  // pid_t

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;     // CException
using namespace nsSysteme;  // Fork()

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);

    cout << "Avant le fork()" << endl;
    const ::pid_t pid = Fork();
    cout << "Apres le fork() : pid = " << pid << endl;

    return 0;

} // ppal()

Corrigé

fork_b.cxx
// fork_b.cxx

#include <iostream>
#include <string>
#include <exception>

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;     // CException
using namespace nsSysteme;  // Fork()

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);

    cout << "Avant le fork()" << endl;
    cout << "Apres le fork() pid = ";
    cout << Fork() << endl;

    return 0;

} // ppal()

Corrigé

fork_c.cxx
// fork_c.cxx
#include <iostream>
#include <string>
#include <exception>

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;     // CException
using namespace nsSysteme;  // Fork()

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);

    cout << "Avant le fork()" << endl;
    cout << "Apres le fork() pid = " << endl;
    cout << Fork() << endl;

    return 0;

} // ppal()

Corrigé

fork_d.cxx
// fork_d.cxx
#include <iostream>
#include <string>
#include <exception>

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;     // CException
using namespace nsSysteme;  // Fork()

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);

    cout << "Avant le fork()" << endl;
    cout << "Apres le fork() pid = \n";
    cout << Fork() << endl;
 
    return 0;
 
} // ppal()

Corrigé

fork_e.cxx
// fork_e.cxx
#include <iostream>
#include <string>
#include <exception>

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;     // CException
using namespace nsSysteme;  // Fork()

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CException (string ("Usage : ") + argv [0], CstExcArg);

    cout << "Avant le fork()" << endl;
    cout << "Apres le fork() pid = " << Fork() << endl;

    return 0;

} // ppal()

Corrigé

fork_f.cxx
// fork_f.cxx

#include <iostream>
#include <string>
#include <exception>

#include <stdio.h>      // printf()

#include "nsSysteme.h"
#include "CException.h"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;
using namespace nsSysteme;

int std::ppal (int argc, char * argv[]) throw (exception)
{
    if (1 != argc) throw CExc (string ("Usage : ") + argv [0]);

    cout << "Avant le fork()" << endl;
    printf ("Apres le fork() pid = %d\n", Fork());

    return 0;

} // ppal()

Corrigé

 Sommaire


Identification des processus père et fils

   L'objectif de ces exercices est de reconnaître, dans un processus qui crée un ou plusieurs fils, quelles parties de code sont exécutées respectivement par le père et par le(s) fils.
 

Gnôthi seauton (connais-toi toi-même) [1]

exo_01
    Dans le fichier exo_01.cxx, écrire la fonction ppal() qui crée un fils. Le père affiche son propre numéro de pid et celui de son fils, le fils affiche son numéro de pid et celui de son père.

    Compiler et tester. Que se passe-t-il lorsque le fils commence par dormir une seconde ?

Rq1

Corrigé : exo_01.cxx

 Sommaire


Contrôle des naissances ou petits lapins ?

exo_02
 
  • Liste des fichiers : exo_02.cxx
  •      Dans le fichier exo_02.cxx, écrire un processus père qui crée 5 fils dans une boucle. Immédiatement après chaque création, le père et le fils créé font un compte-rendu de l'opération en s'identifiant : puis le fils se termine, pendant que le père passe à la création du fils suivant.

        Compiler et tester. Vérifier que les cinq fils ont été créés

    Corrigé : exo_02.cxx

     Sommaire


    Y a-t-il de l'écho ?

        Recopier dans le répertoire courant le fichier ah_que.cxx qui est dans le répertoire dirprocess des corrigés. Analyser le code et prévoir à l'avance le résultat de son exécution. Comparer aver le résultat effectif, etc.
     
    // ah_que.cxx

    #include <iostream>
    #include <exception>
    #include <string>

    #include "CException.h"
    #include "nsSysteme.h"        // Fork()
    #include "CstCodErr.h"

    using namespace nsUtil;       // CException
    using namespace nsSysteme;    // Fork()

    int std::ppal (int argc, char * argv[]) throw (exception)
    {
        if (1 != argc)
            throw CException (string ("Usage : ") + argv [0], CstExcArg);

        for (int i = 4; --i; ) if (!Fork()) cout << "Coucou" << endl;

        return 0;

    } // ppal()

    Corrigé

    Processus démons

    Mise en évidence des démons existant

        Par la commande ps -Al, vérifier l'existence du processus init et de nombreux autres démons. Si on suppose que les premiers processus créés par le système ont des numéros de pid croissants, vérifier qu'il n'en a survécu que très peu de la phase initiale de lancement du système.

    Sommaire


    Création d'un démon

    exo_03
        Dans le fichier exo_03.cxx, écrire la fonction ppal() qui crée un démon dont le corps consiste à :

        Compiler et tester. Vérifier que le processus fils existe bien toujours et est un démon. Pourquoi est-il demandé de faire dormir le démon une seconde ? Le démon existe-t-il après que vous vous êtes délogé ?

    Rq2

    Corrigé : exo_03.cxx

    Sommaire


    Héritage des propriétés

    Héritage du traitement des signaux

    exo_04
           Recopier les fichiers SaveSig.h et SaveSig.cxx du répertoire dirsignal dans les répertoires include et util. Modifier en conséquence les fichiers MakeUtil, INCLUDE_H et Makefile.

        Dans le fichier exo_04.cxx, écrire la fonction ppal() qui :

        Chaque processus annonce quel est son pid. Le père et le fils modifient les traitants de signaux pour pouvoir être arrêtés respectivement par les signaux SIGINT ou SIGQUIT. Chaque processus part ensuite dans une boucle infinie.

        Dans la fonction de déroutement, afficher un message sous la forme suivante (par exemple) :
     
    Signal 15 reçu par le père de pid 12345

    ou
     
    Signal 15 reçu par le fils de pid 12346

         Compilez et testez en faisant les essais suivants :

    Rq3

    Corrigé : exo_04.cxx    -    Makefile    -    INCLUDE_H    -    MakeUtil

     Sommaire


    Héritage des descripteurs de fichiers ouverts

    exo_05
        Dans l'espace de noms nsSysteme (fichiers include/TestFdOuverts.h et util/TestFdOuverts.cxx ), écrire la fonction TestFdOuverts() qui parcourt la table des descripteurs et, pour chaque descripteur correspondant à un fichier ouvert, affiche, dans le flux qui lui est passé en paramètre (cout par défaut), le numéro du processus suivi de la valeur du descripteur. La fonction getdtablesize() permet de connaître le nombre maximal de fichiers que peut ouvrir un processus, donc la taille maximale de la table des file descriptors.

        Pour tester si un descripteur correspond à un fichier ouvert, appeler une fonction système qui doit échouer sur un fichier fermé, et récupérer l'exception. Attention, si l'opération réussit (le fichier est ouvert), il ne faut pas qu'elle modifie l'état du fichier (donc pas de read() ou de write() ... ou de close()!!!). Si nécessaire, vous pouvez utiliser une fonction concernant les fichiers que vous n'auriez pas encore mise en oeuvre (nous avons utilisé lseek()). Dans ce cas, ajoutez-la aux fichiers nsSysteme.*.

        Mettre à jour les fichiers MakeUtil et INCLUDE_H en conséquence.

        Dans le fichier exo_05.cxx, écrire la fonction ppal() qui ouvre plusieurs fichiers (ou plusieurs fois le même fichier !), en referme certains (pour faire des "trous" dans la table des file descriptors), puis appelle la fonction TestFdOuverts(). Mettre à jour le fichier Makefile.

        Compiler et tester.

        Lorsqu'elle a été validée, modifier la fonction ppal() pour créer un processus fils APRES avoir appelé la fonction TestFdOuverts(). Faites alors afficher les fichiers ouverts dans le processus fils. Vérifier que ce sont bien les mêmes descripteurs qui correspondent à des fichiers ouverts dans les deux processus.

    Rq4

    Corrigé : exo_05.cxx    -    TestFdOuverts.h    -    TestFdOuverts.cxx    -    MakeUtil    -    INCLUDE_H    -    Makefile

     Sommaire


    Partage du pointeur de fichier [2]

    exo_06
        Ajouter, si ce n'est pas déjà fait, le wrapper de la fonction système lseek() à l'espace des noms nsSysteme.

        Les fichiers exo_06.cxx et exo_06.txt sont dans le répertoire dirprocess des corrigé. Recopiez-les dans votre répertoire courant. Analyser le code correspondant ci-dessous et, avant de lancer le programme, essayer de prévoir (par écrit) ce que peut donner chaque exécution.
     
    /**
     *  @File : exo_06.cxx
     **/
    #include <string>
    #include <iostream>
    #include <exception>

    #include <unistd.h>          // SEEK_SET
    #include <fcntl.h>           // O_RDWR

    #include "nsSysteme.h"       // Open(), Close(), Read(), Write(), Lseek()
    #include "CException.h"
    #include "CstCodErr.h"

    using namespace std;
    using namespace nsUtil;      // CException
    using namespace nsSysteme;   // Open(), Close(), Read(), Write(), Lseek()

    int std::ppal (int argc, char * argv []) throw (exception)
    {
        if (1 != argc)
            throw CException (string ("Usage : ") + argv [0], CstExcArg);

        char c;

        const int fd = Open ("exo_06.txt", O_RDWR);

        if (Fork ())                                        /* 01 */
        {
            Lseek (fd, 10, SEEK_SET);                       /* 02 */
            Read  (fd, &c, 1);                              /* 03 */
            cout << "lu par le pere : " << c << endl;       /* 04 */
            Read  (fd, &c, 1);                              /* 05 */
            cout << "lu par le pere : " << c << endl;       /* 06 */
        }
        else
        {

            Lseek (fd, 3, SEEK_SET);                        /* 07 */
            Read  (fd, &c, 1);                              /* 08 */
            Write (fd, "ABCDE", 3);                         /* 09 */
            cout << "lu par le fils : " << c << endl;       /* 10 */
        }
        Close (fd);
        return 0;

    } // ppal()

        Compiler et tester.

        En analysant les affichages obtenus et en visualisant le fichier résultant exo_06.txt, retrouver le déroulement du programme, en utilisant les numéros des instructions indiquées dans le listing. Exécuter plusieurs fois le programme, car les résultats peuvent être différents. Pouvez-vous en conclure quelque chose concernant la localisation du pointeur de fichier ? Est-ce en accord avec ce que vous avez (peut-être ...) montré au cours des exercices correspondants concernant les fichiers ?

    Corrigé

     Sommaire


    Constructeurs/destructeurs et fork()

    exo_07
        Dans le fichier exo_07.cxx, écrire dans l'espace de noms anonyme la struct CX qui comporte :     Dans la fonction ppal() définir un objet de la classe CX, puis créer un processus fils. C'est tout.

        Compiler, tester plusieurs fois et interpréter les résultats.

    Rq5

    Corrigé : exo_07.cxx

     Sommaire


    Fonction kill()

    Compréhension de la fonction kill()

        Les programmes TuQuoqueFili_*.cxx suivants sont dans le répertoire ~mathieu/PARTAGE/src/tp/tpsys/tpprocess dirprocess des corrigés. Recopiez-les dans votre répertoire courant. Avant de les lancer, essayez de prévoir ce que va donner chaque exécution. Comparer avec le résultat effectif et, en cas de divergence, essayer de l'expliquer (vous serez sans doute obligé de lire attentivement le man de la fonction système kill()). En cas de symptômes prolongés, consulter un spécialiste.
    TuQuoqueFili_a
    /**
     * @File : TuQuoqueFili_a.cxx
     **/
    #include <string>
    #include <exception>
    #include <csignal>          // SIGKILL

    #include <unistd.h>         // getppid(), sleep()
    #include <sys/types.h>      // pid_t

    #include "nsSysteme.h"      // Fork(), Kill()
    #include "CException.h"
    #include "CstCodErr.h"

    using namespace nsUtil;     // CException
    using namespace nsSysteme;  // Fork(), Kill()

    int std::ppal (int argc, char * argv[]) throw (exception)
    {
        if (1 != argc)
            throw CException (string ("Usage : ") + argv [0], CstExcArg);
     
        ::pid_t n;
        if (!(n = Fork ()))
        {
            ::sleep (3);
            Kill (n, SIGKILL);
        }
        else
            Kill (::getppid (), SIGKILL);

        return 0;

    } // ppal()

    Corrigé

     Sommaire


    TuQuoqueFili_b
    /**
     * @File : TuQuoqueFili_b.cxx
     **/
    #include <string>
    #include <exception>
    #include <csignal>          // SIGKILL

    #include <unistd.h>         // getppid(), sleep()
    #include <sys/types.h>      // pid_t

    #include "nsSysteme.h"      // Fork(), Kill()
    #include "CException.h"
    #include "CstCodErr.h"

    using namespace nsUtil;     // CException
    using namespace nsSysteme;  // Fork(), Kill()

    int std::ppal (int argc, char * argv[]) throw (exception)
    {
        if (1 != argc)
            throw CException (string ("Usage : ") + argv [0], CstExcArg);
     
        ::pid_t n;
        if (!(n = Fork ()))
            Kill (n, SIGKILL);
        else
        {
            ::sleep (3);
            Kill (::getppid (), SIGKILL);
        }

        return 0;

    } // ppal()

    Corrigé

     Sommaire


    Communication par signaux (facultatif)

        En utilisant aussi les fonctions Fork(), getpid(), getppid(), pause() et sleep(), écrire deux processus père et fils qui s'envoient mutuellement et périodiquement (de l'ordre de quelques secondes) un signal (SIGUSR1 par exemple)

       Compiler et tester.

    Corrigé : exo_08.cxx

     Sommaire


    Relations fils --> père : fonctions wait...()

    Wrappers des fonctions système wait(), waitpid(), wait3() et wait4()

        Ajouter à l'espace des noms nsSysteme les wrappers des fonctions système wait() , waitpid() , wait3() et wait4()

        Donner :

    Corrigé : nsSysteme.h,    -    nsSysteme.hxx

     Sommaire


    status d'un fils mort

    exo_09

        Sachant que la valeur de retour du fils est contenue dans le deuxième octet de plus faible poids du paramètre status de la fonction wait(), écrire dans le fichier exo_09.cxx une macro-instruction GETSTATUS qui renvoie cette valeur sous forme d'un entier non signé. Attention les entiers peuvent être codés sur plus de 2 octets et seul le second octet doit être récupéré.

        Dans la fonction ppal(), écrire un processus qui crée deux fils :

    Rq6

    Corrigé : exo_09.cxx

     Sommaire


    status d'un fils stoppé

    exo_10
         Afin de montrer que le père d'un processus peut être informé (par un signal SIGCHLD) qu'un de ses processus fils a été stoppé par un signal (grâce à la macro WSTOPSIG(), écrire dans le fichier exo_10.cxx la fonction ppal() qui effectue les opérations suivantes :

    Rq7

         Compiler et tester, en envoyant au fils les deux signaux qui peuvent le stopper (SIGSTOP et SIGTSTP). Les identifier grâce aux tableaux des signaux (tableau 1 ou tableau 2).

         Il est intéressant d'effectuer le test suivant : envoyer au père et au fils le même signal SIGTSTP, en utilisant la commande Unix killall, puis relancer le fils. Relancer ensuite le père. Garde-t-il la trace de l'arrêt de son fils ?

    Rq8

    Corrigé : exo_10.cxx

     Sommaire


    Mort de plusieurs fils

        L'objectif des deux exercices suivants est de montrer que la fonction wait(), même utilisée sans en exploiter le paramètre status, ne peut pas être considérée seulement comme une simple fonction pause().
    Version 1 - exo_11a
        Dans le fichier exo_11a.cxx,

        Compiler et tester.

        Ajouter un argument facultatif à la commande (n'importe quoi). Modifier le programme pour que, si cet argument est présent, tous les fils dorment le même temps (par exemple NbreFils secondes).

        Compiler et tester (plusieurs essais seront peut-être nécessaires pour que vous obteniez le comportement attendu).

    Rq9

    Corrigé : exo_11a.cxx

     Sommaire


    Version 2 - exo_11b

        Recopiez le fichier exo_11a.cxx (dernière version) dans le fichier exo_11b.cxx. Remplacez l'appel de la fonction pause() par celui de la fonction Wait().

        Compiler et tester. Comparer le comportement par rapport à celui de l'exo_11a.

    Rq10

    Corrigé : exo_11b.cxx

     Sommaire


    Autopsie des processus (facultatif)

    exo_12
        Cet exercice a pour objectifs d'illustrer les différentes possibilités de retour de l'appel à la fonction wait() par un processus père et d'analyser toutes les causes de retour possibles d'un processus fils.

        Recopier à partir des répertoires include et util des corrigés les fichiers Autopsie.h et Autopsie.cxx. Ils contiennent la fonction Autopsie() de l'espace des noms nsSysteme qui injecte dans un flux de sortie la cause de la mort d'un processus. Modifier en conséquence les fichiers Makefile, INCLUDE_H et MakeUtil.

        Dans le fichier exo_12.cxx, montrer ce que provoque l'appel de la fonction Wait() en l'absence de fils.

        Compiler et tester.

        A l'espace de noms anonyme, ajouter une fonction de déroutement vide, et les trois fonctions Fils1(), Fils2() et Fils3(), dont les comportements sont les suivants :

        Dans la fonction ppal(), le père :

        Compiler et tester.

    Rq11

    Corrigés : exo_12.cxx    -    Makefile    -    INCLUDE_H    -    MakeUtil

     Sommaire


    Nettoyage des zombies : fonction wait3()

    exo_13
        Dans l'espace de noms anonyme, écrire un "nettoyeur de zombies" qui autopsie chaque fils mort (boucle for dans laquelle est appelée la fonction wait3() non bloquante).

        Ecrire la fonction ppal() qui :

        Au bout d'un laps de temps supérieur à 5 secondes, vérifiez qu'il n'y a plus de zombies.

    Corrigé : exo_13.cxx

     Sommaire


    Tri sélectif : fonction waitpid()

    exo_14
         Ecrire un processus père qui crée 5 fils (le ième fils "dort" i secondes avant de se terminer) et attend la fin du fils dont le rang (entre 0 et 4) est passé en argument de la commande (waitpid()).

    Corrigé : exo_14.cxx

     Sommaire


    Lancement de commandes

    Fonction execl() : héritage de la table des descripteurs

    exo_15
         Dans le fichier exo_15a.cxx, écrire un programme qui affiche le nombre et la liste des arguments passés lors de son lancement (argc et argv), ainsi que la liste des descripteurs de fichiers ouverts (au moyen de la fonction TestFdOuverts()).

         Compiler et tester. Essayer en tapant des arguments "bizarres" (contenant par exemple des caractères | * ; etc.) et trouver la solution pour qu'ils apparaissent dans la liste des arguments.

    Rq12

         On rappelle que les fonctions de la famille exec...() remplacent le code du processus qui appelle cette fonction par celui du programme qui est passé en paramètre à la fonction. Si l'appel se déroule normalement, on ne revient donc jamais des fonctions exec...(). Donc tout retour signifie une erreur. Il est donc inutile d'écrire un wrapper, mais il est indispensable de vérifier que le programme appelant ne revient pas de la fonction, en plaçant un traitement d'erreur juste après l'appel.

         Dans le fichier exo_15.cxx, écrire un programme qui ouvre ou crée quelques fichiers puis lance le programme exo_15a (avec des paramètres quelconques) au moyen de la fonction execl().

         Compiler et tester.

    Corrigé : exo_15a.cxx    -    exo_15.cxx

     Sommaire


    Fermeture des fichiers par exec

    exo_16
         Ajouter à l'espace de noms nsSysteme les 3 wrappers des fonctions système fcntl()

         Recopier le fichier exo_15.cxx dans exo_16.cxx. Dans la fonction ppal() :

         Compiler et tester.

    Rq13

    Corrigé : exo_16.cxx

     Sommaire


    Emulation de la fonction system()

    exo_17
         Récupérer le fichier Chrono.cxx dans le répertoire dirprocess des corrigés. Le compiler et le tester.

         Dans la fonction ppal() du fichier exo_17.cxx, lancer au moyen de la fonction ::system() l'exécutable Chrono avec les paramètres désirés.

         Compiler et tester.

         Dans l'espace de noms anonyme, écrire la fonction System (const char * const Commande); qui émule la fonction système system(). Remplacer l'une par l'autre.

         Compiler et tester.

         Modifier la fonction ppal() pour lui permettre les possibilités suivantes :

         Compiler et tester.

    Corrigé : exo_17.cxx

     Sommaire


    Téléchargement des corrigés :

    tpprocess.zip

    Chemins d'accès aux sources des corrigés :

    ~mathieu/PARTAGE/src/tp/tpsys/tpprocess/dirprocess
    ~mathieu/PARTAGE/src/tp/tpsys/tpprocess/util
    ~mathieu/PARTAGE/src/tp/tpsys/tpprocess/include



    [1] devise de Socrate ...

    [2] inspiré de "La programmation sous UNIX" de Rifflet

    © D. Mathieu    mathieu@romarin.univ-aix.fr
    I.U.T.d'Aix en Provence - Département Informatique