IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Vérifier si un programme est ouvert et l'utiliser


Sujet :

C

  1. #41
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Ça ressemble à un hack assez obscur pour faire, comme son nom l'indique, un assert statique (même si assert() est une macro, elle teste la véracité de ton expression au runtime et appelle au final une fonction de la libC) en générant un code qui soit ne peut pas compiler si x est faux, soit génère un code toujours vrai et ne faisant rien, pouvant être optimisé et donc annihilé par le compilo. C'est donc un assert() au niveau de la précompilation.
    Tout à fait.
    En fait, "(x)" (la valeur ou le résultat) doit être un entier constant (constante littérale), car ce qui se trouve entre le "case" et le ":" doit non seulement pouvoir être connu ou calculé par le préprocesseur mais doit aussi être un entier. Dans ce cas, si "(x)" donne 0, le préprocesseur remplace "0 != (x)" par "0". Sinon, par "1". Et dans le premier cas, le compilateur sort une erreur, à cause de deux "case 0:" dans le même "switch".

    Donc, cette macro provoque une erreur si l'expression "(x)" :
    * ne peut pas être connue/calculée par le préprocesseur; ou :
    * si elle peut être connue/calculée par le préprocesseur mais qu'elle n'est pas un entier; ou:
    * si elle peut être connue/calculée par le préprocesseur mais qu'elle vaut 0

  2. #42
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Citation Envoyé par troumad Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
      /* Ensure that sun_path is the last member */
      STATIC_ASSERT(sizeof *addr ==
          offsetof(struct sockaddr_un, sun_path) + sizeof addr->sun_path);
    Je suis autodidacte en C : mes profs ne connaissaient que le Pascal
    Et là, il y a des choses qui me dépassent !
    1) pourquoi pas de () après le sizeof
    Parce que sizeof est un mot-clé du C et pas une fonction.

    Le piège, ce que si ce n'est pas une fonction dans le sens C du terme, c'en est bien une au sens mathématique, puisqu'il s'agit d'une expression évaluable pouvant se trouver au sein d'une formule, et dont la valeur dépend du paramètre.

    En fait, sizeof x va te donner la taille de x. « x » peut être l'instance d'une variable, par exemple, mais ça peut être également un nom de type, auquel cas c'est ce nom qui doit être entre parenthèses, comme s'il s'agissait d'un cast. Maintenant, x peut être remplacé par n'importe quelle expression, et rien ne t'empêche de mettre, en soi, cette expression entre parenthèses. C'est de la même façon que « x + 5 » équivaut à « (x) + 5 ».

    Comme sizeof ressemble à une fonction, et que l'analyse du langage fait que mettre des parenthèses fonctionne dans tous les cas, on pense souvent qu'elles sont nécessaires.

    2) je ne comprends pas l'égalité qui doit être testée !
    C'est écrit dans le commentaire : « assure-toi que sun_path est le dernier membre ». La macro s'en acquitte en vérifiant si offset du membre + taille de ce membre = taille totale de la structure.

  3. #43
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Entre temps, j'ai modifié mon petit programme. Je lui ai permis de gérer plusieurs communication d'un coup avec la création de processus fils.
    Je suis content : ça a marché

    Mais, il me reste un problème : le serveur ne peut être arrêté ! Ce n'est pas joli pour un exemple de proposé le Ctrl-C qui ne va pas effacer le socket !

    Citation Envoyé par Obsidian Voir le message
    C'est écrit dans le commentaire : « assure-toi que sun_path est le dernier membre ». La macro s'en acquitte en vérifiant si offset du membre + taille de ce membre = taille totale de la structure.
    OK. Finalement je comprends ces nouvelle fonctionnalités !
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  4. #44
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Citation Envoyé par troumad Voir le message
    Entre temps, j'ai modifié mon petit programme. Je lui ai permis de gérer plusieurs communication d'un coup avec la création de processus fils.
    Je suis content : ça a marché

    Mais, il me reste un problème : le serveur ne peut être arrêté ! Ce n'est pas joli pour un exemple de proposé le Ctrl-C qui ne va pas effacer le socket !
    Il faut donc passer à l'étape suivante : select(), ce qui te permettra, en jonglant bien avec toutes ces notions, de n'utiliser qu'un seul processus serveur pour tous tes clients.

    Vois aussi du côté de signal() et sigaction() pour gérer les handlers de signaux, qui te permettront dans certains cas de faire le ménage quand même en cas de fin prématurée de ton processus.

  5. #45
    Membre émérite

    Homme Profil pro
    Directeur des systèmes d'information
    Inscrit en
    Septembre 2010
    Messages
    450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Directeur des systèmes d'information

    Informations forums :
    Inscription : Septembre 2010
    Messages : 450
    Par défaut
    Citation Envoyé par troumad Voir le message
    Ceci m'informera de la présence active du programme dans la mémoire. Mais comment lui passer un message s'il tourne ?
    J'arrive en retard, désolé.

    http://tldp.org/LDP/lpg/node7.html Voici une doc.
    http://tldp.org/LDP/lpg-0.4.pdf Chapitre 6!

    J'espère que cela t'aidera. En tout cas, le mot clé qui te manquait pour chercher les réponses c'est "IPC", qui désigne les méthodes de communications inter-processus.

    C'est du C un petit peu avancé, mais c'est très intéressant.
    Si vous moinsez, merci de répondre pour argumenter!
    Ma présentation

  6. #46
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Bonjour

    YannPeniguel, ce que tu proposes, c'est pour Linux non ? Pour Linux, il ne me reste plus qu'à tester. Je suis arrivé à mes fins avec les essais que j'ai faits ce WE, tu peux voir le programme que je propose un peu plus haut.
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  7. #47
    Membre émérite

    Homme Profil pro
    Directeur des systèmes d'information
    Inscrit en
    Septembre 2010
    Messages
    450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Directeur des systèmes d'information

    Informations forums :
    Inscription : Septembre 2010
    Messages : 450
    Par défaut
    Je sais, d'ou ma première phrase d'excuse pour le fait d'arriver après coup.
    Mais je me suis dit que même à postériori, des ressources documentaires ne font jamais de mal.
    Si vous moinsez, merci de répondre pour argumenter!
    Ma présentation

  8. #48
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Mon exemple va en rester là : pour GTK, il y a bien mieux. http://live.gnome.org/LibUnique gère tout.
    Pour rendre le code propre et compréhensible, je peux virer la notion père/fils que j'ai testé pour la première fois avec ce petit test. Je suis assez déçu que même avec des passages par adresse, le fils ne puisse rien fournir au père
    On est loin de mes premiers essais sous Sharp PC-1403 puis sous DOS dans un système absolument pas multitâche où l'adresse est une vraie adresse physique !
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  9. #49
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    D'accord mais, avant de refermer le dossier, essaie quand même SELECT().

  10. #50
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Je vais peut-être m'y frotter quand j'aurais un peu de temps. Existe-t'il des exemples commentés en français ?
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  11. #51
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    En gros, c'est l'appel système nécessaire qu'il manque pour pouvoir gérer des connexions simultanées, quelles qu'elles soient. C'est ce qui va te permettre de faire un appel bloquant sur plusieurs sockets à la fois, et être débloqué soit lorsque l'un d'eux à quelque chose à offrir, soit à la fin d'un éventuel laps de temps.

    Tu peux voir éventuellement pselect() et poll(), dans le même esprit.

    Voici un petit programme que j'avais appelé µIRC-server, mais que je n'ai pas finalisé parce qu'il remplissait déjà les fonctions que je voulais vérifier. Il se contente d'accepter toutes les connexions entrantes, de rediffuser vers chaque client les lignes de texte envoyées par l'un d'eux, et de prevenir tout le monde lorsque que quelqu'un se connecte ou se déconnecte.

    Il n'est pas du tout optimisé et ne gère par les erreurs, mais ce n'est pas l'objet de l'exercice :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    /*
     * µIRC-server.c  -     µIRC - Micro-serveur pour exercices sur les sockets.
     *                      Partie serveur.
     *
     *                      Obsidian, le dimanche 17 avril 2011 à 23h30.
     *
     */
     
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <arpa/inet.h>
    #include <sys/select.h>
     
    #define SERVER_PORT 5000
    #define NCNX        16
    #define TOOMANY     "Too Many Open Connections. Sorry.\n"
     
    int main (void)
    {
        int                 s       = -1;
        int                 max     = -1;
        int                 on      =  1;
        int                 d[NCNX];
        struct sockaddr_in  serv;
        fd_set              efds;
        fd_set              rfds;
     
        serv.sin_family                 = AF_INET;
        serv.sin_port                   = htons(SERVER_PORT);
        serv.sin_addr.s_addr            = INADDR_ANY;
     
        /* On passe sur la gestion des erreurs, ici.
         * Çaimal, mais ce n'est pas l'objet de l'exercice. */
     
        s = socket(PF_INET,SOCK_STREAM,0);
        bind (s,(const struct sockaddr *)&serv,(socklen_t)sizeof serv);
     
        memset (d,-1,sizeof d);
     
        listen (s,0);
     
        do
        {
            int i;
     
            max = s;
            FD_ZERO (&rfds);
            FD_SET  (s,&rfds);
            for (i=0;i<NCNX;++i)
            if (d[i]>=0)
            {
                FD_SET (d[i],&rfds);
                if (max<d[i]) max=d[i];
            }
     
            select (max+1,&rfds,NULL,NULL,NULL);
     
            if (FD_ISSET (s,&rfds))
            {
                int             j;
                int             c;
                struct sockaddr clt;
                socklen_t       clt_l;
     
                printf ("Incoming connection ... "); fflush (stdout);
                c = accept (s,&clt,&clt_l);
     
                for (i=0;i<NCNX;++i)
                {
                    if (d[i]==-1)
                    {
                        d[i]=c;
                        break;
                    }
                }
     
                if (i>=NCNX)
                {
                    printf ("rejected because of a lack of available slots.\n");
     
                    write (c,TOOMANY,sizeof TOOMANY);
                    shutdown (c,SHUT_RDWR);
                    close (c);
                }
                else
                {
                    int         n;
                    char        buffer [1024];
     
                    printf ("affected on slot %d.\n",i);
                    snprintf (buffer,sizeof buffer,"Number %d has joined the channel.\n",i);
                    n = strlen (buffer);
     
                    for (j=0;j<NCNX;++j)
                    if (d[j]>=0) write (d[j],buffer,n);
                }
            }
     
            for (i=0;i<NCNX;++i)
            {
                if (FD_ISSET(d[i],&rfds))
                {
                    int         n = 0;
                    int         j = 0;
                    char        buffer [1024];
     
                    memset (buffer,0,sizeof buffer);
                    n = read (d[i],buffer,(sizeof buffer)-1);
     
                    if (!n)
                    {
                        snprintf (buffer,sizeof buffer,"Number %d has quit.\n",i);
                        shutdown (d[i],SHUT_RDWR);
                        close    (d[i]);
                        d[i] = -1;
                    }
                    else if (n>0) buffer[n] = '\0';
     
                    n = strlen(buffer);
     
                    for (j=0;j<NCNX;++j) if (d[j]>=0) write (d[j],buffer,n);
                }
            }
        }
        while (on);
     
        return 0;
    }

    C'est donc un micro-serveur qui gère n connexions avec un seul thread.

    Pas besoin de client dédié pour s'y connecter : un simple telnet ou nc (netcat) sur 127.0.0.1:5000 suffit. Le mieux pour en tirer profit est d'ouvrir trois ou quatre fenêtres en mosaïque sur ton écran.

    À ce stade, dès lors qu'un tel système fonctionne, il suffit de mettre en place un langage de commandes pour lui demander de réagir comme on le souhaite, à commencer par s'éteindre.

  12. #52
    Membre émérite

    Homme Profil pro
    Directeur des systèmes d'information
    Inscrit en
    Septembre 2010
    Messages
    450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Directeur des systèmes d'information

    Informations forums :
    Inscription : Septembre 2010
    Messages : 450
    Par défaut
    Citation Envoyé par troumad Voir le message
    Mon exemple va en rester là : pour GTK, il y a bien mieux. http://live.gnome.org/LibUnique gère tout.
    Pour rendre le code propre et compréhensible, je peux virer la notion père/fils que j'ai testé pour la première fois avec ce petit test. Je suis assez déçu que même avec des passages par adresse, le fils ne puisse rien fournir au père
    On est loin de mes premiers essais sous Sharp PC-1403 puis sous DOS dans un système absolument pas multitâche où l'adresse est une vraie adresse physique !
    La possibilité pour un fils d'écrire dans l'espace mémoire du père de manière directe serait une catastrophe en terme de sécurité.
    Je suis un vilain executable CGI placé chez un hebergeur mutualisé, imagines ce que je pourrais faire? J'ai un accès direct à la mémoire d'apache...

    Ceci explique l'invention des mécanismes IPC pour permettre une communication fiable et sécurisée entre les processus.
    Si vous moinsez, merci de répondre pour argumenter!
    Ma présentation

  13. #53
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Je m'en doute bien ! Mais, je n'avais plus regardé si près de la machine depuis bien longtemps... Je me souviens encore d'une réflexion que j'avais du avoir pour transférer un programme sur disquette : ce programme, sur TO7/70, se chargeait à partir d'une cassette et prenait la place de la mémoire écran ! Il était donc impossible de prendre la main sous peine de perdre les données. L'histoire de plage mémoire protégée n'était pas d'actualité !
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  14. #54
    Membre émérite

    Homme Profil pro
    Directeur des systèmes d'information
    Inscrit en
    Septembre 2010
    Messages
    450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Directeur des systèmes d'information

    Informations forums :
    Inscription : Septembre 2010
    Messages : 450
    Par défaut
    Si tu aimes ce genre de reflexions, le domaine des systèmes embarqués devrait t’intéresser, c'est l'un des rares ou ce genre de reflexions est encore d'actualité.
    Si vous moinsez, merci de répondre pour argumenter!
    Ma présentation

  15. #55
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    J'aimais... Et j'ai du faire un tri dans ce que j'aime...
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  16. #56
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Citation Envoyé par troumad Voir le message
    Je suis assez déçu que même avec des passages par adresse, le fils ne puisse rien fournir au père On est loin de mes premiers essais sous Sharp PC-1403 puis sous DOS dans un système absolument pas multitâche où l'adresse est une vraie adresse physique !
    Bien sûr si, c'est encore possible : ça s'appelle un segment de mémoire partagée. Mais il faut demander au système de te l'allouer d'une part (ça, en soi, ce n'est pas différent d'un malloc) et, d'autre part, tu vas recevoir un descripteur pour cette ressource en particulier. Chaque processus va même pouvoir, dans une certaine mesure et s'il le souhaite, attacher ce segment à l'emplacement qui lui chante dans le plan mémoire qui lui est attribué.

    Ce segment peut être obtenu par héritage (processus père → fils), mais également en utilisant l'identifiant concerné si le processus est lancé indépendamment et a posteriori.

    Les segments de mémoire partagée sont très utilisés. Seulement, leur exploitation implique la prise en charge de petits détails assez rasoirs, et que l'on aurait vite tendance à faire passer à la trappe. À savoir :

    — Sous UNIX, les segments de mémoire partagée font a priori partie des IPC SysV. Ces segments survivent en principe à la mort du processus. Chaque ressource allouée a un identifiant, mais peut être aussi repérée par une clé sur 32 bits. Or, comme le risque de collision est quand même plus grand avec ce système qu'avec un nom de fichier, il peut être intéressant de s'en tenir au socket, se servir de celui-ci pour communiquer avec l'autre processus, et utiliser ce biais pour se faire transmettre l'identifiant du segment idoine ;
    — Curieusement, même si ça choque l'intuition au départ, la manière la plus naturelle, de partager un segment de mémoire est de mapper un fichier avec mmap(). Ceci parce que le mapping de fichier utilise le même procédé que pour la mémoire swap, c'est-à-dire la mise sur disque de page de mémoires anonymes. Et des pages de mémoire anonymes sur disque qui survivent éventuellement à leur processus (pour que d'autres puissent les reprendre) et qui ont un identifiant sur le système de fichier comme pour les sockets… et bien, ce sont tout simplement des fichiers ordinaires.
    — Il faut quand même un mécanisme de synchronisation entre les différents processus : il faut pouvoir être prévenu si quelque chose change dans la mémoire partagée sans avoir à faire du polling (la scruter en permanence). Donc, soit un sémaphore, soit un tube (de préférence) ou un socket que l'on puisse utiliser avec select().

    Pour revenir à la problématique de départ — vérifier si une application est déjà ouverte — les sockets restent quand même les plus indiqués, pour toutes les raisons exposées au-dessus, mais également parce qu'ils fonctionnent au-dessus du NFS, ce qui permet d'utiliser de distribuer éventuellement les applications sur un cluster par exemple, sans avoir à en faire des applications explicitement réseau.

  17. #57
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    les sockets restent quand même les plus indiqués, pour toutes les raisons exposées au-dessus, mais également parce qu'ils fonctionnent au-dessus du NFS, ce qui permet d'utiliser de distribuer éventuellement les applications sur un cluster par exemple, sans avoir à en faire des applications explicitement réseau.
    Juste une petite question : qu'est-ce que c'est pour toi une "application explicitement réseau" ???


    Venant d'un monde Unix et avant ça VMS, pour moi qui dit réseau dit socket...

    La communication intra-machine n'est qu'un cas particulier...

    (encore une fois, comme X11, qui peut bien entendu afficher par défaut sur ton écran, mais est conçu dès le départ pour aller afficher sur le 3ième écran du 4ième noeud du réseau si on veut..)

  18. #58
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Pardon de ne pas avoir répondu plus tôt. J'ai suivi d'autres messages dans le tableau de bord et j'ai oublié d'y revenir ensuite.

    Citation Envoyé par souviron34 Voir le message
    Juste une petite question : qu'est-ce que c'est pour toi une "application explicitement réseau" ???
    J'entends par là une application qui est faite pour exploiter le réseau et, donc, pour être visible depuis l'extérieur de la machine (et surtout du périmètre du système d'exploitation en fonctionnement), que ce soit en tant que serveur ou pour dialoguer avec un autre hôte. Cela implique également l'attribution d'une adresse, et du respect des règles en vigueur sur le réseau entier, contrôlées (et éventuellement établies) par l'administrateur système. J'exclus volontairement les liaisons point-à-point type RS/232 ou autres.

    Sinon, il faudrait considérer les autres mécanismes IPC comme du réseau également.

    Donc, une application qui n'a pas vocation à être visible du réseau mais qui occupe un port simplement pour s'en servir de mutex, comme cela s'est déjà vu, c'est pénible et potentiellement dangereux, spécialement lorsque l'on tourne sur des serveurs mutualisés. Ça se fait plus souvent qu'on ne le croit en entreprise et dans les centres de recherches et c'était jadis la norme avec les machines UNIX.

    Venant d'un monde Unix et avant ça VMS, pour moi qui dit réseau dit socket...
    Dans une certaine mesure, oui, mais ce n'est pas réciproque : qui dit « socket » ne dit pas forcément « réseau ».

    Les sockets sont avant tout une proposition d'interface unifiée pour la communication inter-processus. Ils servent d'un côté à exploiter des liaisons bidirectionnelles et éventuellement multiples (avec accept() et select()) tout en respectant le paradigme du fichier et en restant compatibles avec ses fonctions. Et, de l'autre côté, ces sockets sont définis dans un « domaine » particulier, qui regroupe tous les sockets d'un même type et au sein duquel ceux-ci peuvent être nommés, donc recevoir un identifiant unique qui permet de s'y référer de façon déterministe.

    C'est la même philosophie, à la base, que les tubes nommés et anonymes. Tu peux exploiter l'entrée et la sortie d'un pipe anonyme à condition que les deux homologues soient père et fils, et que le second ait hérité des descripteurs du premier. Sinon, impossible de les retrouver, et le tube disparaît quand tous les processus ont refermé leur descripteur, comme un fichier que l'on effacerait alors qu'il est encore ouvert. Alternativement, tu peux utiliser les tubes nommés. Comme ils sont associés à une entrée sur le système de fichier, les lecteurs et écrivains peuvent ouvrir ce tube a posteriori et de façon asynchrone.

    Même chose pour les sockets, à ceci près qu'il peuvent être « éventuellement » nommés et qu'ils le sont après leur création.

    Et c'est seulement ensuite (même si le cas a dû être prévu dès le départ) que ce modèle s'applique, entre autres, à l'exploitation du réseau.

    La communication intra-machine n'est qu'un cas particulier...
    Non, parce que pour cela, en principe, on n'utilise pas une adresse de loopback mais le domaine AF_UNIX. La communication intra-machine dans ce cas n'est donc pas un sous-ensemble de l'exploitation du réseau en général.

    Un bon exemple est le domaine AF_NETLINK de Linux qui, contrairement à ce que son nom laisse penser, ne sert pas à exploiter le réseau. Le domaine « AF_NETLINK » sert à communiquer avec le noyau. En ce sens, ça permet d'avoir le même genre d'infos que dans « /proc » et l'interface socket permet de se faire prévenir par l'intermédiaire de select() plutôt que faire du polling sur le système de fichiers (ce que fait « tail -f », malheureusement). Parmi les infos proposées, on a des fonctions d'audit, des surveillances de sockets, etc.

    Donc :

    AF_UNIX : communication processus à processus en interne en utilisant le système de fichiers comme point de rendez-vous (pas de réseau nécessaire, pas de suivi d'une quelconque table de routage) ;
    AF_INET : utilisation du protocole Internet. Communication processus à processus en utilisant le domaine des adresses Internet. Après, c'est le système qui fait transiter les données via l'équipement réseau si nécessaire ;
    AF_NETLINK (Linux) : Communication processus à noyau. L'espace d'adressage est représenté par un uint32. À noter que l'on peut quand même communiquer d'un processus à l'autre à condition de connaître l'adresse de son homologue, puisque l'espace d'adressage reste visible en entier, mais ce n'est pas à cela que ce domaine sert.

    Et puis, tant que l'on reste sur le réseau, on a tous pris l'habitude, aujourd'hui, de travailler avec AF_INET, mais rien ne garantit que ce domaine soit disponible sur toutes les machines (pas même sur un Linux, donc la compilation du module concerné est optionnelle, même si le bon sens a conduit à la rendre sélectionnée par défaut depuis le départ), et rien ne garantit non plus que la machine se trouve sur un réseau IP. Elle peut toujours se trouver, par exemple, sur un réseau Novell IPX/SPX, auquel cas même la classe 127.x.x.x n'aurait rien à faire sur la machine-hôte. À ce moment-là, quelle classe choisir par défaut pour faire un programme portable ? AF_IPX ? AF_X25 ? AF_APPLETALK ?

    Et surtout, encore un point important : quand on utilise le domaine AF_INET, on indique au système que l'on souhaite utiliser le protocole Internet, soit. Mais cela signifie aussi que l'on souhaite utiliser son espace d'adressage qui, lui, n'est pas systématiquement privé et qui, globalement, est majoritairement conçu pour être public. Par cela, je veux dire que le programmeur qui a utilisé un port comme mutex a probablement utilisé INADDR_ANY pour se mettre à l'écoute et pas spécialement 127.0.0.0/8. Donc, si la machine est publique — parce qu'Internet sert quand même initialement à ça — alors le monde entier peut vérifier si l'application est lancée et tenter de s'y connecter.

    Pour bien distinguer ce qui relève de l'IPC par socket, et si l'on ne disposait pas de réseau informatique international tel qu'Internet, on pourrait tout-à-fait imaginer un domaine AF_PHONE, par exemple, dans lequel les adresses des sockets seraient les numéros de téléphone des lignes sur lesquelles serait branché le modem de chaque ordinateur. On voit que les numéros de téléphone (même sur liste rouge) sont publics dans le sens où ils peuvent être joints depuis n'importe quelle compagnie de téléphone et depuis n'importe quel pays, et il est tout-à-fait possible d'envisager qu'un connect() sur un socket de ce type provoque la numérotation du modem et l'établissement d'une connexion point-à-point avec l'ordinateur distant, ainsi identifié. Tout le système de tube et les signaux associés (notamment SIGHUP) permettent de le faire.

    (encore une fois, comme X11, qui peut bien entendu afficher par défaut sur ton écran, mais est conçu dès le départ pour aller afficher sur le 3ième écran du 4ième noeud du réseau si on veut..)
    X11 est une application réseau, mais c'est avant tout un serveur. Et c'est parce qu'elle a été architecturée de cette façon que les clients qui l'exploitent peuvent le faire à travers le réseau.

  19. #59
    Rédacteur/Modérateur
    Avatar de troumad
    Homme Profil pro
    Enseignant
    Inscrit en
    Novembre 2003
    Messages
    5 608
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 5 608
    Par défaut
    Bonsoir

    Je n'ai même pas testé la compilation, mais je travaille sur l'exemple...

    J'ai rajouté des commentaires pour expliquer ton code. J'espère que je ne me suis trop trompé !

    Je pense que dans ton exemple, tu n'as pas mis la réception des messages des clients par le serveur. ni la désactivation des clients morts afin d'éviter une saturation trop rapide
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <unistd.h>
     
    #define SERVER_PATH     "/tmp/server"
    #define BUFFER_LENGTH    250
    #define FALSE              0
    /* NCNX : nombre maximum d'entrées traitées en // */
    #define NCNX        16
    #define TOOMANY     "Trop de connexion ouvertes. Désolé.\n"
     
    #ifndef SUN_LEN
    #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen ((ptr)->sun_path)) 
    #endif
     
    int main(int argc, char *argv[])
    {
       int    sd=-1, sd2=-1, rc,i,j,c;
       char   buffer[BUFFER_LENGTH],buffer1[BUFFER_LENGTH];
       struct sockaddr_un serveraddr;
       struct sockaddr clt;
       socklen_t       clt_l;
       char d,cont=1, * fin=&cont;
       pid_t pid;
       int                 max     = -1;
       int                 on      =  1;
       int                 slot[NCNX];   fd_set              efds;
       fd_set              rfds; 
     
          sd = socket(AF_UNIX, SOCK_STREAM, 0); /* création du socket AF_UNIX : on ne passe pas par un port, ce sera interne au PC	*/
          if (sd < 0)                          		/* retour un entier >=0 si ça c'est bien passé, sinon -1		*/
          {
             perror("echec de socket()");
          }
          else
          {
    	serveraddr.sun_family = AF_UNIX;
    	strcpy(serveraddr.sun_path, SERVER_PATH);
     	rc = connect(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
    	if (rc < 0)					/*  No such file or directory est gténéré par le connect		*/
    	{						/* car il n'y a pas de serveur à écouter				*/
    	  perror("serveur absent : le programme devient serveur");
     
    	  memset (slot,-1,sizeof slot);			/* tous les slots sont déclarés libres					*/
    	  memset(&serveraddr, 0, sizeof(serveraddr));	/* remplir de 0 la chaîne d'entrée sortie 				*/
    	  serveraddr.sun_family = AF_UNIX;           	/* choix de la forme de socket            				*/
    	  strcpy(serveraddr.sun_path, SERVER_PATH);	/* emplacement du répertoire temporaire  				*/
    	  rc = bind(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
    	  if (rc < 0)					/* donne un nom à la socket						*/
    	  {
    	    perror("echec de bind()");
    	    return 1;
    	  }
    	  rc = listen(sd, 0);				/* socket utilisée pour accepter les demandes de connexions entrantes	*/
    	  if (rc< 0)
    	  {
    	    perror("echec de listen()");
    	    return 2;
    	  }
     
    	  do
    	  {  
                max=sd;					/* max retiend le + grand des descripteurs, pas le + grd nb d'entrées	*/
                FD_ZERO (&rfds); 	    			/* efface rfds : uniquement en écoute					*/
                FD_SET  (sd,&rfds);	    			/* ajoute un descripteur dans l'ensemble : l'écoute sur notre port	*/
    	    for (i=0;i<NCNX;++i)			/* Parcourt les entrées possibles					*/
    	      if (slot[i]>=0)				/* Si l'entrée est active						*/
    	      {
    		  FD_SET (slot[i],&rfds);		/* la rajouter à l'ensemble des entrées à écouter			*/
    		  if (max<slot[i]) max=slot[i];		/* la prendre en compte pour le max					*/
    	      }	
    	    select (max+1,&rfds,NULL,NULL,NULL);	/* on est uniquement en écoute : les trois autres entrées sont à NULL	*/
     
    	    if (FD_ISSET (sd,&rfds))
    	    {						/* s'il y a un nouveau appel : un nouveau client veut communiquer	*/
    		printf ("Connexion entrante ... "); fflush (stdout);
    		c = accept (sd,&clt,&clt_l);
     
    		for (i=0;i<NCNX;++i)
    		{
    		    if (slot[i]==-1)			/* trouver le premier emplacement libre dans slot[]			*/
    		    {
    			slot[i]=c;			/* l'affecter à la nouvelle entrée					*/
    			break;				/* on sort du for sans mettre au max pour le if suvant			*/
    		    }
    		}
     
    		if (i>=NCNX)
    		{
    		    printf ("rejétée car plus de slots disponible.\n");
     
    		    write (c,TOOMANY,sizeof TOOMANY);	/* prévient le client potentiel qu'il ne sera pa pris en compte		*/
    		    shutdown (c,SHUT_RDWR);
    		    close (c);
    		}
    		else
    		{
    		    int         n;
    		    char        buffer [1024];
     
    		    printf ("affectée au slot %d.\n",i);	/* signal au client qu'il est accepté				*/
    		    snprintf (buffer,sizeof buffer,"numéro %d a rejoint le canal.\n",i);
    		    n = strlen (buffer);	    
    		    write (slot[i],buffer,n);		/* envoi du message							*/
    		}      
    	    }
    	    for (i=0;i<NCNX;++i)			/* Parcourt les entrées possibles					*/
    	      if (slot[i]!=-1 && FD_ISSET (slot[i],&rfds))/* Si l'entrée a reçu un message					*/
    	      {						/* traitement du message						*/
    		rc=read(slot[i],buffer,BUFFER_LENGTH-1);/* lecture du message							*/
    		if (rc < 0)
    		{
    		  perror("echec de recv()");
    		} 
    		else if (rc == 0 )
    		{					/* message pour signaler la fermeture du client				*/
    		  printf("Le client %d a fermé la connexion\n",i);
    		  slot[i]=-1;				/* on libère le slot							*/
    		}
    		else
    		{
    		  if (rc<BUFFER_LENGTH)
    		      buffer[rc]=0;	 /*finir la chaine de caractères par un 0 car il n'est pas mis en fin du message	*/
     
    		  c=0;
    		  for (j=0;j<strlen(buffer);j++)	/* fabrication du code de vérification					*/
    		  {
    		    c^=buffer[j];			/* un ou exclusif tout simple sur un octet				*/
    		  }
    		  if (strcmp(buffer,"fin")==0) on=0;
    		  printf("%d bites de données ont été reçus du client %d signature %hu : %s\n", rc,i,c,buffer);
    		  sprintf(buffer,"%hu",c);		/* préparation et envoi du code de vérification à l'envoyeur		*/
    		  rc = write(slot[i], buffer, strlen(buffer));
    		  if (rc < 0)
    		  {
    		    perror("echec de send()");
    		  }
    		}
     
    	      }	
     
    	  } while (on);
    	  remove (SERVER_PATH);
    	}
            else						/* le serveur est présent : le programme prend la version client	*/
    	{
    	    rc = recv(sd,buffer1,BUFFER_LENGTH, 0);	/* recception du signal d'entrée					*/
    	    if (rc == 0)
    	    {
    		printf("Le serveur a fermé la connexion\n");
    		return 0;
    	    }
    	    if (rc<BUFFER_LENGTH)
    	      buffer1[rc]=0;				/*finir la chaine de caractères par un 0				*/
    	    printf("%d : %s\n",rc,buffer1);
    	    do
    	    { 						/* boucle sur la connexion ouverte					*/
    		printf("Message à envoyer au serveur (fin pour arrêter le serveur) : ");
    		fflush(stdin);
    		fgets(buffer, BUFFER_LENGTH, stdin);
    		buffer[strlen(buffer)-1]=0;		/* enlever le /n de la fin, le remplacer par un 0			*/
     
    		rc = send(sd, buffer, strlen(buffer), 0);	/* envoie du message au serveur					*/
    		//rc = write(sd, buffer, strlen(buffer));
    		if (rc < 0)
    		{
    		  perror("echec de send()");
    		  break;
    		}
     
    		c=0;					/* calcul du code de vérification					*/
    		for (i=0;i<strlen(buffer);i++)
    		{
    		  c^=buffer[i];
    		}            				/* réception du code de vérification du serveur				*/
    		rc = recv(sd,buffer1,BUFFER_LENGTH, 0);
    		if (rc == 0)
    		{
    		    printf("Le serveur a fermé la connexion\n");
    		    break;
    		}
    		if (rc<BUFFER_LENGTH)
    		  buffer1[rc]=0;			/* finir la chaine de caractères par un 0				*/
    		printf("%d : %s\n",rc,buffer1);
    		sscanf(buffer1,"%hu",&i);		/* lecture du code de vérification envoyé par le serveur		*/
    		if (c==i)				/* vérification du code							*/
    		  printf("Vérification OK\n");
    		else
    		  printf("Erreur dans le transfert ici %hu, là-bas : %hu\n",c,i);
    		fflush(stdin);
    		printf("Continuer ? (o/n) : ");
    		do
    		  c=getchar();	  
    		while (c!='o' && c!='n'); 
    		do					/* vider le buffer jusqu'au saut de ligne				*/
    		{
    		  d=getchar();	  
    		  printf("%c",d);
    		}
    		while (d!='\n'); 
    	    }
    	    while(c=='o');
    	}
     
       }
       if (sd != -1)
          close(sd);
       if (sd2 != -1)
          close(sd2);
     
       return 0;
    }
    Ai-je dit des bétises ?
    Modérateur Mageia/Mandriva Linux
    Amicalement VOOotre
    Troumad Alias Bernard SIAUD à découvrir sur http://troumad.org
    Mes tutoriels : xrandr, algorigramme et C, xml et gtk...

  20. #60
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    C'est pas mal du tout ! Quelques remarques tout de même :

    — Tu as plusieurs fflush(stdin) dans ta partie client et qui ne servent à rien : tu ne pourras jamais vider le buffer d'entrée de cette façon ;
    — Tu as quelques break en dehors de boucles ;
    — À la ligne 77, à côté du select(), tu précises « on est uniquement en écoute ». En fait, l'argument concerné spécifie les descripteurs à débloquer s'ils sont disponibles en lecture. Et il se trouve que c'est dans cette catégorie que select() range les sockets sur lesquelles arrive une connexion entrante.

    Et surtout, ta partie client attend une entrée utilisateur, puis attend un message du serveur, puis une confirmation utilisateur, et recommence. Cela veut dire que tant que l'utilisateur n'a pas répondu, les messages du serveur ne peuvent être traités. Il faut donc utiliser select() du côté client également.

Discussions similaires

  1. Vérifier que le programme est ouvert
    Par Loenix dans le forum Programmation multimédia/Jeux
    Réponses: 2
    Dernier message: 14/05/2009, 15h50
  2. Vérifier si un programme est ouvert
    Par wonderboutin123 dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 03/02/2008, 11h36
  3. Vérifier si calc windows est ouverte ou pas???
    Par electrosat03 dans le forum Contribuez
    Réponses: 4
    Dernier message: 10/03/2006, 19h28
  4. Vérifier qu'un formulaire est ouvert
    Par com800 dans le forum WinDev
    Réponses: 2
    Dernier message: 07/04/2005, 20h27
  5. Vérifier si une form est ouverte
    Par nivet dans le forum Langage
    Réponses: 6
    Dernier message: 23/11/2004, 09h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo