문제

나는 며칠 뒤에 이것에 대한 질문이 있어요.내 솔루션은 허용된 답변에서 제안된 내용과 일치합니다.그런데 내 친구가 다음과 같은 해결책을 내놓았습니다.

아래 답변의 제안 사항을 반영하기 위해 코드가 여러 번 업데이트되었습니다(수정 버전 확인).새로운 답변을 제공하려는 경우 문제가 많았던 이전 코드가 아닌 이 새 코드를 염두에 두고 답변해 주시기 바랍니다.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]){
    int fd[2], i, aux, std0, std1;

    do {
        std0 = dup(0); // backup stdin
        std1 = dup(1); // backup stdout

        // let's pretend I'm reading commands here in a shell prompt
        READ_COMMAND_FROM_PROMPT();

        for(i=1; i<argc; i++) {
            // do we have a previous command?
            if(i > 1) {
                dup2(aux, 0);
                close(aux);
            }

            // do we have a next command?
            if(i < argc-1) {
                pipe(fd);

                aux = fd[0];
                dup2(fd[1], 1);
                close(fd[1]);
            }

            // last command? restore stdout...
            if(i == argc-1) {
                dup2(std1, 1);
                close(std1);
            }

            if(!fork()) {
                // if not last command, close all pipe ends
                // (the child doesn't use them)
                if(i < argc-1) {
                    close(std0);
                    close(std1);
                    close(fd[0]);
                }

                execlp(argv[i], argv[i], NULL);
                exit(0);
            }
        }

        // restore stdin to be able to keep using the shell
        dup2(std0, 0);
        close(std0);
    }

    return 0;
}

이는 bash와 같은 파이프를 통해 일련의 명령을 시뮬레이션합니다. 예를 들면 다음과 같습니다.cmd1 명령 | cmd2 명령 | ...| cmd_n.내가 "시뮬레이트"라고 말한 이유는 보시다시피 명령이 실제로 인수에서 읽혀지기 때문입니다.간단한 쉘 프롬프트를 코딩하는 시간을 절약하기 위해...

물론 오류 처리와 같이 수정하고 추가해야 할 몇 가지 문제가 있지만 여기서는 그게 요점이 아닙니다.코드를 어느 정도 얻은 것 같지만 이 모든 것이 어떻게 작동하는지 여전히 혼란스럽습니다.

내가 뭔가를 놓치고 있는 걸까요? 아니면 이것이 실제로 효과가 있고 문제를 해결하는 훌륭하고 깨끗한 솔루션입니까?그렇지 않다면 누구든지 이 코드에 있는 중요한 문제를 지적해 줄 수 있습니까?

도움이 되었습니까?

해결책

합리적으로 보이지만 누출 문제를 해결해야 합니다. std 그리고 aux 자식과 루프 이후, 부모의 원본 stdin 영원히 사라졌습니다.

컬러로 하면 더 좋을 것 같은데...

./a.out foo bar baz <stdin >stdout
std = dup(stdout)     ||     |+==========================std
                      ||     ||                          ||
pipe(fd)              ||     ||    pipe1[0] -- pipe0[1]  ||
                      ||     ||       ||          ||     ||
aux = fd[0]           ||     ||      aux          ||     ||
                      ||     XX       ||          ||     ||
                      ||      /-------++----------+|     ||
dup2(fd[1], 1)        ||     //       ||          ||     ||
                      ||     ||       ||          ||     ||
close(fd[1])          ||     ||       ||          XX     ||
                      ||     ||       ||                 ||
fork+exec(foo)        ||     ||       ||                 ||
                      XX     ||       ||                 ||
                       /-----++-------+|                 ||
dup2(aux, 0)          //     ||       ||                 ||
                      ||     ||       ||                 ||
close(aux)            ||     ||       XX                 ||
                      ||     ||                          ||
pipe(fd)              ||     ||    pipe2[0] -- pipe2[1]  ||
                      ||     ||       ||          ||     ||
aux = fd[0]           ||     ||      aux          ||     ||
                      ||     XX       ||          ||     ||
                      ||      /-------++----------+|     ||
dup2(fd[1], 1)        ||     //       ||          ||     ||
                      ||     ||       ||          ||     ||
close(fd[1])          ||     ||       ||          XX     ||
                      ||     ||       ||                 ||
fork+exec(bar)        ||     ||       ||                 ||
                      XX     ||       ||                 ||
                       /-----++-------+|                 ||
dup2(aux, 0)          //     ||       ||                 ||
                      ||     ||       ||                 ||
