web-dev-qa-db-fra.com

Python lire nommé PIPE

J'ai un canal nommé sous Linux et je veux le lire à partir de python. Le problème est que le processus python "consomme" un cœur (100%) en continu. Mon code est le suivant:

FIFO = '/var/run/mypipe'
os.mkfifo(FIFO)
with open(FIFO) as fifo:
    while True:
        line = fifo.read()

Je veux demander si le "sommeil" aidera la situation ou le processus à perdre certaines données d'entrée du tuyau. Je ne peux pas contrôler l'entrée, donc je ne connais pas la fréquence d'entrée des données. J'ai lu sur select et poll mais je n'ai pas trouvé d'exemple pour mon problème. Enfin, je veux demander si l'utilisation à 100% aura un impact sur l'entrée de données (perte ou quelque chose?).

edit: je ne veux pas rompre la boucle. Je veux que le processus s'exécute en continu et "entende" les données du tuyau.

8
user1005633

De manière UNIX typique, read(2) renvoie 0 octet pour indiquer la fin du fichier, ce qui peut signifier:

  • Il n'y a plus d'octets dans un fichier
  • L'autre extrémité d'un socket a coupé la connexion
  • L'écrivain a fermé un tuyau

Dans votre cas, fifo.read() renvoie une chaîne vide, car le rédacteur a fermé son descripteur de fichier.

Vous devez détecter ce cas et sortir de votre boucle:

reader.py :

import os
import errno

FIFO = 'mypipe'

try:
    os.mkfifo(FIFO)
except OSError as oe: 
    if oe.errno != errno.EEXIST:
        raise

print("Opening FIFO...")
with open(FIFO) as fifo:
    print("FIFO opened")
    while True:
        data = fifo.read()
        if len(data) == 0:
            print("Writer closed")
            break
        print('Read: "{0}"'.format(data))

Exemple de session

Terminal 1:

$ python reader.py 
Opening FIFO...
<blocks>

Terminal 2:

$ echo -n 'hello' > mypipe 

Terminal 1:

FIFO opened
Read: "hello"
Writer closed
$ 

Mise à jour 1 - Réouverture continue

Vous indiquez que vous souhaitez continuer à écouter les écritures sur la pipe, probablement même après la fermeture d'un écrivain.

Pour le faire efficacement, vous pouvez (et devriez) profiter du fait que

Normalement, l'ouverture des blocs FIFO jusqu'à ce que l'autre extrémité soit également ouverte.

Ici, j'ajoute une autre boucle autour de open et de la boucle read. De cette façon, une fois le canal fermé, le code tentera de le rouvrir, ce qui bloquera jusqu'à ce qu'un autre écrivain ouvre le canal:

import os
import errno

FIFO = 'mypipe'

try:
    os.mkfifo(FIFO)
except OSError as oe:
    if oe.errno != errno.EEXIST:
        raise

while True:
    print("Opening FIFO...")
    with open(FIFO) as fifo:
        print("FIFO opened")
        while True:
            data = fifo.read()
            if len(data) == 0:
                print("Writer closed")
                break
            print('Read: "{0}"'.format(data))

Terminal 1:

$ python reader.py 
Opening FIFO...
<blocks>

Terminal 2:

$ echo -n 'hello' > mypipe 

Terminal 1:

FIFO opened
Read: "hello"
Writer closed
Opening FIFO...
<blocks>

Terminal 2:

$ echo -n 'hello' > mypipe 

Terminal 1:

FIFO opened
Read: "hello"
Writer closed
Opening FIFO...
<blocks>

... etc.


Vous pouvez en savoir plus en lisant la page man pour les tuyaux:

21
Jonathon Reinhart

(Des années plus tard) Si je comprends le cas d'utilisation de l'OP en utilisant for ... in ... fait exactement ce que l'on souhaite:

import os

FIFO = 'myfifo'
os.mkfifo(FIFO)
with open(FIFO) as fifo:
    for line in fifo:
        print(line)

Ce programme attend patiemment l'entrée du fifo jusqu'à ce qu'il soit fourni, puis l'imprime à l'écran. Aucun CPU n'est utilisé entre-temps.

C'est aussi la manière la plus idiomatique de Python donc je le recommanderais plutôt que d'utiliser read () directement.

Si le côté client écrit sur le fifo se ferme, la boucle for se termine et le programme se ferme. Si vous vouliez qu'il rouvre le fifo pour attendre que le prochain client l'ouvre, vous pouvez mettre la section for dans une boucle while:

import os

FIFO = 'myfifo'
os.mkfifo(FIFO)
while True:
    with open(FIFO) as fifo:
        for line in fifo:
            print(line)

Cela rouvrira le fifo et attendra comme d'habitude.

0
Tristan