2016-04-17 10 views
0

Ich möchte ein Python Skript von meinem C++ Code aufrufen. Der Python-Skript wie folgt aussieht:C++ starte ein Skript (bash, python ...) und benutze stdin/stdout für datatransfare [linux]

hello.py 
    #!/usr/bin/python 
    import sys 
    print "Hello!" 
    Readin = sys.stdin.read() 
    print Readin 

Der C++ Code aus einer anderen Frage aus Stapelüberlauf ist. Wie es funktionieren sollte:

  • Erstellen eines Paares von Rohren.

  • Erstellen eines untergeordneten Prozesses mit fork().

  • kind verbiegt seine rohre zu stdin/stdout. Schließen Sie die anderen Enden und starten Sie das Skript.

  • Vater hört auf die Rohre read(), empfangenden Eingang. Und danach Senden einer Nachricht write().

Das Programm kehrt nicht von Väter Linie, wenn switch (readResult = read(childToPa... eingegeben wird.

Ich weiß auch nicht, ob dieses Schreibgerät seinen Job macht. Ist das eine vielversprechende Idee, oder gibt es andere Arbeitsmöglichkeiten? Danke!

Es sieht aus wie:

// maybe not all includes are necessary 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <errno.h> 
#include <sys/stat.h> // mkdir 
#include <stdlib.h>  // system() 
#include <unistd.h> // rmdir 
#include <cstring> // memset 

// wait: 
#include <sys/types.h> 
#include <sys/wait.h> 

using namespace std; 

int main() { 

    char target[] = "./hello.py"; 

    enum PIPE_FILE_DESCRIPTERS { 
    READ_FD = 0, WRITE_FD = 1 
    }; 

    enum CONSTANTS { 
    BUFFER_SIZE = 100 
    }; 

    int parentToChild[2]; 
    int childToParent[2]; 
    pid_t pid; 
    string dataReadFromChild; 
    char buffer[BUFFER_SIZE + 1]; 
    memset(buffer,0x00,BUFFER_SIZE + 1); 
    ssize_t readResult; 
    int status; 

    int retPipe1 = pipe(parentToChild); 
    int retPipe2 = pipe(childToParent); 

    switch (pid = fork()) { 
    case -1: 
    printf("Fork failed"); 
    exit(-1); 

    case 0: /* Child will start scripts*/ 
    { 
    // Bending stdin/out to the pipes? 
    int retdup21 = dup2(parentToChild[READ_FD], STDIN_FILENO); 
    int retdup22 = dup2(childToParent[WRITE_FD], STDOUT_FILENO); 
    int retdup23 = dup2(childToParent[WRITE_FD], STDERR_FILENO); 
    // Close in this Process the other sides of the pipe 
    int retclose1 = close(parentToChild[WRITE_FD]); 
    int retclose2 = close(childToParent[READ_FD]); 

    int retexe = execlp(target ," ");    // warning not enough variable arguments to fit a sentinel [-Wformat=] 

    printf("This line should never be reached!!!"); // why? what happens if execlp finishes? 
    exit(-1); 
    break; // to make the compiler happy =) 
    } 
    default: /* Parent */ 
    cout << "Child " << pid << " process running..." << endl; 

    // close the other ends of the pipe from the other process. 
    int retdup21 = close(parentToChild[READ_FD]); 
    int retdup22 = close(childToParent[WRITE_FD]); 

    // readtry 
    while (true) { 
     switch (readResult = read(childToParent[READ_FD], buffer, 1)) // execution does not return from this function. 
     { 
     case 0: /* End-of-File, or non-blocking read. */ 
     { 
     cout << "End of file reached..." << endl << "Data received was (" << dataReadFromChild.size() << "):" << endl 
      << dataReadFromChild << endl; 

     cout << "starting writing" << endl; 
     char bufferW[] = "{\"AElement\":\"Something\"}\0"; 


       int writeResult = write(parentToChild[WRITE_FD],bufferW,sizeof(bufferW)); 
       int saveerrno = errno; 

       if(-1 == writeResult) 
       { 
       cout << "errno while writing: " << errno << std::endl; 
       if (9 == saveerrno) 
        cout << "Errno Bad File descriptor" << endl; 
       } 

       cout << "Write Result: " << writeResult << std::endl; 

     int retWait = waitpid(pid, &status, 0); 

     cout << endl << "Child exit staus is: " << WEXITSTATUS(status) << endl << endl; 

     exit(0); 
     } 
     case -1: 
     { 
     if ((errno == EINTR) || (errno == EAGAIN)) { 
      errno = 0; 
      break; 
     } else { 
      printf("read() failed"); 
      exit(-1); 
     } 
     } 
     default: 
     dataReadFromChild.append(buffer, readResult); 
     printf("%s",buffer); 
     memset(buffer,0x00,BUFFER_SIZE + 1); 
     break; 
     } 
    } /* while (true) */ 
    } /* switch (pid = fork())*/ 
} 
+0

Erstens: Wie ist es relevant, dass Sie in dem anderen Programm Python-Code ausführen? Antwort: Es ist nicht. So könnten Sie Ihren Code und Ihre Frage leicht reduzieren, indem Sie sich einem minimalen Beispiel nähern. Aber selbst dann frage ich mich, woher du die Idee für den Ansatz hast? Es ist nicht neu, nicht einzigartig und tatsächlich gut etabliert. Es sollte also trivial sein, existierende, funktionierende Beispiele zu finden! –

+0

@Ulrich Eckhardt Die Baugruppe hat einen C++ - Kern. Dies erhält eine Eingabe für einen Job. Der Kern bereitet diesen Job vor, aktualisiert die Datenbank usw. Danach wird der Job von python ausgeführt. Der Kern bleibt für die Zukunft. Aber die Art von Aufgaben wird sich in Zukunft ändern. Also haben wir nach einem einfachen Weg gesucht. Geben Sie den Scriptfile-Namen mit API ein -> C++ preparing -> Python doing. Gibt es einen besseren Weg, dies zu erreichen? Ich bin ein Programmierer, und ich bevorzuge diesen Hack nicht. Da diese Skripte möglich sein sollten, von einander aufgerufen zu werden, wurde der stdin/put-Weg vorgeschlagen. Bessere Ideen? Thx =) –

Antwort

1

Ihre Probleme Ausgang, nicht geschlossene Filedeskriptoren gepuffert werden und EOF mit dem Ende eines Teil des Getriebes zu signalisieren. Die ersten beiden Probleme können gelöst werden, aber die letzte benötigt einen anderen Ansatz. Mehr dazu später.

Schritt für Schritt:

Python gepuffert über I/O, so können Sie Python wollen zwingen, indem Sie eine Zeile sys.stdout.flush() nach der ersten print-Anweisung der Ausgabe zu spülen. Jetzt wird "Hello!\n" Zeichen für Zeichen gelesen.

Aber dann blockiert die nächste read bis ein neues Zeichen eintrifft oder die Leitung geschlossen ist. STDOUT des Python-Skripts ist noch offen, das C++ - Programm wartet auf etwas, aber auch das Python-Skript wartet auf Eingaben. Klassischer Deadlock.

Sie könnten den letzten Ausdruck im Python-Skript verwerfen und versuchen, seinen STDOUT zu schließen. Da read blockiert, bis alle Dateideskriptoren, die auf das Schreibende der Pipe verweisen, geschlossen sind, müssen Sie os.close(sys.stdout.fileno()) und os.close(sys.stdout.fileno()) nach der flush hinzufügen.

Aber es gibt immer noch gültige Dateideskriptoren, die auf den Schreibteil dieser Pipe verweisen. Erinnern Sie sich an die dup2 in der C++ - Quelle? Nach diesen drei dup2 Zeilen gibt es immer noch parentToChild[READ_FD] und Verweis auf die Skripts STDIN und STDOUT. Also müssen wir sie schließen. Add close(parentToChild[READ_FD]); und close(childToParent[WRITE_FD]); direkt nach der dup2 s. Jetzt gibt read0 zurück, wenn Python-Skript STDOUT und STDERR schließt.

Als nächstes sendet das übergeordnete Element "{\"AElement\":\"Something\"}\0" und erreicht waitpid, die zurückgegeben wird, wenn das untergeordnete Element beendet wird. Aber das Kind liest immer noch von STDIN. Sie müssen also close(parentToChild[WRITE_FD]); vor waitpid hinzufügen.


Jetzt für den konzeptionellen Teil: Sie kann nicht read(), bis es wieder ein 0 (Rohr geschlossen), und dann weiter von dem geschlossenen Rohr zu lesen. Ihre Auswahlmöglichkeiten:

  • Einmal gelesen, bis das Rohr geschlossen ist. Keine zweite Nachricht möglich.
  • Wissen, wie viel zu lesen. Entweder im Voraus oder durch Interpretieren der empfangenen Bytes.
  • Überwachen Sie beide Rohre z.B. mit poll(2) und entscheiden dynamisch, wenn Sie lesen oder schreiben möchten.

BTW: Die Argumente von execlp sind const char *file, const char *arg, ..., wo arg, ... sind die üblichen char *args[] mit arg[0] beginnt und mit einem Null-Zeiger-Endung (!). Bitte diese Zeile int retexe = execlp(target, target, (char*) NULL); ändern


#!/usr/bin/python2.7 

import os 
import sys 

print "Hello!" 
sys.stdout.flush() 
os.close(sys.stdout.fileno()) 
os.close(sys.stderr.fileno()) 

data = sys.stdin.read() 
with open("data_received_by_child.txt", "w") as fp: 
    print >>fp, data 

#include <cerrno> 
#include <cstdio> 
#include <cstdlib> 
#include <iostream> 

#include <sys/wait.h> 
#include <unistd.h> 

using namespace std; 

int main() 
{ 
    const char *target = "./hello.py"; 

    enum PIPE_FILE_DESCRIPTERS { 
     READ_FD = 0, WRITE_FD = 1 
    }; 

    /* Make pipes */ 
    int parentToChild[2]; /* Parent to child pipe */ 
    if (pipe(parentToChild) < 0) 
    { 
     perror("Can't make pipe"); 
     exit(1); 
    } 
    int childToParent[2]; /* Child to parent pipe */ 
    if (pipe(childToParent) < 0) 
    { 
     perror("Can't make pipe"); 
     exit(1); 
    } 

    /* Create a child to run command. */ 
    pid_t pid = fork(); 
    switch (pid) 
    { 
     case -1: 
      perror("Can't fork"); 
      exit(1); 

     case 0: /* Child */ 
      close(parentToChild[WRITE_FD]); 
      close(childToParent[READ_FD]); 
      dup2(parentToChild[READ_FD], STDIN_FILENO); 
      dup2(childToParent[WRITE_FD], STDOUT_FILENO); 
      close(parentToChild[READ_FD]); 
      close(childToParent[WRITE_FD]); 
      execlp(target, target, (char *) NULL); 
      perror("Can't execute target"); 
      exit(1); 

     default: /* Parent */ 
      close(parentToChild[READ_FD]); 
      close(childToParent[WRITE_FD]); 
      cout << "Child " << pid << " process running..." << endl; 
    } 

    /* Read data from child */ 
    string dataReadFromChild; 
    char ch; 
    int rc; 
    while ((rc = read(childToParent[READ_FD], &ch, 1)) != 0) 
    { 
     if (rc == -1) { 
      if ((errno == EINTR) || (errno == EAGAIN)) { 
       continue; 
      } 
      perror("read() failed"); 
      exit(-1); 
     } 
     dataReadFromChild += ch; 
    } 
    close(childToParent[READ_FD]); 
    cout << "End of file reached..." << endl; 
    cout << "Data received was (" << dataReadFromChild.size() << "):" << endl; 
    cout << dataReadFromChild << endl; 

    /* Write data to child */ 
    cout << "starting writing" << endl; 
    const char bufferW[] = "{\"AElement\":\"Something\"}\0"; 
    while (true) { 
     int rc = write(parentToChild[WRITE_FD], bufferW, sizeof(bufferW)); 
     if (rc == -1) { 
      if ((errno == EINTR) || (errno == EAGAIN)) { 
       continue; 
      } 
      perror("write() failed"); 
      exit(-1); 
     } 
     break; 
    } 
    close(parentToChild[WRITE_FD]); 

    /* Wait for child to exit */ 
    int status; 
    int retWait = waitpid(pid, &status, 0); 
    cout << endl << "Child exit status is: " << WEXITSTATUS(status) << endl << endl; 
} 
+0

Vielen Dank für die vielen Informationen und Hinweise auf jeden Fehler, den ich gemacht habe. Ich werde in ein paar Tagen bei denen arbeiten. Um mehr Informationen zu diesem Thema zu bekommen. Programmkommunikation, Pipes, Start von externen Programmen und Zombies. Können Sie mir den Namen eines guten Buches oder einiger Stichwörter geben, wo ich anfangen soll? –

+0

Das beste Buch über POSIX-Level und Linux-Programmierung, auf das ich gestoßen bin, ist "The Linux Programming Interface" von Michael Kerrisk. Ich mochte auch den "POSIX Programmer's Guide" von Donald Lewine (erschienen 1991, jetzt kostenlos erhältlich). Aber meine wichtigste Referenz sind die man-Seiten. – Yurim

+0

** Danke! ** Für die erste Iteration in das Thema möchte ich Bücher haben. Dann könnt ihr die Man-Seiten für weitere Fragen genießen. –