frage zu pipe(2)
-
Ich möchte folgendes realisieren.
Ich habe einen Vater Prozess den ich mit fork(2) kopiere und mir einen Sohn Prozess erzeuge. Der Vater wertet dann Datensätze aus und sendet die Ergebnisse über eine pipe an den Sohn.(Auswertung wird noch erweitert) Der Sohn sendet die Daten via TCP an einen ClientPC.
Wenn der ClientPC die TCP Verbindung beendet, wird der Sohn beendet.
Der vater will aber immer noch in die pipe schreiben und bleibt dann bei
write(pipefd,data,sizeof(data));
hängen, weil der Sohn nicht mehr liest. ( write() ist ausgelagert nach pipe_write() )
wie kann ich das verhindern?
vater:
void process1(void) { pid_t pid; int test=0; if(pipe_init()) { //fehler init Pipe } printf("TIDASERVER: %s -> Dupliezire Prozess\n",__FUNCTION__); pid = fork(); switch( pid ) { case -1: perror("fork"); exit(EXIT_FAILURE); break; case 0: // Sohn if(pipe_set_generation(0)) { exit(EXIT_FAILURE); } process2(); break; default: // Vater if(pipe_set_generation(1)) { exit(EXIT_FAILURE); } break; } printf("TIDASERVER: %s -> Vater startet mit berechnung und senden über pipe\n",__FUNCTION__); while(!test) //mainloop solange Shon Process noch aktive { switch(cam_read_frame()) { case 0: usleep(1000); break; case -1: // fehler read Freame. was tun? default: for(size_t i=0; i < cam_read_buffer_size/sizeof(uint16_t); i++) { cam_read_buffer[i]=cam_read_buffer[i] >> 6; } pipe_write(cam_read_buffer,cam_read_buffer_size); } test = waitpid(pid, 0,WNOHANG); printf("waitpid -> %d\n",test); } printf("TIDASERVER: %s -> ende Vater Prozess weil Shon Prozess beendet\n",__FUNCTION__); pipe_close(); tcp_close_client_socket(); tcp_close_server_socket(); exit(EXIT_SUCCESS); }
sohn:
void process2(void) { char DATA[PAGE_SIZE]; printf("TIDASERVER: %s -> Sohn startet mit senden über TCP\n",__FUNCTION__); while(1) { if(pipe_read(DATA,PAGE_SIZE)) { printf("TIDASERVER: %s -> ende Sohn process weil Pipe get nicht \n",__FUNCTION__); pipe_close(); tcp_close_client_socket(); tcp_close_server_socket(); exit(EXIT_SUCCESS); } if(tcp_write(DATA,PAGE_SIZE)) { pipe_read(DATA) printf("TIDASERVER: %s -> ende Sohn process weil tcp get nicht \n",__FUNCTION__); pipe_close(); tcp_close_client_socket(); tcp_close_server_socket(); exit(EXIT_SUCCESS); } } }
-
/*! * @struct struct pipefd_strukt pipefd * @brief struktur in der die Pipe File diskriptoren gespeichert werden * * @author JJ * @date 9.02.2017 * * @var VS Vater -> Sohn Pipe * @var send nach aufruf von int pipe_set_generation(int gereration) * der File Diskriptor in den Sohn oder Fater schreibt * @var resive nach aufruf von int pipe_set_generation(int gereration) * der File Diskriptor von dem Sohn oder Fater liest * */ struct pipefd_strukt { int VS[2]; int send; int resive; }pipefd={0}; /*! * @fn int pipe_init(void) * @brief initialiesiert die Pipe. auszuführen vor fork(2) * * @author JJ * @date 9.02.2017 * * @return 0: Success * -1: Error * */ int pipe_init(void) { printf("TIDASERVER: %s -> pipe init\n",__FUNCTION__); if(pipe(pipefd.VS)) { printf("TIDASERVER: %s -> pipe init error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } return 0; } /*! * @fn int pipe_set_generation(int gereration) * @brief stellt ein wer der Vater und wer der Sohn ist * * @author JJ * @date 9.02.2017 * * @param[in] gereration 0 Sohn Process * !=0 Vater Process * @return 0: Success * -1: Error * */ int pipe_set_generation(int gereration) { if(gereration) //Vater { printf("TIDASERVER: %s -> Vater schlist RX pipefd\n",__FUNCTION__); if(close(pipefd.VS[PIPE_RX])) //mus nicht auf der pipe VS empfangen { printf("TIDASERVER: %s -> close pipefd Vater error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } pipefd.send = pipefd.VS[PIPE_TX]; } else // Sohn { printf("TIDASERVER: %s -> Sohn schiest TX pipefd\n",__FUNCTION__); if(close(pipefd.VS[PIPE_TX])) //mus nicht auf der pipe VS senden { printf("TIDASERVER: %s -> close pipefd Sohn error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } pipefd.resive = pipefd.VS[PIPE_RX]; } return 0; } /*! * @fn int pipe_read(*char data_in, size_t n_bytes) * @brief sendet daten an die pipe * * @author JJ * @date 9.02.2017 * * @param[out] data_in daten die gesendet wurden sollen * @param[in] n_bytes Anzahl der zu empfangennen Bytes * @return 0: Success * -1: Error * */ int pipe_write(void* data_out, size_t n_bytes) { size_t retval=0; retval = write(pipefd.send,data_out,n_bytes); if(retval != n_bytes) { printf("TIDASERVER: %s -> pipe_write error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } return 0; } /*! * @fn int pipe_read(*char data_in, size_t n_bytes) * @brief liest daten vom der Pipe * * @author JJ * @date 9.02.2017 * * @param[out] data_in daten die enpfangen wurden sollen * @param[in] n_bytes Anzahl der zu empfangennen Bytes * @return 0: Success * -1: Error * */ int pipe_read(void* data_in, size_t n_bytes) { size_t retval=0; retval = read(pipefd.resive,data_in,n_bytes); if(retval != n_bytes) { printf("TIDASERVER: %s -> pipe_read error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } return 0; } /*! * @fn void pipe_close(void) * @brief schliest alle pipes * * @author JJ * @date 9.02.2017 * * */ void pipe_close(void) { printf("TIDASERVER: %s -> close Pipe\n",__FUNCTION__); close(pipefd.resive); close(pipefd.send); bzero(&pipefd,sizeof(pipefd)); }
-
Das sollte da nicht hängen.
man 7 pipe schrieb:
If all file descriptors referring to the read end of a pipe have been closed, then a write(2) will cause a SIGPIPE signal to be generated for the calling process. If the calling process is ignoring this signal, then write(2) fails with the error EPIPE.
write liefert einen ssize_t zurück, also ein size_t mit Vorzeichen. Wenn der Wert negativ ist (sprich -1), trat ein Fehler auf. Hier sollte dann in errno der Wert EPIPE stehen.
-
Oh. Wichtig ist natürlich, dass du das SIGPIPE Signal auch ignorierst, sonst terminiert der Vater bei dem write, wenn das Leseende geschlossen wird.
#define _POSIX_C_SOURCE 200809L #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <unistd.h> int main(void) { sigset_t mask; sigemptyset(&mask); struct sigaction sa = { .sa_handler = SIG_IGN, .sa_mask = mask, .sa_flags = SA_RESTART, }; if (sigaction(SIGPIPE, &sa, 0) == -1) { perror("sigaction"); return EXIT_FAILURE; } int p[2] = {-1, -1}; if (pipe(p) == -1) { perror("pipe"); return EXIT_FAILURE; } pid_t const pid = fork(); if (pid == -1) { perror("fork"); return EXIT_FAILURE; } else if (pid) // Parent { int const tx = p[1]; close(p[0]); // Close read end. while (1) { char const buf[] = "Hello child!"; ssize_t const ret = write(tx, buf, sizeof buf); if (ret == -1) { if (errno == EPIPE) { puts("Child exited."); return EXIT_SUCCESS; } perror("write"); return EXIT_FAILURE; } } } else // Child { int const rx = p[0]; close(p[1]); // Close write end. // Do something … char buf[512] = {0}; ssize_t const ret = read(rx, buf, sizeof buf - 1); if (ret == -1) { perror("read"); return EXIT_FAILURE; } printf("Child read: %s\n", buf); sleep(1); return EXIT_SUCCESS; // Child exits. } return EXIT_SUCCESS; }
-
ich werte den Rückgabewert von write() ja aus. doch obwohl der Sohn Prozess
geschossen ist, bekomme ich keine "-1" von write() im Vater Prozess. Stattdessen
bekomme ich gar nichts mehr. Er bleibt bei write() einfach hängen und tut nichts ./*! * @fn int pipe_read(*char data_in, size_t n_bytes) * @brief sendet daten an die pipe * * @author JJ * @date 9.02.2017 * * @param[out] data_in daten die gesendet wurden sollen * @param[in] n_bytes Anzahl der zu empfangennen Bytes * @return 0: Success * -1: Error * */ int pipe_write(void* data_out, size_t n_bytes) { ssize_t retval=0; retval = write(pipefd.send,data_out,n_bytes); if(retval != (ssize_t)n_bytes) { printf("TIDASERVER: %s -> pipe_write error %d, %s\n",__FUNCTION__, errno, strerror(errno)); return -1; } printf("%d %d \n",n_bytes,retval); return 0; }
-
Client ist verbunden
ps PID TTY TIME CMD 1012 ttymxc0 00:00:00 sh 1070 ttymxc0 00:00:00 tidaserver_jj 1071 ttymxc0 00:00:01 tidaserver_jj 1072 ttymxc0 00:00:00 tidaserver_jj 1073 ttymxc0 00:00:00 ps
Client schlisst die Verbindung
ps PID TTY TIME CMD 1012 ttymxc0 00:00:00 sh 1055 ttymxc0 00:00:00 tidaserver_jj 1056 ? 00:00:00 tidaserver_jj <defunct> 1058 ttymxc0 00:00:00 ps
die ausgabe:
483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 483840 TIDASERVER: tcp_write -> Client socket write error 0, Success TIDASERVER: process2 -> ende Sohn process weil tcp get nicht TIDASERVER: pipe_close -> close Pipe TIDASERVER: tcp_close_client_socket -> close Client Socket TIDASERVER: tcp_close_server_socket -> close Server Socket
danach kommt nichts mehr.
-
Sorry schwer von begriff!
sigset_t mask; sigemptyset(&mask); struct sigaction sa = { .sa_handler = SIG_IGN, .sa_mask = mask, .sa_flags = SA_RESTART, }; if (sigaction(SIGPIPE, &sa, 0) == -1) { perror("sigaction"); return EXIT_FAILURE; }
da ist der Punkt gewesen.
-
Joachim Jähn schrieb:
Sorry schwer von begriff!
sigset_t mask; sigemptyset(&mask); struct sigaction sa = { .sa_handler = SIG_IGN, .sa_mask = mask, .sa_flags = SA_RESTART, }; if (sigaction(SIGPIPE, &sa, 0) == -1) { perror("sigaction"); return EXIT_FAILURE; }
Wo war denn da das Problem genau?
da ist der Punkt gewesen.
-
da war kein Problem. das habe ich nicht gemacht. deswegen wurde der Vater Prozess terminiert. ich habe diesen Codeblock in meine pipe_init() übernommen. das hat geholfen. sihe Biolunar's beispiel.
-
das ganze läuft schon fast richtig. aber
jetzt erkennt der Vater das der Sohn nicht mehr da ist und schließt alle Verbindungen und fürt ein exit(EXIT_SUCCESS); aus
trotzdem liefert mir
ps 1159 ? 00:00:00 tidaserver_jj <defunct>
while(!waitpid(pid, 0,WNOHANG)) //mainloop solange Shon Process noch aktive { switch(cam_read_frame()) { case 0: printf("nF "); break; case -1: // fehler read Freame. was tun? default: for(size_t i=0; i < cam_read_buffer_size/sizeof(uint16_t); i++) { cam_read_buffer[i]=cam_read_buffer[i] >> 6; } pipe_write(cam_read_buffer,cam_read_buffer_size); } } printf("TIDASERVER: %s -> ende Vater Prozess weil Shon Prozess beendet\n",__FUNCTION__); pipe_close(); tcp_close_client_socket(); tcp_close_server_socket(); exit(EXIT_SUCCESS); }
-
Ok das Problem ist was anderes. hängt mit fork() zusammen.
Der main Server Starte via fork() Prozess1 und Prozess1() startet via fork() Prozess2().
Wenn Prozess2() beendet wird fragt Prozess1() den Status Prozess2 ab und beendet sich selbst. weil der main Server den Status des von Prozess1 nie ab fragt, bleibt ein Zombi von Prozess1 zurück.
Dem main Server interessiere es nicht ob Prozess1 noch da ist oder nicht.
Wie wird man in diesen Fall die Zomis wieder los? ( außer mit ner Shotgan )
-
Für jedes fork() brauchst du ein wait oder waitpid im Elternprozess um Zombies zu entfernen. Du kannst z.B. auf das SIGCHLD Signal im Elternprozess warten und im Signalhandler dann wait aufrufen.