Subprocess Informationen austauschen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

Hallo,

ich beschäftige mich gerade mit dem Subprocess Modul. Im prinzip habe ich zwei Python skripte. Im main Skript möchte ich einen Subprocess starten, mit welchem dauerhaft kommuniziert werden soll.
Also ich sende vom Mainprocess fortlaufend Zahlen an den Child Process. Dieser sendet die Daten dann zurück.
Wie mach ich das?

Mein Code sieht bisher so aus:

Code: Alles auswählen

import subprocess

while True:
    publish = subprocess.Popen(
        ["/home/pi/neu/bin/python3.7",  
        "/home/pi/sktripte/rumspielen3.py"])
und der Child Prozess:

Code: Alles auswählen

while (1):
    print("hello vom Subprozess")
Die Ausgabe "hello vom Subprozess" kommt im Mainprozess schonmal an. Doch wie bekomme ich jetzt Information vom Main Prozess in den Subprozess?
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bert321: Da kommt keine Ausgabe in einem anderen Prozess an, die beiden Prozesse tauschen keinerlei Daten aus.

Es werden im Elternprozess auch am laufenden Band Zombieprozesse erzeugt, weil die `Popen`-Objekte nicht sauber mit `wait()` behandelt werden.

Und es werden so schnell wie es geht neue Prozesse in einer Endlosschleife gestartet von denen dann jeder in einer Endlosschleife so schnell es geht Print-Ausgaben raus haut. Eine extrem unsinnige Ressourcenverschwendung.

Was willst Du denn *eigentlich* machen? Wenn beides in Python geschrieben ist, dann wäre das vielleicht eher ein Fall für das `concurrent.futures`-Modul oder `multiprocessing`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

so ist gut

Code: Alles auswählen

while True:
aber warum so?

Code: Alles auswählen

while (1):
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

__blackjack__ hat geschrieben: Mittwoch 7. April 2021, 17:55 @bert321: Da kommt keine Ausgabe in einem anderen Prozess an, die beiden Prozesse tauschen keinerlei Daten aus.

Es werden im Elternprozess auch am laufenden Band Zombieprozesse erzeugt, weil die `Popen`-Objekte nicht sauber mit `wait()` behandelt werden.
Da hast du natürlich recht. Das hatte ich übersehen.
__blackjack__ hat geschrieben: Mittwoch 7. April 2021, 17:55 @bert321: Da kommt keine Ausgabe in einem anderen Prozess an, die beiden Prozesse tauschen keinerlei Daten aus.
Was willst Du denn *eigentlich* machen? Wenn beides in Python geschrieben ist, dann wäre das vielleicht eher ein Fall für das `concurrent.futures`-Modul oder `multiprocessing`.
Ich habe ein bereits fertiges Skript was für die Kommunikation über einen Bus zuständig ist und quasi in einer Endlosschleife laufen muss. Des weiteren habe ich auch noch ein C++ Programm, was genauso in einer Endlosschleife laufen muss.

Ich habe gerade mal versucht das ganze mithilfe von diesem Tutorial zu realisieren (Allerdings mit einer C++ und Python Datei): https://www.geeksforgeeks.org/python-su ... languages/
Hier habe ich quasi zwei dateien erstellt. Ein Python Skript mit:

Code: Alles auswählen

import subprocess
import os


def executeCpp():
    # create a pipe to a child process
    data, temp = os.pipe()

    # write to STDIN as a byte object(convert string
    # to bytes with encoding utf8)
    os.write(temp, bytes("5 10\n", "utf-8"));
    os.close(temp)

    # store output of the program as a byte string in s
    s = subprocess.check_output("g++ HelloWorld.cpp -o out2;./out2", stdin=data,shell=True)

    # decode s to a normal string
    print(s.decode("utf-8"))


if __name__=="__main__":
    while True:
        executeCpp()
/[code]
und eine C++ Datei mit:
[code]
#include <iostream>
using namespace std;
int main()
{
    int a, b;
    cin >> a >> b;
    
     cout << "Hello World from C++.Values are:" << a << " " << b;
    
    return 0;
}
Funktioniert auch soweit. Nur sobald ich in der C++ Datei den Part mit cout in eine while schleife setze, also so:

Code: Alles auswählen

#include <iostream>
using namespace std;
int main()
{
    int a, b;
    cin >> a >> b;
    
     cout << "Hello World from C++.Values are:" << a << " " << b;
    
    return 0;
}
kommt in meinem Python Skript nichts mehr an.
Ist meine Vermutung richtig, dass ich mit subprocess.check_output nur den C++ Code einmal kurz starte, warte bis es durchgelaufen ist und dann die Ausgabe aus dem C++ Code bekomme? Also ist das Problem dass das so nicht funktionieren kann?

