GNU/Linux >> Tutoriels Linux >  >> Linux

O_RDWR sur les canaux nommés avec poll()

Selon la page de manuel open(2), vous pouvez passer O_RDONLY|O_NONBLOCK ou O_WRONLY|O_NONBLOCK pour éviter le open syscall à bloquer (vous obtiendrez errno == ENXIO dans ce cas)

Comme je l'ai commenté, lisez également les pages de manuel fifo(7) et mkfifo(3).


Tout d'abord, quelques préliminaires :

Utilisation de O_NONBLOCK et poll() est une pratique courante - et non l'inverse. Pour travailler avec succès, vous devez être sûr de gérer tous les poll() et read() retourner les états correctement :

  • read() valeur de retour de 0 signifie EOF - l'autre côté a fermé sa connexion. Cela correspond (généralement, mais pas sur tous les OS) à poll() renvoyant un POLLHUP revent. Vous voudrez peut-être vérifier POLLHUP avant de tenter read() , mais ce n'est pas absolument nécessaire depuis read() est garanti de retourner 0 après la fermeture du côté écriture.
  • Si vous appelez le read() avant qu'un rédacteur ne se soit connecté, et vous avez O_RDONLY | O_NONBLOCK , vous obtiendrez EOF (read() retour 0 ) à plusieurs reprises, comme vous l'avez remarqué. Cependant, si vous utilisez poll() attendre un POLLIN événement avant d'appeler read() , il attendra que l'enregistreur se connecte et ne produira pas les EOF.
  • read() valeur de retour -1 signifie généralement erreur. Cependant, si errno == EAGAIN , cela signifie simplement qu'il n'y a plus de données disponibles pour le moment et que vous ne bloquez pas, vous pouvez donc revenir à poll() au cas où d'autres appareils auraient besoin d'être manipulés. Si errno == EINTR , puis read() a été interrompu avant la lecture des données, et vous pouvez soit revenir à poll() ou appelez simplement le read() à nouveau immédiatement.

Maintenant, pour Linux :

  • Si vous ouvrez côté lecture avec O_RDONLY , alors :
    • Le open() bloquera jusqu'à ce qu'un écrivain correspondant soit ouvert.
    • poll() donnera un POLLIN se déclenche lorsque les données sont prêtes à être lues, ou EOF se produit.
    • read() se bloquera jusqu'à ce que le nombre d'octets demandé soit lu, que la connexion soit fermée (renvoie 0), qu'elle soit interrompue par un signal ou qu'une erreur IO fatale se produise. Ce type de blocage va à l'encontre du but de l'utilisation de poll() , c'est pourquoi poll() est presque toujours utilisé avec O_NONBLOCK . Vous pouvez utiliser un alarm() se réveiller de read() après un délai d'attente, mais c'est trop compliqué.
    • Si le rédacteur se ferme, le lecteur recevra un poll() POLLHUP événement et read() renverra 0 indéfiniment ensuite. À ce stade, le lecteur doit fermer son descripteur de fichier et le rouvrir.
  • Si vous ouvrez côté lecture avec O_RDONLY | O_NONBLOCK , alors :
    • Le open() ne bloquera pas.
    • poll() donnera un POLLIN événement lorsque les données sont prêtes à être lues, ou EOF se produit. poll() bloquera également jusqu'à ce qu'un écrivain soit disponible, s'il n'y en a pas.
    • Une fois toutes les données actuellement disponibles lues, read() renverra -1 et définira errno == EAGAIN si la connexion est toujours ouverte, sinon elle renverra 0 si la connexion est fermée (EOF) ou pas encore ouverte par un écrivain . Quand errno == EAGAIN , cela signifie qu'il est temps de revenir à poll() , puisque la connexion est ouverte mais qu'il n'y a plus de données. Quand errno == EINTR , read() n'a encore lu aucun octet et a été interrompu par un signal, il peut donc être redémarré.
    • Si le rédacteur se ferme, le lecteur recevra un poll() POLLHUP événement, et read() renverra 0 indéfiniment ensuite. À ce stade, le lecteur doit fermer son descripteur de fichier et le rouvrir.
  • (Spécifique à Linux :) Si vous ouvrez du côté lecture avec O_RDWR , alors :
    • Le open() ne bloquera pas.
    • poll() donnera un POLLIN lorsque les données sont prêtes à être lues. Cependant, pour les canaux nommés, EOF ne causera pas POLLIN ou POLLHUP événements.
    • read() se bloquera jusqu'à ce que le nombre d'octets demandé soit lu, qu'il soit interrompu par un signal ou qu'une autre erreur IO fatale se produise. Pour les canaux nommés, il ne renverra pas errno == EAGAIN , il ne renverra même pas 0 un des. Il restera là jusqu'à ce que le nombre exact d'octets demandés soit lu, ou jusqu'à ce qu'il reçoive un signal (auquel cas il renverra le nombre d'octets lus jusqu'à présent, ou renverra -1 et définira errno == EINTR si aucun octet n'a été lu jusqu'à présent).
    • Si le rédacteur se ferme, le lecteur ne perdra pas la possibilité de lire le tube nommé plus tard si un autre rédacteur ouvre le tube nommé, mais le lecteur ne recevra aucune notification non plus.
  • (Spécifique à Linux :) Si vous ouvrez du côté lecture avec O_RDWR | O_NONBLOCK , alors :
    • Le open() ne bloquera pas.
    • poll() donnera un POLLIN lorsque les données sont prêtes à être lues. Cependant, EOF ne causera pas POLLIN ou POLLHUP revents sur les canaux nommés.
    • Une fois toutes les données actuellement disponibles lues, read() renverra -1 et définissez errno == EAGAIN . C'est le moment de revenir à poll() pour attendre plus de données, éventuellement d'autres flux.
    • Si l'écrivain se ferme, le lecteur ne perdra pas la possibilité de lire le tube nommé plus tard si un autre écrivain ouvre le tube nommé. La connexion est persistante.