close(aux)            ||     ||       XX                 ||
                      ||     ||                          ||
pipe(fd)              ||     ||    pipe3[0] -- pipe3[1]  ||
                      ||     ||       ||          ||     ||
aux = fd[0]           ||     ||      aux          ||     ||
                      ||     XX       ||          ||     ||
                      ||      /-------++----------+|     ||
dup2(fd[1], 1)        ||     //       ||          ||     ||
                      ||     ||       ||          ||     ||
close(fd[1])          ||     ||       ||          XX     ||
                      ||     XX       ||                 ||
                      ||      /-------++-----------------+|
dup2(std, 1)          ||     //       ||                 ||
                      ||     ||       ||                 ||
fork+exec(baz)        ||     ||       ||                 ||
  • foo 얻다 stdin=stdin, stdout=pipe1[1]
  • bar 얻다 stdin=pipe1[0], stdout=pipe2[1]
  • baz 얻다 stdin=pipe2[0], stdout=stdout

내 제안은 부모의 내용을 망가뜨리는 것을 방지한다는 점에서 다릅니다. stdin 그리고 stdout, 자식 내에서만 조작하고 FD를 유출하지 않습니다.하지만 다이어그램을 그리는 것이 조금 더 어렵습니다.

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
parent
    cmds = [foo, bar, baz]
    fds = {0: stdin, 1: stdout}

cmd = cmds[0] {
    there is a next cmd {
        pipe(new_fds)
            new_fds = {3, 4}
            fds = {0: stdin, 1: stdout, 3: pipe1[0], 4: pipe1[1]}
    }

    fork             => child
                        there is a next cmd {
                            close(new_fds[0])
                                fds = {0: stdin, 1: stdout, 4: pipe1[1]}
                            dup2(new_fds[1], 1)
                                fds = {0: stdin, 1: pipe1[1], 4: pipe1[1]}
                            close(new_fds[1])
                                fds = {0: stdin, 1: pipe1[1]}
                        }
                        exec(cmd)

    there is a next cmd {
        old_fds = new_fds
            old_fds = {3, 4}
    }
}

cmd = cmds[1] {
    there is a next cmd {
        pipe(new_fds)
            new_fds = {5, 6}
            fds = {0: stdin, 1: stdout, 3: pipe1[0], 4: pipe1[1],
                                        5: pipe2[0], 6: pipe2[1]}
    }

    fork             => child
                        there is a previous cmd {
                            dup2(old_fds[0], 0)
                                fds = {0: pipe1[0], 1: stdout,
                                       3: pipe1[0], 4: pipe1[1],
                                       5: pipe2[0], 6: pipe2[1]}
                            close(old_fds[0])
                                fds = {0: pipe1[0], 1: stdout,
                                                    4: pipe1[1],
                                       5: pipe2[0]  6: pipe2[1]}
                            close(old_fds[1])
                                fds = {0: pipe1[0], 1: stdout,
                                       5: pipe2[0], 6: pipe2[1]}
                        }
                        there is a next cmd {
                            close(new_fds[0])
                                fds = {0: pipe1[0], 1: stdout, 6: pipe2[1]}
                            dup2(new_fds[1], 1)
                                fds = {0: pipe1[0], 1: pipe2[1], 6: pipe2[1]}
                            close(new_fds[1])
                                fds = {0: pipe1[0], 1: pipe1[1]}
                        }
                        exec(cmd)

    there is a previous cmd {
        close(old_fds[0])
            fds = {0: stdin, 1: stdout,              4: pipe1[1],
                                        5: pipe2[0], 6: pipe2[1]}
        close(old_fds[1])
            fds = {0: stdin, 1: stdout, 5: pipe2[0], 6: pipe2[1]}
    }

    there is a next cmd {
        old_fds = new_fds
            old_fds = {3, 4}
    }
}

cmd = cmds[2] {
    fork             => child
                        there is a previous cmd {
                            dup2(old_fds[0], 0)
                                fds = {0: pipe2[0], 1: stdout,
                                       5: pipe2[0], 6: pipe2[1]}
                            close(old_fds[0])
                                fds = {0: pipe2[0], 1: stdout,
                                                    6: pipe2[1]}
                            close(old_fds[1])
                                fds = {0: pipe2[0], 1: stdout}
                        }
                        exec(cmd)

    there is a previous cmd {
        close(old_fds[0])
            fds = {0: stdin, 1: stdout,              6: pipe2[1]}
        close(old_fds[1])
            fds = {0: stdin, 1: stdout}
    }
}

