1Danke
ERLEDIGT
JA
JA
ANTWORTEN
6
6
ZUGRIFFE
7652
7652
EMPFEHLEN
-
Hallo zusammen,
ich verzweifle gerade an einer Sache, von der ich dachte, dass sie eigentlich zu den Grundfunktionen der stdio gehören:
Ich versuche, aus meinem C/C++ Programm heraus ein weiteres Programm aufzurufen, und sowohl stdin als auch stdout auf pipes umzulenken, damit ich Zugriff sowohl auf Ein- als auch auf die Ausgabe habe.
Unter Windows gibt es hierfür eine WinAPI Funktion, "CreateProcess", der ich meine Kanäle übergeben kann, das Problem ist nur... ich brauch das ganze für Linux (SuSE 10.1). Weiß jemand Rat?
Gruß
Achim
-
11.04.07 10:51 #2
- Registriert seit
- Jun 2005
- Beiträge
- 8.168
Hi.Laut POSIX Standard muß eine Pipe lediglich unidirektional sein - nicht bidirektional. Unter Linux ist das auch der Fall, so das verhindert wird das es zu einem Deadlock kommt.
Du kannst das ganze mit fork, exec* und mehreren Pipes unter Linux realisieren:Also so ungefähr sollte das funktionieren. Vor allem Fehlerbehandlung hab ich komplett weggelassen...Code c:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include <sys/types.h> #include <unistd.h> #include <stdio.h> pid_t pid; int p2c[2], c2p[2]; pipe(p2c); pipe(c2p); pid = fork(); if (pid == 0) { /* child */ dup2(p2c[0], stdin); dup2(p2c[1], stdout); execvp(...); /* anderes Programm starten */ } else { FILE *tochild, *fromchild; close(p2c[0]); tochild = fdopen(p2c[1], "w"); close(c2p[1]); fromchild = fdopen(c2p[0], "r"); /* tochild / fromchild benutzen */ }
GrußIf at first you don't succeed, try again. Then quit. No use being a damn fool about it.
-
03.09.07 11:54 #3
- Registriert seit
- Oct 2003
- Ort
- Hamburg
- Beiträge
- 125
Danach suche ich gerade. Die grds. Idee ist mir klargeworden, danke für die kurze Erläuterung. Hat jemand dafür vielleicht ein komplettes Beispiel?
Gruß
René
-
Ich habe die Lösung inzwischen auch zu 90% (würd ich sagen) verstanden, ich kann mal versuchen, das in Worten wiederzugeben. Danach hänge ich mal ein Beispiel an, wie ich das gelöst habe.
Wenn ich das richtig verstanden habe, und mein Testcode hat zumindest funktioniert, dann funktioniert ein CGI- Call wie folgt:
Zuerst mal erzeuge ich 2 Pipes. Jede Pipe hat logischerweise 2 Handles, nämlich eins am Lese- und eins am Schreibende.
Die eine Pipe ist quasi die Richtung "Parent -> Child", die andere ist die Richtung "Child -> Parent".
Dann wird geforkt.
Im Child wird zunächst STDIN geschlossen (). Grund dafür ist der, dass der (spätere) CGI- Prozess ja keine Pipe öffnen soll, sondern diese Pipes auf STDIN und STDOUT gemappt werden sollen, damit der Child direkt darüber mit dem Parent kommunizieren können.Code :1
close(0)
Nachdem STDIN geschlossen wurde, kann jetzt das Lese- Handle unserer "Parent->Child" Pipe dupliziert werden:.Code :1
dup(p0[0])
p0 ist hierbei unsere Parent -> Child Pipe, p1 die umgekehrte Richtung.
Damit wird das Handle auf den gerade geschlossenen Kanal (nämlich STDIN) dupliziert.
Als nächstes machen wir dasselbe mit STDOUT:.Code :1
close(1); dup(p1[1])
In diesem Fall wurde das Schreibende der anderen Richtung (p1) auf den wiederum zuvor geschlossenen Kanal (STDOUT) gemappt.
Jetzt kann der Childprozess seine nicht benötigten Handles der Pipes schließen, nämlich die Enden, die der Parent braucht.
------------------
Im Parent machen wir dasselbe, nur dass wir in dem Fall nicht noch STDIN und STDOUT umbiegen müssen. Wir öffnen stattdessen einfach die Handles und schließen die überflüssigen, das wars auch schon.
------------------
Die letzte Anweisung im Child- Zweig lautet dann execlp(Programmname, Argumente, NULL). Damit wird das Programm beendet und an dessen Stelle tritt dann das aufgerufene Kommando. Der somit neu erzeugte Prozess kennt weder p0 noch p1, aber STDIN und STDOUT werden automatisch vererbt. Beim Beenden des Prozesses werden diese Handles standardmäßig auch wieder von der Shell geschlossen, so dass da keine offenen Handles zurückbleiben.
-------------------
Beispiel:
Meine CGIComm.h und .cpp:
CGIComm.h
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#ifndef CGICOMM_H #define CGICOMM_H #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> typedef struct CGI { FILE *in; FILE *out; int pid; }; CGI *OpenCGI(char *Command, char *Args); //CGI- Prozess erzeugen char* ReadCGI(CGI* cgi); //Vom CGI- Prozess lesen void WriteCGI(CGI* cgi, char* Data); //An CGI- Prozess senden void CloseCGI(CGI *cgi); //CGI- Handles parentseitig schließen int CGICommDirect(char* Command, char* Data, char* Answer, int AnsLen); //Kurzbefehl für alles zusammen #endif
--------------------------------
CGIComm.cpp
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#include "CGIComm.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h> CGI *OpenCGI(char *Command, char *Args) { FILE *f = fopen(Command, "r"); if (f == NULL) return NULL; fclose(f); CGI *cgi = (CGI*)malloc(sizeof(CGI)); int p0[2], p1[2]; pipe(p0); pipe(p1); int cid = fork(); if (!cid) { close(0); dup(p0[0]); close(1); dup(p1[1]); close(p0[1]); close(p1[0]); execlp(Command, Args, NULL); } else { cgi->in = fdopen(p0[1], "w"); close(p0[0]); cgi->out = fdopen(p1[0], "r"); close(p1[1]); cgi->pid = cid; } return cgi; } char* ReadCGI(CGI* cgi) { char* Data = NULL; if (cgi == NULL) return NULL; int Len = 0; int DataLen = 0; char Buffer[128]; while ((Len = fread(Buffer, 1, 128, cgi->out))) { char* NData = (char*)malloc(Len + DataLen); memcpy(NData, Data, DataLen); memcpy(&NData[DataLen], Buffer, Len); if (Data != NULL) free(Data); Data = NData; DataLen += Len; } if (DataLen == 0) return NULL; char *NData = (char*)malloc(DataLen+1); memcpy(NData, Data, DataLen); NData[DataLen] = 0; if (Data != NULL) free(Data); Data = NData; return Data; } void WriteCGI(CGI* cgi, char* Data) { if (cgi == NULL) return; int Len = strlen(Data)+1; fputs(Data, cgi->in); fclose(cgi->in); cgi->in = NULL; } void CloseCGI(CGI *cgi) { if (cgi == NULL) return; int result; waitpid(cgi->pid, &result, 0); if (cgi->in != NULL) fclose(cgi->in); fclose(cgi->out); free(cgi); } int CGICommDirect(char* Command, char* Data, char* Answer, int AnsLen) { int pfds0[2], pfds1[2]; FILE *fp_in, *fp_out; pipe(pfds0); pipe(pfds1); int cid = fork(); if (!cid) { close(0); dup(pfds1[0]); close(1); dup(pfds0[1]); close(pfds0[0]); close(pfds1[1]); execlp(Command, NULL, NULL); } else { fp_in = fdopen(pfds0[0], "r"); close(pfds0[1]); fp_out = fdopen(pfds1[1], "w"); close(pfds1[0]); fputs(Data, fp_out); fclose(fp_out); fgets(Answer, AnsLen, fp_in); fclose(fp_in); wait(NULL); } return 0; }
-------------------------------
Bei Fragen fragen
Der Code steht übrigens zur freien Verwendung zur Verfügung, ich übernehme allerdings keine Garantie auf Funktionsfähigkeit oder Zweckdienlichkeit (halt die Haftungsauschlussklausel und so...)
-
Hallo,
tut mir leid diesen alten Thread wieder ausgraben zu müssen, aber er beinhaltet genau das, wonach ich suche.
Ich habe allerdings noch eine Frage zum Code von deepthroat. Kann es sein, dass in deinem Code ein kleiner Fehler ist?
Müsste dieser Code
nicht eher soCode :1 2 3
if (pid == 0) { /* child */ dup2(p2c[0], stdin); dup2(p2c[1], stdout);
sein?Code :1 2 3
if (pid == 0) { /* child */ dup2(p2c[0], stdin); dup2(c2p[1], stdout);
Für was es den Integer (p2c bzw. c2p) jeweils 2 mal gibt, habe ich noch nicht verstanden. Kann das eventuell noch jemand erklären?
Gruß
Ryo
-
02.11.11 12:56 #6
- Registriert seit
- Jun 2005
- Beiträge
- 8.168
If at first you don't succeed, try again. Then quit. No use being a damn fool about it.
-
Habe die Funktionsweise von pipe() nun verstanden und weiß, warum du ihm ein Array übergibst.
Nochmal vielen Dank für deinen Code, der hat mir sehr weitergeholfen!
€: Hast du eventuell noch eine Idee, wie ich im Parent-Prozess feststellen kann, wann der Child-Prozess fertig ist? Denn ich möchte ja solange der Child-Prozess lebt im Parent-Prozess die Ausgaben mitlesen und wenn der Child-Prozess beendet ist, soll das Programm beendet werden.
€2: Folgendes Abbruchkriterium scheint wohl zu funktionieren:
Code :1
while (readResult > 0);
Geändert von RyoShinzo (02.11.11 um 16:30 Uhr)
Ähnliche Themen
-
stdout in Logfile umleiten
Von DestroyerJoe im Forum JavaAntworten: 2Letzter Beitrag: 21.04.09, 11:31 -
Ausgaben eines Prozesses in Datei umleiten
Von Eroli im Forum .NET CaféAntworten: 8Letzter Beitrag: 31.07.08, 15:50 -
Stdout eines Prozesses ermitteln
Von Hroudtwolf im Forum C/C++Antworten: 2Letzter Beitrag: 17.11.07, 19:43 -
[C] Linux: stdout in pipe umleiten
Von plonk im Forum C/C++Antworten: 3Letzter Beitrag: 25.04.04, 16:06 -
stdin eines client-prozess mit pipe des parent verbinden
Von smartlok im Forum Linux & UnixAntworten: 1Letzter Beitrag: 16.06.03, 16:31





Zitieren

Login






