Does this multiple pipes code in C makes sense?

后端 未结 5 1551
暖寄归人
暖寄归人 2020-12-18 13:39

I\'ve created a question about this a few days. My solution is something in the lines of what was suggested in the accepted answer. However, a friend of mine came up with th

5条回答
  •  南笙
    南笙 (楼主)
    2020-12-18 14:13

    The key problem is that you create a bunch of pipes and don't make sure that all the ends are closed properly. If you create a pipe, you get two file descriptors; if you fork, then you have four file descriptors. If you dup() or dup2() one end of the pipe to a standard descriptor, you need to close both ends of the pipe - at least one of the closes must be after the dup() or dup2() operation.


    Consider the file descriptors available to the first command (assuming there are at least two - something that should be handled in general (no pipe() or I/O redirection needed with just one command), but I recognize that the error handling is eliminated to keep the code suitable for SO):

        std=dup(1);    // Likely: std = 3
        pipe(fd);      // Likely: fd[0] = 4, fd[1] = 5
        aux = fd[0];
        dup2(fd[1], 1);
        close(fd[1]);  // Closes 5
    
        if (fork() == 0) {
             // Need to close: fd[0] aka aux = 4
             // Need to close: std = 3
             close(fd[0]);
             close(std);
             execlp(argv[i], argv[i], NULL);
             exit(1);
        }
    

    Note that because fd[0] is not closed in the child, the child will never get EOF on its standard input; this is usually problematic. The non-closure of std is less critical.


    Revisiting amended code (as of 2009-06-03T20:52-07:00)...

    Assume that process starts with file descriptors 0, 1, 2 (standard input, output, error) open only. Also assume we have exactly 3 commands to process. As before, this code writes out the loop with annotations.

    std0 = dup(0); // backup stdin - 3
    std1 = dup(1); // backup stdout - 4
    
    // Iteration 1 (i == 1)
    // We have another command
    pipe(fd);   // fd[0] = 5; fd[1] = 6
    aux = fd[0]; // aux = 5
    dup2(fd[1], 1);
    close(fd[1]);       // 6 closed
    // Not last command
    if (fork() == 0) {
        // Not last command
        close(std1);    // 4 closed
        close(fd[0]);   // 5 closed
        // Minor problemette: 3 still open
        execlp(argv[i], argv[i], NULL);
        }
    // Parent has open 3, 4, 5 - no problem
    
    // Iteration 2 (i == 2)
    // There was a previous command
    dup2(aux, 0);      // stdin now on read end of pipe
    close(aux);        // 5 closed
    // We have another command
    pipe(fd);          // fd[0] = 5; fd[1] = 6
    aux = fd[0];
    dup2(fd[1], 1);
    close(fd[1]);      // 6 closed
    // Not last command
    if (fork() == 0) {
        // Not last command
        close(std1);   // 4 closed
        close(fd[0]);  // 5 closed
        // As before, 3 is still open - not a major problem
        execlp(argv[i], argv[i], NULL);
        }
    // Parent has open 3, 4, 5 - no problem
    
    // Iteration 3 (i == 3)
    // We have a previous command
    dup2(aux, 0);      // stdin is now read end of pipe 
    close(aux);        // 5 closed
    // No more commands
    
    // Last command - restore stdout...
    dup2(std1, 1);     // stdin is back where it started
    close(std1);       // 4 closed
    
    if (fork() == 0) {
        // Last command
        // 3 still open
        execlp(argv[i], argv[i], NULL);
    }
    // Parent has closed 4 when it should not have done so!!!
    // End of loop
    // restore stdin to be able to keep using the shell
    dup2(std0, 0);
    // 3 still open - as desired
    

    So, all the children have the original standard input connected as file descriptor 3. This is not ideal, though it is not dreadfully traumatic; I'm hard pressed to find a circumstance where this would matter.

    Closing file descriptor 4 in the parent is a mistake - the next iteration of 'read a command and process it won't work because std1 is not initialized inside the loop.

    Generally, this is close to correct - but not quite correct.

提交回复
热议问题