편집하다

업데이트된 코드는 이전 FD 누출을 수정했지만… 하나를 추가했습니다.너 지금 새고 있어 std0 아이들에게.Jon이 말했듯이 이것은 아마도 대부분의 프로그램에 위험하지 않을 것입니다 ...하지만 여전히 이것보다 더 나은 동작 쉘을 작성해야 합니다.

비록 그것이 임시적이라 할지라도, 나는 여러분 자신의 쉘의 표준 in/out/err(0/1/2)을 맹글링하지 말 것을 강력히 권합니다. exec 직전에 자식 내에서만 그렇게 하십시오.왜?몇 가지를 추가한다고 가정 해 보겠습니다. printf 중간에 디버깅하거나 오류 조건으로 인해 구제 조치를 취해야 합니다.엉망인 표준 파일 설명자를 먼저 정리하지 않으면 문제가 발생할 수 있습니다.제발 물건을 갖기 위해서 예상치 못한 상황에서도 예상대로 작동, 필요할 때까지 장난치지 마세요.


편집하다

다른 댓글에서 언급했듯이 작은 부분으로 나누면 이해하기가 훨씬 쉽습니다.이 작은 도우미는 쉽게 이해할 수 있고 버그가 없어야 합니다.

/* cmd, argv: passed to exec
 * fd_in, fd_out: when not -1, replaces stdin and stdout
 * return: pid of fork+exec child
 */
int fork_and_exec_with_fds(char *cmd, char **argv, int fd_in, int fd_out) {
    pid_t child = fork();
    if (fork)
        return child;

    if (fd_in != -1 && fd_in != 0) {
        dup2(fd_in, 0);
        close(fd_in);
    }

    if (fd_out != -1 && fd_in != 1) {
        dup2(fd_out, 1);
        close(fd_out);
    }

    execvp(cmd, argv);
    exit(-1);
}

다음과 같이 해야 합니다.

void run_pipeline(int num, char *cmds[], char **argvs[], int pids[]) {
    /* initially, don't change stdin */
    int fd_in = -1, fd_out;
    int i;

    for (i = 0; i < num; i++) {
        int fd_pipe[2];

        /* if there is a next command, set up a pipe for stdout */
        if (i + 1 < num) {
            pipe(fd_pipe);
            fd_out = fd_pipe[1];
        }
        /* otherwise, don't change stdout */
        else
            fd_out = -1;

        /* run child with given stdin/stdout */
        pids[i] = fork_and_exec_with_fds(cmds[i], argvs[i], fd_in, fd_out);

        /* nobody else needs to use these fds anymore
         * safe because close(-1) does nothing */
        close(fd_in);
        close(fd_out);

        /* set up stdin for next command */
        fd_in = fd_pipe[0];
    }
}

너는 볼 수있어 세게 때리다'에스 execute_cmd.c#execute_disk_command 에서 전화를 받고 execute_cmd.c#execute_pipeline, xsh'에스 process.c#process_run 에서 전화를 받고 jobs.c#job_run, 그리고 심지어 BusyBox'에스 다양한 작은 그리고 최소한의 껍질 그들을 분할합니다.

다른 팁

주요 문제는 많은 파이프를 만들고 모든 끝이 올바르게 닫혀 있는지 확인하지 않는다는 것입니다. 파이프를 만들면 두 개의 파일 설명자가 얻을 수 있습니다. 포크가 있으면 네 개의 파일 설명자가 있습니다. 만약 너라면 dup() 또는 dup2() 파이프의 한쪽 끝은 표준 설명 자로 이루어지면 파이프의 양쪽 끝을 닫아야합니다. 최소한 하나는 DUP () 또는 dup2 () 작동 후에야 닫기 중 하나 이상이어야합니다.


첫 번째 명령에 사용할 수있는 파일 설명자를 고려하십시오 (일반적으로 처리해야 할 것입니다. pipe() 또는 하나의 명령으로 필요한 I/O 리디렉션), 그러나 코드를 적합하게 유지하기 위해 오류 처리가 제거되었음을 알고 있습니다.)

    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);
    }

이에 주목하십시오 fd[0] 아이가 닫히지 않으면, 아이는 표준 입력에 대해 결코 EOF를 얻지 못할 것입니다. 이것은 일반적으로 문제가됩니다. 비 폐쇄 std 덜 중요합니다.


수정 코드 재 방문 (2009-06-03T20 : 52-07 : 00) ...