Comme vous êtes à juste titre concerné, en utilisant O_RDWR avec des tuyaux n'est pas standard, POSIX ou ailleurs.

Cependant, comme cette question semble revenir souvent, la meilleure façon sous Linux de créer des "canaux nommés résilients" qui restent actifs même lorsqu'un côté se ferme, et qui ne causent pas POLLHUP renvoie ou retourne 0 pour read() , est d'utiliser O_RDWR | O_NONBLOCK .

Je vois trois manières principales de gérer les canaux nommés sous Linux :

  1. (Portable.) Sans poll() , et avec un seul tube :

    • open(pipe, O_RDONLY);
    • Boucle principale :
      • read() autant de données que nécessaire, éventuellement en boucle sur read() appels.
        • Si read() == -1 et errno == EINTR , read() tout recommencer.
        • Si read() == 0 , la connexion est fermée et toutes les données ont été reçues.

  2. (Portable.) Avec poll() , et en s'attendant à ce que les canaux, même nommés, ne soient ouverts qu'une seule fois, et qu'une fois fermés, ils doivent être rouverts à la fois par le lecteur et l'auteur, en créant un nouveau pipeline :

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • Boucle principale :
      • poll() pour POLLIN événements, éventuellement sur plusieurs canaux à la fois. (Remarque :Cela empêche read() d'obtenir plusieurs EOF avant qu'un écrivain ne se soit connecté.)
      • read() autant de données que nécessaire, éventuellement en boucle sur read() appels.
        • Si read() == -1 et errno == EAGAIN , revenir à poll() étape.
        • Si read() == -1 et errno == EINTR , read() tout recommencer.
        • Si read() == 0 , la connexion est fermée et vous devez terminer ou fermer et rouvrir le tuyau.

  3. (Non portable, spécifique à Linux.) Avec poll() , et en supposant que les canaux nommés ne se terminent jamais et peuvent être connectés et déconnectés plusieurs fois :

    • open(pipe, O_RDWR | O_NONBLOCK);
    • Boucle principale :
      • poll() pour POLLIN événements, éventuellement sur plusieurs canaux à la fois.
      • read() autant de données que nécessaire, éventuellement en boucle sur read() appels.
        • Si read() == -1 et errno == EAGAIN , revenir à poll() étape.
        • Si read() == -1 et errno == EINTR , read() tout recommencer.
        • Si read() == 0 , quelque chose ne va pas -- cela ne devrait pas arriver avec O_RDWR sur les canaux nommés, mais uniquement avec O_RDONLY ou tuyaux sans nom ; il indique un tuyau fermé qui doit être fermé et rouvert. Si vous mélangez des canaux nommés et sans nom dans le même poll() boucle de gestion des événements, ce cas devra peut-être encore être traité.

Linux
  1. Erreurs lors du clonage de disque avec Cat ?

  2. Lecture de lignes à partir d'un fichier avec Bash :pour Vs. Tandis que?

  3. Simuler un périphérique de bloc défectueux avec des erreurs de lecture ?

  4. Linux :y a-t-il une lecture ou une réception à partir du socket avec un délai d'attente ?

  5. Exemple d'utilisation de canaux nommés dans Linux Bash

Une introduction aux canaux et aux canaux nommés sous Linux

Tutoriel de commande Linux mkfifo pour les débutants (avec exemples)

Comment utiliser les canaux et les canaux nommés sous Linux (avec exemples)

Lire des livres électroniques à partir de la ligne de commande avec Epy Ebook Reader

Conseils Vim - Lire et écrire des fichiers distants avec Vim sous Linux

Commande Linux/Unix pour joindre N lignes d'entrée avec des délimiteurs ?