ThomasL hat geschrieben: Donnerstag 8. April 2021, 06:22 aber warum so?

Code: Alles auswählen

while (1):
Dachte das wäre äquivalent, bisher...
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du ein Programm mit Endloschleife hast, ist das doch etwas anderes, als Endlos ein Programm immer wieder zu starten.`

`check_output` wartet, bis das Programm beendet ist, also bei einem Programm mit einer Endlosschleife endlos.
Du brauchst also Popen und mußt in einer Schleife stdout lesen.

`while (1):` macht von außen schon das selbe wie `while True:`, zweiteres ist aber halt viel lesbarer. Da sind zum einen die unnötigen Klammern, wo man sich fragt, warum ist der Ausdruck geklammert. Dann muß der Interpreter jedesmal prüfen ob 1 zu True oder False evaluiert (naja, wenn es der Compiler nicht wegoptimieren würde).
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

so, jetzt komm ich endlich mal wieder dazu mich hier zu melden.
Sirius3 hat geschrieben: Donnerstag 8. April 2021, 16:13 `check_output` wartet, bis das Programm beendet ist, also bei einem Programm mit einer Endlosschleife endlos.
Du brauchst also Popen und mußt in einer Schleife stdout lesen.
Genau das hatte ich quasi schon vermutet.
Ich habe zu deinem Tipp das hier gefunden: https://stackoverflow.com/questions/367 ... thon-and-c
Fraglich ist für mich nur, was der erste Part seine Codes macht. Also das hier:
proc = subprocess.Popen("C:\Python27\PythonPipes.exe",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
Wird hiermit das C++ Programm gestartet?

Wenn ich das ganze dann in Stdin/Stdout printe ist doch quasi meine Konsole mit den Informationen die hier ausgetauscht werden zugemüllt und ich kann Ausgaben, die vom Code zwecks debugging erzeugt werden garnicht mehr richtig herausfiltern?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann printe es doch einfach nicht.
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

ja gut, aber das ist dann trotzdem keine Lösung für diese Problem:
bert321 hat geschrieben: Dienstag 13. April 2021, 06:13 Ich habe zu deinem Tipp das hier gefunden: https://stackoverflow.com/questions/367 ... thon-and-c
Fraglich ist für mich nur, was der erste Part seine Codes macht. Also das hier:
proc = subprocess.Popen("C:\Python27\PythonPipes.exe",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
Wird hiermit das C++ Programm gestartet?
Kann mir da evtl. jemand weiterhelfen?
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Verstehe ich nicht. Wenn du Ausgaben deines Programms bekommst, dann wird das doch gestartet worden sein. Woher sonst sollen die kommen?
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bert321: Ja, mit `Popen` wird das externe Programm gestartet. Und danach muss man dann dafür sorgen, dass die Standardein- und ausgabe von dem Prozess entsprechend ”bedient” werden. Wenn das beispielsweise zwei Zahlen als Zeichenketten erwartet, bevor es selbst etwas ausgibt, dann muss man natürlich erst diese beiden Werte an das Programm übermitteln, bevor man die Ausgabe auslesen kann. Wobei es auch von dem externen Programm abhängen kann ob es überhaupt ausreicht nur die Zeichenkette(n) zu schreiben, oder ob man die Standardeingabe des Prozesses eventuell auch schliessen muss, bevor das Programm die Daten verarbeitet.

Es lässt sich nicht so allgemein jedes Programm auf diese Weise ansprechen, weil das Betriebssystem oder die C- oder C++-Laufzeitbibliothek eventuell auch Daten blockweise puffern.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

Alles klar, dass mit dem Popen hat mir schon weitergeholfen.

Mein Code sieht jetzt so aus:

Code: Alles auswählen

import subprocess

proc = subprocess.Popen("/home/pi/test.sh",stdin=subprocess.PIPE,stdout=subprocess.PIPE)

state = "run"
while state == "run":
    input = input("first") #Nachricht, die an C++ Skript gesendet wird. Es sollte First Hello! zurückkommen

    if input == "quit":
        state = "terminate" # any string other than "run" will do

    proc.stdin.write(input + "\n")
    cppMessage = proc.stdout.readline().rstrip("\n")
    print ("cppreturn message ->" + cppMessage + " written by python \n")
    print ("test")
Meine test.sh sieht so aus:

Code: Alles auswählen

#!/bin/bash
g++ -o Test1 /home/pi/helloFromC.cpp
sudo ./Test1
Der C++ Code:

Code: Alles auswählen

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char* args[]){

    string python_message = "";
    bool quit = false;

    while (!quit){
        cin >> python_message;

        if (python_message == "quit"){
            quit = true;
        }
        else if (python_message == "first"){
            cout << "First Hello!" << endl;
        }
        else if (python_message == "second"){
            cout << "Second Hello!" << endl;
        }
        else if (python_message == "third"){
            cout << "Third Hello!" << endl;
        }
        else {
            cout << "Huh?" << endl;
        }
    }
    return 0;
}
Wenn ich nun das Pythonskript starte wird "first" in der Konsole ausgegeben. Dann scheint es jedoch zu hängen. Mein in der letzter Zeile geschriebener print "test" wird nicht ausgegeben. Warum?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Du solltest die eingebaute Funktion `input` nicht durch die Variable `input` überdecken. Würdest Du, wie es sich gehört, alles in einer Funktion stehen haben, würde Dir das auch gleich mit der richtigen Fehlermeldung beschieden.
Wenn Du eine while-Schleife hast, die nur durch einen Dummy-Wert überhaupt startet, hast Du eigentlich eine while-True-Schleife:

Code: Alles auswählen

import subprocess

def main():
    proc = subprocess.Popen("/home/pi/test.sh", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    while True:
        command = input("first") #Nachricht, die an C++ Skript gesendet wird. Es sollte First Hello! zurückkommen
        proc.stdin.write(command + "\n")
        proc.stdin.flush()
        cpp_message = proc.stdout.readline().rstrip("\n")
        print(f"cppreturn message -> {cpp_message} written by python")
        print("test")
        if command == "quit":
            break

if __name__ == '__main__':
    main()
Nach der Ausgabe solltest Du die Buffer flushen. Das hat Dir __blackjack__ schon geraten, und das solltest Du in Deinem C-Programm genauso tun.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Warum um alles in der Welt wird das übersetzte C++-Programm eigentlich mit ``sudo`` gestartet? 😱
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du Kontrolle über den C++ code hast, dann gibt es deutlich bessere Möglichkeiten zum Datenaustausch. Zb UNIX Domain Sockets, oder FIFOs, oder Bibliotheken wie ZeroMQ oder Nanomsg.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Was ist denn an UNIX Domains Sockets oder FIFOs besser? Ich meine das sind doch letztlich auch Dateien, aber nicht so portabel wie direkt über die Standardein- und Ausgabe-Dateien zu kommunizieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Meines Erachtens ist deren Semantik freundlicher. Flush zumindest musste ich noch nie nutzen. Oder vertue ich mich da gerade?

Spätestens bei zeromq und Nanomsg sind die Vorteile aber unzweifelhaft. Message orientiert, verschiedene Semantiken (pipes, req/response), automatisch verbindend etc.
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

Sirius3 hat geschrieben: Montag 19. April 2021, 16:34 Du solltest die eingebaute Funktion `input` nicht durch die Variable `input` überdecken. Würdest Du, wie es sich gehört, alles in einer Funktion stehen haben, würde Dir das auch gleich mit der richtigen Fehlermeldung beschieden.
Wenn Du eine while-Schleife hast, die nur durch einen Dummy-Wert überhaupt startet, hast Du eigentlich eine while-True-Schleife:
Nach der Ausgabe solltest Du die Buffer flushen. Das hat Dir __blackjack__ schon geraten, und das solltest Du in Deinem C-Programm genauso tun.
Danke für deinen Tipp. Leider funktioniert dein Code auch nicht. Es kommt wieder lediglich nur die Ausgabe "first" und sonst nichts. Oder liegt das daran, dass ich mich nocht nicht darum gekümmert habe die Buffer zu flushen?
__blackjack__ hat geschrieben: Montag 19. April 2021, 17:04 Warum um alles in der Welt wird das übersetzte C++-Programm eigentlich mit ``sudo`` gestartet? 😱
Hatte das so aus einem Tutorial
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

An Deinem C-Code habe ich ja nichts geändert.
bert321
User
Beiträge: 7
Registriert: Mittwoch 7. April 2021, 14:53

Wenn ich den C-Code aber eigenständig starte funktioniert er.
Woran liegt das eigentlich, dass so Beispiele aus dem Internet gefühlt nie funktionieren? :shock:
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Weil dein Programm dann in ein terminal ausgjbt. Und das liefert Daten zeilenweise aus. Eine Pipe hingegen ohne flush Buffert gerne mehrere KB.
Antworten