프로세스가 파일 설명자 0, 1, 2 (표준 입력, 출력, 오류)로 시작한다고 가정합니다. 또한 처리 할 정확한 3 개의 명령이 있다고 가정합니다. 이전과 마찬가지로이 코드는 주석이있는 루프를 기록합니다.

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

따라서 모든 어린이는 원래 표준 입력을 파일 설명 자로 연결합니다. 이것은 이상적이지는 않지만 이상적이지는 않지만 이상적이지는 않습니다. 나는 이것이 중요한 상황을 찾기가 힘들다.

파일 설명자를 닫는다 4 부모의 실수입니다. std1 루프 내부에서 초기화되지 않습니다.

일반적으로 이것은 정확하지만 정확하지는 않습니다.

그것은 기대하지 않는 결과를 줄 것입니다. 그것은 좋은 해결책과는 거리가 멀다. 부모 프로세스의 표준 설명자를 엉망으로 만들고, 표준 입력을 복구하지 않으며, 설명자가 어린이에게 누출되기 등.

재귀 적으로 생각한다면 이해하기가 더 쉬울 수 있습니다. 아래는 오류 확인없이 올바른 솔루션입니다. 링크 된 목록 유형을 고려하십시오 command, 그와 함께 next 포인터와 a argv 정렬.

void run_pipeline(command *cmd, int input) {
  int pfds[2] = { -1, -1 };

  if (cmd->next != NULL) {
    pipe(pfds);
  }
  if (fork() == 0) { /* child */
    if (input != -1) {
      dup2(input, STDIN_FILENO);
      close(input);
    }
    if (pfds[1] != -1) {
      dup2(pfds[1], STDOUT_FILENO);
      close(pfds[1]);
    }
    if (pfds[0] != -1) {
      close(pfds[0]);
    }
    execvp(cmd->argv[0], cmd->argv);
    exit(1);
  }
  else { /* parent */
    if (input != -1) {
      close(input);
    }
    if (pfds[1] != -1) {
      close(pfds[1]);
    }
    if (cmd->next != NULL) {
      run_pipeline(cmd->next, pfds[0]);
    }
  }
}

링크리스트에서 첫 번째 명령으로 호출하고 input = -1. 나머지는합니다.

이 질문과 다른 질문 모두 (첫 번째 게시물에 링크 된대로), 일시적인 이 질문에서 가능한 해결책으로 입증 된 부모 파일 디스크립터를 엉망으로 만들지 않고 문제에 대한 해결책을 제안했습니다.

나는 그의 해결책을 얻지 못했고 이해하려고 노력했지만 이해하려고 노력했지만 그것을 얻을 수없는 것 같습니다. 나는 또한 이해하지 않고 코딩하려고했지만 작동하지 않았습니다. 아마도 내가 그것을 올바르게 이해하지 못했고 코딩되어야했을 것입니다.

어쨌든, 나는 의사 코드에서 이해했던 것들 중 일부를 사용하여 내 자신의 솔루션을 생각해 내려고 노력했으며 다음과 같습니다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

#define NUMPIPES 5
#define NUMARGS 10

