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.


Log in to reply