문제

내 코드는 다음과 같습니다.

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

#define NUMPIPES 2

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

    pipe(fdPipe);

    while(1) {
        bBuffer = readline("Shell> ");

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

        sPtr = bBuffer;
        pCount = -1;

        do {
            aPtr = strsep(&sPtr, "|");
            pipeComms[++pCount] = aPtr;
        } while(aPtr);

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

            do {
                aPtr = strsep(&pipeComms[i], " ");
                cmdArgs[++aCount] = aPtr;
            } while(aPtr);

            cmdArgs[aCount] = 0;

            if(strlen(cmdArgs[0]) > 0) {
                pid = fork();

                if(pid == 0) {
                    if(i == 0) {
                        close(fdPipe[0]);

                        dup2(fdPipe[1], STDOUT_FILENO);

                        close(fdPipe[1]);
                    } else if(i == 1) {
                        close(fdPipe[1]);

                        dup2(fdPipe[0], STDIN_FILENO);

                        close(fdPipe[0]);
                    }

                    execvp(cmdArgs[0], cmdArgs);
                    exit(1);
                } else {
                    lPids[i] = pid;

                    /*waitpid(pid, &status, 0);

                    if(WIFEXITED(status)) {
                        printf("[%d] TERMINATED (Status: %d)\n",
                            pid, WEXITSTATUS(status));
                    }*/
                }
            }
        }

        for(i = 0; i < pCount; i++) {
            waitpid(lPids[i], &status, 0);

            if(WIFEXITED(status)) {
                printf("[%d] TERMINATED (Status: %d)\n",
                    lPids[i], WEXITSTATUS(status));
            }
        }
    }

    return 0;
}

(아래 두 가지 답변에서 제안한 변경 사항을 반영하도록 코드가 업데이트되었지만 여전히 제대로 작동하지 않습니다 ...)

이것이 실패한 테스트 사례는 다음과 같습니다.

nazgulled ~/Projects/SO/G08 $ ls -l
total 8
-rwxr-xr-x 1 nazgulled nazgulled  7181 2009-05-27 17:44 a.out
-rwxr-xr-x 1 nazgulled nazgulled   754 2009-05-27 01:42 data.h
-rwxr-xr-x 1 nazgulled nazgulled  1305 2009-05-27 17:50 main.c
-rwxr-xr-x 1 nazgulled nazgulled   320 2009-05-27 01:42 makefile
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o
-rwxr-xr-x 1 nazgulled nazgulled    16 2009-05-27 17:19 test
nazgulled ~/Projects/SO/G08 $ ./a.out 
Shell> ls -l|grep prog
[4804] TERMINATED (Status: 0)
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o

문제는 그 후에 쉘로 돌아가야 한다는 것입니다. 추가 입력을 기다리는 "Shell> "이 표시되어야 합니다.또한 "[4804] TERMINATED(상태:0)"(그러나 다른 PID를 사용함) 이는 두 번째 프로세스가 종료되지 않았음을 의미합니다.

나는 이것이 grep과 관련이 있다고 생각합니다. 왜냐하면 이것이 작동하기 때문입니다:

nazgulled ~/Projects/SO/G08 $ ./a.out 
Shell> echo q|sudo fdisk /dev/sda
[4838] TERMINATED (Status: 0)

The number of cylinders for this disk is set to 1305.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): 
[4839] TERMINATED (Status: 0)

두 개의 "종료" 메시지를 쉽게 볼 수 있습니다.

그럼 내 코드에 무슨 문제가 있는 걸까요?

도움이 되었습니까?

해결책

파이프 라인의 첫 명령이 종료 된 후에도 닫힙니다. stdout=~fdPipe[1]), 부모는 여전히 가지고 있습니다 fdPipe[1] 열려 있는.

따라서 파이프 라인의 두 번째 명령은 stdin=~fdPipe[0] 파이프의 다른 종말점이 여전히 열려 있기 때문에 결코 EOF를 얻지 못합니다.

새로운 것을 만들어야합니다 pipe(fdPipe) 각각 |, 부모의 두 엔드 포인트를 모두 닫으십시오. 즉

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
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

또한 더 안전한 경우, 당신은 사건을 처리해야합니다. fdPipe 그리고 {STDIN_FILENO,STDOUT_FILENO} 어떤 일을 수행하기 전에 겹치는 close 그리고 dup2 운영. 누군가가 stdin 또는 stdout을 닫은 상태에서 당신의 쉘을 시작한 경우, 여기에서 코드와 큰 혼란을 초래할 수 있습니다.

편집하다

   fdPipe1           fdPipe3
      v                 v
cmd1  |  cmd2  |  cmd3  |  cmd4  |  cmd5
               ^                 ^
            fdPipe2           fdPipe4

부모의 파이프의 끝점을 닫는 것 외에도 나는 fdPipe1, fdPipe2, 등. 할 수 없습니다 동일합니다 pipe().

