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 :

FIFO à vider entre 2 lectures


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Janvier 2011
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Janvier 2011
    Messages : 20
    Par défaut FIFO à vider entre 2 lectures
    Bonjour,
    Je débute en C, veuillez d'avance excuser toute énormités/bêtise/erreur que je pourrais écrire ici.

    Voici mon problème. Je cherche à utiliser un FIFO pour périodiquement afficher des informations dans une barre de status.

    Cependant, il semble que le FIFO ne soit pas vidé entre deux lectures. Sinon, c'est que le tableau qui me sert à stocker le contenu du FIFO n'est pas vidé entre 2 exécutions.

    En effet, lorsque je lance echo "treslongmessage" >> /tmp/dwmbuf → pas de soucis.
    Si ensuite je lance echo "mini" >> /tmp/dwmbuf → il s'affiche alors "mini[]longmessage"

    Voici la fonction qui permet de lire le FIFO :

    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
    char *
    snotif()
    {
        char buf[BUFSIZ];
        char *retstr = NULL;
        buf[0]='\0';
     
        int f = open(FIFO, O_NONBLOCK);
        if (f == -1){
            perror("fifo opening");
            return smprintf("%s","");
        }
     
        if (read(f, buf, sizeof(buf) ) == -1){
            perror("fifo read");
            return smprintf("%s","");
        }
        close(f);
     
        buf[strlen(buf)-1] = '\0';
        retstr = smprintf("%s",buf);
     
        return smprintf("%s",retstr);
    }
    La fonction smprintf
    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
     
    char *
    smprintf(char *fmt, ...)
    {
    	va_list fmtargs;
    	char *buf = NULL;
     
    	va_start(fmtargs, fmt);
    	if (vasprintf(&buf, fmt, fmtargs) == -1){
    		fprintf(stderr, "malloc vasprintf\n");
    		exit(1);
        }
    	va_end(fmtargs);
     
    	return buf;
    }
    Et voici la boucle dans main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
        #define FIFO "/tmp/dwmbuf"
        // code coupé
        char *info = NULL;
        int ret = 0;
        ret = mkfifo(FIFO, ACCESSPERMS);
        if (ret == -1) perror("fifo creation");
        for (;;sleep(1)) {
                info = snotif();
            }

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 839
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 839
    Billets dans le blog
    1
    Par défaut
    Salut
    Un fifo étant avant tout un fichier, il faut spécifier dans open() le mode d'ouverture (O_RDONLY ou O_WRONLY) selon que tu veux lire ou écrire dedans.
    Si tu veux faire les deux, il faudra alors spécifier O_RDWR.

    Tu peux concaténer tes options d'ouverture avec d'autres (comme le O_NONBLOCK) en utilisant le "ou" bit à bit => O_RDONLY | O_NONBLOCK. Mais il faut alors comprendre comment se comporte ton fifo s'il n'y a rien à lire => comme c'est non bloquant il redonne la main. Comme il n'a rien lu il renvoie alors (-1) mais comme ce n'est pas vraiment une erreur il positionne errno à EAGAIN. Et donc quand read() renvoie -1 il faut alors vérifier que errno n'est pas à EAGAIN pour conclure qu'il y a un souci. Dans le cas contraire, tu peux retourner à la lecture au cas où il aurait été rempli entre temps.

    Autre chose: tu utilises strlen() pour trouver la fin de buf afin de lui mettre un '\0'. Mais strlen() se base, lui, sur la présence du '\0' pour trouver la longueur. C'est un peu comme les auto références dans Excel...
    Cependant, comme buf n'est pas sensé avoir de '\0', strlen() ne peut pas être utilisé. Et s'il est sensé l'avoir, alors ça ne sert plus à rien de vouloir le remettre...

    2 méthodes possibles
    1. comme read() renvoie le nb d'octets lus, tu récupères ce retour dans une variable nb. Et ensuite tu peux mettre un '\0' dans buf[nb]
    2. du coté émetteur tu envoies strlen(chaine) + 1. Ce n'est pas bien lourd un octet en plus et comme cet octet est justement celui qui contient le '\0', tu es sûr de le récupérer de l'autre coté


    Accessoirement je n'aime pas les for (;;). C'est se donner un style "je suis à l'aise" mais ça aussi vite de mettre un while (1) et ça devient bien plus lisible...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre averti
    Inscrit en
    Janvier 2011
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Janvier 2011
    Messages : 20
    Par défaut Merci
    Tout d'abord, un grand merci pour avoir pris le temps de détailler cette réponse. J'y ai beaucoup appris en quelques lignes qu'en une demi-journée de recherche.

    J'ai donc pu modifier le code afin que cela fonctionne. Désormais, voici à quoi il ressemble :
    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
     
    char *
    snotif()
    {
        char buf[BUFSIZ];
        int len = 0;
     
        int f = open(FIFO, O_NONBLOCK | O_RDWR);
        if (f == -1){
            if (errno == EAGAIN) perror("fifo opening");
            return smprintf("%s","");
        }
     
        len = read(f, buf, sizeof(buf));
        if (len == -1){
            perror("fifo read");
            return smprintf("%s","");
        }
        close(f);
     
        buf[len-1] = '\0';
     
        return smprintf("%s",buf);
    }
    Maintenant, je sais à quoi m'en tenir pour strlen.
    J'utilise malgré tout buf[len-1] = '\0'; puisque je veux me débarasser du caractère de retour à la ligne.

    Pour le for(;;...), vous pensez bien qu'il n'est pas de moi, mais du code d'origine piqué sur suckless.org.

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 487
    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 487
    Par défaut
    Il y a quand même quelque chose qui me fait peur dans ton code : on ne sait pas qui est le premier à lire ou à écrire dans ton tube nommé et s'il y a une erreur lors de mkfifo(), tu affiches bien le message d'erreur mais tu n'interromps pas le programme, qui continue tranquillement jusqu'au open(). Et là, si le fichier n'existe pas, il sera créé comme un fichier ordinaire, ce qui expliquerait pourquoi tu récupères les infos que tu as passées d'une exécution à l'autre.

    Fais un « ls -l /tmp/dwmbuf » depuis la ligne de commande et examine le tout premier caractère de la réponse, à gauche des flags. Si c'est un « p » (comme pipe), pas de problème. Si c'est un tiret « - », c'est un fichier ordinaire.

  5. #5
    Membre averti
    Inscrit en
    Janvier 2011
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Janvier 2011
    Messages : 20
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Fais un « ls -l /tmp/dwmbuf » depuis la ligne de commande et examine le tout premier caractère de la réponse, à gauche des flags. Si c'est un « p » (comme pipe), pas de problème. Si c'est un tiret « - », c'est un fichier ordinaire.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    prwxr-xr-x 1 xavier xavier 0 juin  22 08:31 /tmp/dwmbuf|
    C'est bon.
    Je ne veux pas que le programme s'arrête s'il y a un souci.
    Je corrige pour ne pas lire le pipe s'il y a eu un souci.

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 839
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 839
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Thuban Voir le message
    Tout d'abord, un grand merci pour avoir pris le temps de détailler cette réponse. J'y ai beaucoup appris en quelques lignes qu'en une demi-journée de recherche.

    J'ai donc pu modifier le code afin que cela fonctionne. Désormais, voici à quoi il ressemble :
    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
    char *snotif()
    {
        char buf[BUFSIZ];
        int len = 0;
     
        int f = open(FIFO, O_NONBLOCK | O_RDWR);
        if (f == -1){
            if (errno == EAGAIN) perror("fifo opening");
            return smprintf("%s","");
        }
     
        len = read(f, buf, sizeof(buf));
        if (len == -1){
            perror("fifo read");
            return smprintf("%s","");
        }
        close(f);
     
        buf[len-1] = '\0';
     
        return smprintf("%s",buf);
    }
    Mouais, ben t'as pas bien compris. Déjà le O_RDWR ne se justifie que si tu as l'intention de lire et d'écrire sur le descripteur renvoyé par open(). Si tu n'as que l'intention de lire ce descripteur, alors le mode O_RDONLY suffit. Même si tu as par ailleurs ouvert le même fichier en mode O_WRONLY.

    Ensuite le EAGAIN (signifiant grosso-modo "essaye encore") s'applique au read() et non à l'open(). C'est logique. C'est "à la lecture s'il n'y a rien à lire je renvoie -1 et j'indique EAGAIN). Je ne vois pas trop pourquoi open() positionnerait un EAGAIN.

    Dernier détail: évite de te focaliser sur "-1" en cas d'erreur. C'est vrai que toutes les fonctions système renvoient généralement -1 mais il peut arriver d'avoir autre chose (toutefois toujours négatif).
    Donc teste tes retours de tes fonctions sur < 0.

    Citation Envoyé par Thuban Voir le message
    Maintenant, je sais à quoi m'en tenir pour strlen.
    De même que pour toute fonction de traitement de chaines (strcat, strcpy, strcmp, etc...) qui ne peuvent être utilisées que si toi tu garantis un '\0' dans la zone traitée...

    Citation Envoyé par Thuban Voir le message
    J'utilise malgré tout buf[len-1] = '\0'; puisque je veux me débarasser du caractère de retour à la ligne.
    ok, dans ce cas vaut mieux appliquer la méthode suivante
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt;
    if ((pt=strrchr(buf, '\n')) != NULL) *pt='\0';
    C'est plus propre (tester la présence du '\n' avant de le supprimer) mais ça marche à condition, là aussi, qu'il y ait déjà un '\0' dans buf pour que strrchr() puisse se placer dessus avant de remonter la chaine...

    Citation Envoyé par Thuban Voir le message
    Pour le for(;;...), vous pensez bien qu'il n'est pas de moi, mais du code d'origine piqué sur suckless.org.
    Ben non, on n'en sait rien... mais ce n'est pas parce que d'autres font un truc pourri qu'il faut faire pareil
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    for (;; truc1) {
        truc2;
        truc3;
        truc4;
    }
    N'importe qui aura un bref moment de blocage avant de se souvenir que truc1 se fera en fin de boucle.

    Personnellement, je ne mets dans mon for que des éléments directement associés à ma variable, jamais autre chose.
    Exemple: je veux refaire strlen()
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char *pt;
    unsigned long nb;
    nb=0;
    for (pt=chaine; *pt != '\0'; pt++)
        nb++;
    return nb;

    Alors que la syntaxe suivante renvoie la même chose
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char *pt;
    unsigned long nb;
    for (pt=chaine, nb=0; *pt != '\0'; pt++, nb++);
    return nb;
    Mais je préfère la première parce que nb n'étant pas lié aux variables qui traitent la chaine, n'a rien à faire dans le for. Bon ici c'est un peu exagéré car je pense qu'on peut considérer que nb étant égal à la longueur de la chaine il lui est lié mais c'est pour donner un exemple
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Membre averti
    Inscrit en
    Janvier 2011
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Janvier 2011
    Messages : 20
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Mouais, ben t'as pas bien compris. Déjà le O_RDWR ne se justifie que si tu as l'intention de lire et d'écrire sur le descripteur renvoyé par open(). Si tu n'as que l'intention de lire ce descripteur, alors le mode O_RDONLY suffit. Même si tu as par ailleurs ouvert le même fichier en mode O_WRONLY.
    J'ai choisi O_RDWD car lorsque j'utilisais O_RDONLY, le pipe n'était pas vidé à chaque lecture. Le dernier contenu reste présent puisqu'il est retrouvé à chaque cycle. J'ai supposé qu'il ne pouvait pas être vidé puisque pour cela, il fallait avoir les droits d'écriture. D'ailleurs, avec O_RDWR, le problème disparaît. C'est une erreur?

    Ensuite le EAGAIN (signifiant grosso-modo "essaye encore") s'applique au read() et non à l'open(). C'est logique. C'est "à la lecture s'il n'y a rien à lire je renvoie -1 et j'indique EAGAIN). Je ne vois pas trop pourquoi open() positionnerait un EAGAIN.
    C'est noté. De toute façon, quelle que soit l'erreur, je ne souhaite pas retourner du texte, donc est-ce nécessaire de distinguer EAGAIN d'une autre erreur?

    Je prend note aussi des conseils suivants.

    ok, dans ce cas vaut mieux appliquer la méthode suivante
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *pt;
    if ((pt=strrchr(buf, '\n')) != NULL) *pt='\0';
    C'est plus propre (tester la présence du '\n' avant de le supprimer) mais ça marche à condition, là aussi, qu'il y ait déjà un '\0' dans buf pour que strrchr() puisse se placer dessus avant de remonter la chaine...
    Lors d'un echo "texte" >> /tmp/fifo , y a-t-il '\0' à la fin de "texte"?

    Ben non, on n'en sait rien... mais ce n'est pas parce que d'autres font un truc pourri qu'il faut faire pareil
    J'apprend tout seul, du coup je recopie beaucoup. Je n'ai pas encore assez de recul.

    Merci pour tous les conseils.

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 839
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 839
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Thuban Voir le message
    J'ai choisi O_RDWD car lorsque j'utilisais O_RDONLY, le pipe n'était pas vidé à chaque lecture. Le dernier contenu reste présent puisqu'il est retrouvé à chaque cycle. J'ai supposé qu'il ne pouvait pas être vidé puisque pour cela, il fallait avoir les droits d'écriture. D'ailleurs, avec O_RDWR, le problème disparaît. C'est une erreur?
    C'est une erreur que d'essayer des solutions au petit bonheur car en C, ce n'est pas parce que "ça fonctionne" que c'est correct. C'est en effet un des plus grand dangers du C: faire un truc interdit; car dans ce cas ça amène à un comportement indéterminé. Et un comportement indéterminé c'est "indéterminé" (on ne peut pas le prédire). Ca peut fonctionner comme ça peut planter. Ca peut fonctionner 3 ans puis un jour tu rajoutes un printf() et là ça plante. Et là tu ne comprends plus pourquoi le printf() fait planter.

    La bonne attitude est de chercher ce qui ne va pas. Un exemple précis: hier, j'ai galéré 1h sur un truc. J'envoyais une chaine dans un socket (un fifo réseau). Ca marchait quand je disais "balance 2455 octets" et ça bloquait à 2456.
    J'ai galéré 1h puis j'ai trouvé l'erreur qui était toute conne. En fait, je balançais 2456 octets à partir d'une chaine donnée par le système (argv[1] correspondant à mon premier argument). Or argv[1] n'est pas forcé de contenir 2456 octets. Donc en demandant 2456 octets à partir de cette zone je tapais dans une zone dans laquelle je n'ai pas forcément le droit de taper d'où comportement indéterminé (ça fonctionne à 2455 et ça bloque à 2456).
    Solution: J'ai commencé par recopier argv[1] dans une zone locale puis j'ai balancé les 2456 octets à partir de cette zone sur mon socket. Et là ça a fonctionné (y compris avec plus que 2456). Normal, à ce moment là la zone étant à moi j'avais tout à fait le droit de lire son contenu et l'envoyer où je voulais.

    Donc si tu veux trouver ton pb, déjà shunte ta fonction smprintf() et regarde si ça fonctionne. Puis continues ainsi en procédant par éliminations...

    Citation Envoyé par Thuban Voir le message
    C'est noté. De toute façon, quelle que soit l'erreur, je ne souhaite pas retourner du texte, donc est-ce nécessaire de distinguer EAGAIN d'une autre erreur?
    Non. Tester EAGAIN sert juste à savoir qu'on a le droit de retenter une lecture mais bien entendu uniquement si on a l'intention de retenter une lecture...

    Citation Envoyé par Thuban Voir le message
    Lors d'un echo "texte" >> /tmp/fifo , y a-t-il '\0' à la fin de "texte"?
    Non. Le '\0' n'est qu'une convention utilisée par les concepteurs du C et par les programmeurs de C. Ton echo n'étant pas lié au C, il n'a aucune raison d'envoyer un '\0' en plus. Et donc fifo ne contient que 't', 'e', 'x', 't', 'e', '\n' (et encore le '\n' n'y sera pas si tu rajoutes l'option "-n" à ton echo)...

    Mais tu as une solution
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char zone[5000];
    int nb;
    nb=read(fd, zone, 5000);
    zone[nb]='\0';
    Et là tu as ton '\0' ce qui te permet ensuite de manipuler ta zone comme si c'était une chaine (je dis "comme si" mais en fait c'est une chaine au sens "C" du terme)...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 17
    Dernier message: 07/05/2008, 10h16
  2. Réponses: 2
    Dernier message: 12/01/2006, 10h28
  3. [SAX] Problème SAX lecture du texte entre les balises
    Par BernardT dans le forum Format d'échange (XML, JSON...)
    Réponses: 1
    Dernier message: 07/07/2005, 17h24
  4. Lecture standard et communication entre processus!
    Par Tartar Ukid dans le forum C++Builder
    Réponses: 5
    Dernier message: 05/07/2003, 16h37
  5. garder un FIFO ouvert en lecture
    Par OuiOui dans le forum Réseau
    Réponses: 4
    Dernier message: 05/01/2003, 19h24

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