Pipes in Python
Pipe
Die Pipe
(engl. für Rohr, Röhre)
bezeichnet einen gepufferten
uni- oder bidirektionalen
Datenstrom zwischen zwei
Prozessen nach dem
"FIFO" (First In - First Out)-
Prinzip.
Das bedeutet, dass die
Ausgabe eines Prozesses als Eingabe für einen weiteren verwendet wird.
Pipes wurden 1973 in Unix eingeführt.
Generell unterscheidet man zwei Sorten von Pipes:
- anonyme (anonymous pipes)
und - benamte (named pipes)
Bier-Pipe in Python
"99 Bottles of Beer" ist ein bekanntes und beliebtes Lied in den USA und Kanada.
Es wird häufig bei gemeinsam unternommenen Ausflügen und Feiern gesungen.
Obwohl der Song aus 100 verschiedenen Strophen besteht, stellt er keine besonderen
Anforderungen an die Gedächtnisleistung der Sängerinnen und Sänger:
Ninety-nine bottles of beer on the wall, Ninety-nine bottles of beer. Take one down, pass it around, Ninety-eight bottles of beer on the wall.
Dann geht es mit 98 Flaschen entsprechend weiter bis alle Flaschen "aufgebraucht" sind. Was tun, wenn kein Bier mehr da ist? Klar, neues kaufen gehen:
No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, Ninety-nine bottles of beer on the wall.
Dieses Lied hat auch in der Programmierung eine besondere Bedeutung erlangt. Dieser Song wurde mittlerweile in allen bekannten Programmiersprachen implementiert.
Wir programmieren die Aleph-Null-Variante, d.h. mit der "no more bottles"-Strophe, mit Forking und Pipe:
import os
def child(pipeout):
bottles = 99
while True:
bob = "bottles of beer"
otw = "on the wall"
take1 = "Take one down and pass it around"
store = "Go to the store and buy some more"
if bottles > 0:
values = (bottles, bob, otw, bottles, bob, take1, bottles - 1,bob,otw)
verse = "%2d %s %s,\n%2d %s.\n%s,\n%2d %s %s." % values
os.write(pipeout, verse)
bottles -= 1
else:
bottles = 99
values = (bob, otw, bob, store, bottles, bob,otw)
verse = "No more %s %s,\nno more %s.\n%s,\n%2d %s %s." % values
os.write(pipeout, verse)
def parent():
pipein, pipeout = os.pipe()
if os.fork() == 0:
child(pipeout)
else:
counter = 1
while True:
if counter % 100:
verse = os.read(pipein, 117)
else:
verse = os.read(pipein, 128)
print 'verse %d\n%s\n' % (counter, verse)
counter += 1
parent()
Das Problem im obigen Beispiel besteht darin, dass wir genau wissen müssen, wieviele
Bytes wir jeweils vom Child-Prozess über die Pipe erwarten. Für die ersten 99 Strophen sind
es jeweils 117 Bytes (verse = os.read(pipein, 117)) und für die Strophe ohne
Flaschen 128 Bytes (verse = os.read(pipein, 128)).
In der folgenden Implementierung lesen wir jeweils ganze Zeilen aus der Pipe des Kind-Prozesses:
import os
def child(pipeout):
bottles = 99
while True:
bob = "bottles of beer"
otw = "on the wall"
take1 = "Take one down and pass it around"
store = "Go to the store and buy some more"
if bottles > 0:
values = (bottles, bob, otw, bottles, bob, take1, bottles - 1,bob,otw)
verse = "%2d %s %s,\n%2d %s.\n%s,\n%2d %s %s.\n" % values
os.write(pipeout, verse)
bottles -= 1
else:
bottles = 99
values = (bob, otw, bob, store, bottles, bob,otw)
verse = "No more %s %s,\nno more %s.\n%s,\n%2d %s %s.\n" % values
os.write(pipeout, verse)
def parent():
pipein, pipeout = os.pipe()
if os.fork() == 0:
os.close(pipein)
child(pipeout)
else:
os.close(pipeout)
counter = 1
pipein = os.fdopen(pipein)
while True:
print 'verse %d' % (counter)
for i in range(4):
verse = pipein.readline()[:-1]
print '%s' % (verse)
counter += 1
print
parent()
Bidirektionale Pipes
Wir wollen die Implementierung von bidriektionalen Pipes anhand eines Spieles demonstrieren. Dabei handelt es sich um ein einfaches Zahlenratespiels, wie es von kleinen Kindern gerne gespielt wird. Dieses Spiel hatten wir bereits im Kapitel über Schleifen in unserem Python-Kurs verwendet.
Das folgende Diagram veranschaulicht sowohl die Regeln des Spiels, als auch die Implementierung in unserem Python-Skript. Ein Spieler, in unserem Fall der Eltern-Prozess mit der Funktion "deviser", denkt sich eine Zahl im Bereich 0 bis 100. Der andere Spieler, bei uns der Kindprozess mit der Funktion "guesser", versucht die Zahl zu erraten. Der Elternprozess gibt eine 0 zurück, wenn die Zahl erraten wurde, eine 1, falls die geratene Zahl größer als die zu erratende Zahl ist und ein -1, falls sie kleiner als die zu erratende Zahl ist. Sowohl der Elternprozess als auch der Kindprozess protokollieren die Zwischenergebnisse, d.h. die Zahlen, die geraten werden. deviser schreibt in deviser.log und guesser schreibt in guesser.log
Im folgenden haben wir eine Implementierung des gesamten Skriptes angegeben:
import os, sys, random
def deviser(max):
fh = open("devisor.log","w")
to_be_guessed = int(max * random.random()) + 1
guess = 0
while guess != to_be_guessed:
guess = int(raw_input())
fh.write(str(guess) + " ")
if guess > 0:
if guess > to_be_guessed:
print 1
elif guess < to_be_guessed:
print -1
else:
print 0
sys.stdout.flush()
else:
break
fh.close()
def guesser(max):
fh = open("guesser.log","w")
bottom = 0
top = max
fuzzy = 10
res = 1
while res != 0:
guess = (bottom + top) / 2
print guess
sys.stdout.flush()
fh.write(str(guess) + " ")
res = int(raw_input())
if res == -1: # number is higher
bottom = guess
elif res == 1:
top = guess
elif res == 0:
message = "Wanted number is %d" % guess
fh.write(message)
else: # this case shouldn't occur
print "input not correct"
fh.write("Something's wrong")
n = 100
stdin = sys.stdin.fileno() # usually 0
stdout = sys.stdout.fileno() # usually 1
parentStdin, childStdout = os.pipe()
childStdin, parentStdout = os.pipe()
pid = os.fork()
if pid:
# parent process
os.close(childStdout)
os.close(childStdin)
os.dup2(parentStdin, stdin)
os.dup2(parentStdout, stdout)
deviser(n)
else:
# child process
os.close(parentStdin)
os.close(parentStdout)
os.dup2(childStdin, stdin)
os.dup2(childStdout, stdout)
guesser(n)
Benamte Pipes, Fifos
Unter Linux ebenso wie unter Unix ist es möglich Pipes anzulegen,
die als Dateien eingerichtet sind.
Diese Pipes werden als benamte Pipes (englisch: named Pipes) oder manchmal auch als Fifos (First In First Out) bezeichnet.
Ein Prozess liest und schreibt von einer solchen Pipe wie bei einer normalen Datei. Oft schreiben mehrere Prozesse in eine Pipe und nur ein Prozess liest daraus.
Das folgende Beispiel illustriert den Fall, dass ein Prozess in die Pipe schreibt, der Kindprozess, und ein anderer Prozess, der Elternprozess, aus der Pipe liest.
import os, time, sys
pipe_name = 'pipe_test'
def child( ):
pipeout = os.open(pipe_name, os.O_WRONLY)
counter = 0
while True:
time.sleep(1)
os.write(pipeout, 'Number %03d\n' % counter)
counter = (counter+1) % 5
def parent( ):
pipein = open(pipe_name, 'r')
while True:
line = pipein.readline()[:-1]
print 'Parent %d got "%s" at %s' % (os.getpid(), line, time.time( ))
if not os.path.exists(pipe_name):
os.mkfifo(pipe_name)
pid = os.fork()
if pid != 0:
parent()
else:
child()

Buch kaufen
Buch kaufen
Buch kaufen