/* suppose stdin and stdout have been closed...
 * for example, if your program was started with "./a.out <&- >&-" */
close(0), close(1);

/* then the result you get back from pipe() is {0, 1} or {1, 0}, since
 * fd numbers are always allocated from the lowest available */
pipe(fdPipe);

close(0);
dup2(fdPipe[0], 0);

나는 당신이 사용하지 않는다는 것을 알고 있습니다 close(0) 현재 코드에서는 마지막 단락 에서이 경우에주의를 기울 이도록 경고합니다.

편집하다

다음과 같은 최소 코드로 변경하면 언급 한 특정 실패한 사례에서 작동합니다.

@@ -12,6 +12,4 @@
     pid_t pid;

-    pipe(fdPipe);
-
     while(1) {
         bBuffer = readline("Shell> ");
@@ -29,4 +27,6 @@
         } while(aPtr);

+        pipe(fdPipe);
+
         for(i = 0; i < pCount; i++) {
                 aCount = -1;
@@ -72,4 +72,7 @@
         }

+        close(fdPipe[0]);
+        close(fdPipe[1]);
+
         for(i = 0; i < pCount; i++) {
                 waitpid(lPids[i], &status, 0);

이것은 파이프 라인에서 하나 이상의 명령에 대해서는 작동하지 않습니다. 이를 위해서는 다음과 같은 것이 필요합니다.

@@ -9,9 +9,7 @@
 int main(int argc, char *argv[]) {
     char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
-    int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
+    int fdPipe[2], fdPipe2[2], pCount, aCount, i, status, lPids[NUMPIPES];
     pid_t pid;

-    pipe(fdPipe);
-
     while(1) {
         bBuffer = readline("Shell> ");
@@ -32,4 +30,7 @@
                 aCount = -1;

+                if (i + 1 < pCount)
+                    pipe(fdPipe2);
+
                 do {
                         aPtr = strsep(&pipeComms[i], " ");
@@ -43,11 +44,12 @@

                         if(pid == 0) {
-                                if(i == 0) {
-                                        close(fdPipe[0]);
+                                if(i + 1 < pCount) {
+                                        close(fdPipe2[0]);

-                                        dup2(fdPipe[1], STDOUT_FILENO);
+                                        dup2(fdPipe2[1], STDOUT_FILENO);

-                                        close(fdPipe[1]);
-                                } else if(i == 1) {
+                                        close(fdPipe2[1]);
+                                }
+                                if(i != 0) {
                                         close(fdPipe[1]);

@@ -70,4 +72,17 @@
                         }
                 }
+
+                if (i != 0) {
+                    close(fdPipe[0]);
+                    close(fdPipe[1]);
+                }
+
+                fdPipe[0] = fdPipe2[0];
+                fdPipe[1] = fdPipe2[1];
+        }
+
+        if (pCount) {
+            close(fdPipe[0]);
+            close(fdPipe[1]);
         }

다른 팁

execvp () 후에 오류 종료가 있어야합니다. 언젠가 실패합니다.

exit(EXIT_FAILURE);

@uncleo가 지적했듯이, 인수 목록에는 끝을 나타내는 널 포인터가 있어야합니다.

cmdArgs[aCount] = 0;

두 프로그램을 모두 자유롭게 실행하게된다는 것은 분명하지 않습니다. 두 번째를 시작하기 전에 파이프 라인의 첫 번째 프로그램이 완료되기 전에 파이프 라인의 첫 번째 프로그램이 필요합니다. 이는 파이프가 가득 차서 첫 번째 프로그램이 차단하는 경우 성공을위한 레시피가 아닙니다.

조나단의 생각은 옳습니다.첫 번째 프로세스를 사용하여 다른 모든 프로세스를 포크합니다.다음 항목이 분기되기 전에 각 항목이 완료될 때까지 실행해야 합니다.

대신, 지금처럼 루프에서 프로세스를 분기하되 내부 루프 외부(셸 프롬프트의 경우 큰 루프 하단)에서 프로세스를 기다리세요.

loop //for prompt
    next prompt
    loop //to fork tasks, store the pids
        if pid == 0 run command
        else store the pid
    end loop
    loop // on pids
        wait
    end loop
end loop

당신의 포크 프로세스가 계속 실행될 것이라고 생각합니다.

중 하나를 시도하십시오 :

  • 'return execvp'로 변경
  • '종료 (1);'추가 execvp 이후

잠재적 인 문제 중 하나는 CMDARG가 끝에 쓰레기를 가질 수 있다는 것입니다. execvp ()에 전달하기 전에 널 포인터가있는 배열을 종료해야합니다.

그러나 Grep이 Stdin을 수락하는 것처럼 보이므로 아직 문제가 발생하지 않을 수 있습니다.

파이프의 파일 디스크립터는 참조 계산되고 각 포크로 증가합니다. 모든 포크의 경우 기준 수를 0으로 줄이고 파이프를 닫으려면 두 설명자에 닫기를 발행해야합니다. 나는 추측하고있다.

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