int main(int argc, char *argv[]) {
    char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[NUMARGS];
    int aPipe[2], bPipe[2], pCount, aCount, i, status;
    pid_t pid;

    using_history();

    while(1) {
        bBuffer = readline("\e[1;31mShell \e[1;32m# \e[0m");

        if(!strcasecmp(bBuffer, "exit")) {
            return 0;
        }

        if(strlen(bBuffer) > 0) {
            add_history(bBuffer);
        }

        sPtr = bBuffer;
        pCount =0;

        do {
            aPtr = strsep(&sPtr, "|");

            if(aPtr != NULL) {
                if(strlen(aPtr) > 0) {
                    pipeComms[pCount++] = aPtr;
                }
            }
        } while(aPtr);

        cmdArgs[pCount] = NULL;

        for(i = 0; i < pCount; i++) {
            aCount = 0;

            do {
                aPtr = strsep(&pipeComms[i], " ");

                if(aPtr != NULL) {
                    if(strlen(aPtr) > 0) {
                        cmdArgs[aCount++] = aPtr;
                    }
                }
            } while(aPtr);

            cmdArgs[aCount] = NULL;

            // Do we have a next command?
            if(i < pCount-1) {
                // Is this the first, third, fifth, etc... command?
                if(i%2 == 0) {
                    pipe(aPipe);
                }

                // Is this the second, fourth, sixth, etc... command?
                if(i%2 == 1) {
                    pipe(bPipe);
                }
            }

            pid = fork();

            if(pid == 0) {
                // Is this the first, third, fifth, etc... command?
                if(i%2 == 0) {
                    // Do we have a previous command?
                    if(i > 0) {
                        close(bPipe[1]);
                        dup2(bPipe[0], STDIN_FILENO);
                        close(bPipe[0]);
                    }

                    // Do we have a next command?
                    if(i < pCount-1) {
                        close(aPipe[0]);
                        dup2(aPipe[1], STDOUT_FILENO);
                        close(aPipe[1]);
                    }
                }

                // Is this the second, fourth, sixth, etc... command?
                if(i%2 == 1) {
                    // Do we have a previous command?
                    if(i > 0) {
                        close(aPipe[1]);
                        dup2(aPipe[0], STDIN_FILENO);
                        close(aPipe[0]);
                    }

                    // Do we have a next command?
                    if(i < pCount-1) {
                        close(bPipe[0]);
                        dup2(bPipe[1], STDOUT_FILENO);
                        close(bPipe[1]);
                    }
                }

                execvp(cmdArgs[0], cmdArgs);
                exit(1);
            } else {
                // Do we have a previous command?
                if(i > 0) {
                    // Is this the first, third, fifth, etc... command?
                    if(i%2 == 0) {
                        close(bPipe[0]);
                        close(bPipe[1]);
                    }

                    // Is this the second, fourth, sixth, etc... command?
                    if(i%2 == 1) {
                        close(aPipe[0]);
                        close(aPipe[1]);
                    }
                }

                // wait for the last command? all others will run in the background
                if(i == pCount-1) {
                    waitpid(pid, &status, 0);
                }

                // I know they will be left as zombies in the table
                // Not relevant for this...
            }
        }
    }

    return 0;
}

이것은 최고이고 가장 깨끗한 솔루션은 아니지만 제가 생각해 낼 수있는 것이었고 가장 중요한 것은 내가 이해할 수있는 것입니다. 내가 이해하지 못하는 일을하는 것이 좋은 점은 선생님이 평가하고 코드가 무엇을하는지 설명 할 수 없습니까?

어쨌든, 당신은 이것에 대해 어떻게 생각하십니까?

이것은 내 "최종"코드입니다 일시적인 제안 :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

#define NUMPIPES 5
#define NUMARGS 10

int main(int argc, char *argv[]) {
    char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[NUMARGS];
    int newPipe[2], oldPipe[2], pCount, aCount, i, status;
    pid_t pid;

    using_history();

    while(1) {
        bBuffer = readline("\e[1;31mShell \e[1;32m# \e[0m");

        if(!strcasecmp(bBuffer, "exit")) {
            return 0;
        }

        if(strlen(bBuffer) > 0) {
            add_history(bBuffer);
        }

        sPtr = bBuffer;
        pCount = -1;

        do {
            aPtr = strsep(&sPtr, "|");

            if(aPtr != NULL) {
                if(strlen(aPtr) > 0) {
                    pipeComms[++pCount] = aPtr;
                }
            }
        } while(aPtr);

        cmdArgs[++pCount] = NULL;

        for(i = 0; i < pCount; i++) {
            aCount = -1;

            do {
                aPtr = strsep(&pipeComms[i], " ");

                if(aPtr != NULL) {
                    if(strlen(aPtr) > 0) {
                        cmdArgs[++aCount] = aPtr;
                    }
                }
            } while(aPtr);

            cmdArgs[++aCount] = NULL;

            // do we have a next command?
            if(i < pCount-1) {
                pipe(newPipe);
            }

            pid = fork();

            if(pid == 0) {
                // do we have a previous command?
                if(i > 0) {
                    close(oldPipe[1]);
                    dup2(oldPipe[0], 0);
                    close(oldPipe[0]);
                }

                // do we have a next command?
                if(i < pCount-1) {
                    close(newPipe[0]);
                    dup2(newPipe[1], 1);
                    close(newPipe[1]);
                }

                // execute command...
                execvp(cmdArgs[0], cmdArgs);
                exit(1);
            } else {
                // do we have a previous command?
                if(i > 0) {
                    close(oldPipe[0]);
                    close(oldPipe[1]);
                }

                // do we have a next command?
                if(i < pCount-1) {
                    oldPipe[0] = newPipe[0];
                    oldPipe[1] = newPipe[1];
                }

                // wait for last command process?
                if(i == pCount-1) {
                    waitpid(pid, &status, 0);
                }
            }
        }
    }

    return 0;
}

이제 괜찮습니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top