質問

私のコードは次のとおりです。

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

(コードは、以下の 2 つの回答によって提案された変更を反映するように更新されましたが、まだ正常に機能しません...)

これが失敗するテストケースは次のとおりです。

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 が異なる)、これは 2 番目のプロセスが終了しなかったことを意味します。

これは機能するため、これは 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)

2 つの「終了」メッセージが簡単に表示されます。

それで、私のコードの何が問題なのでしょうか?

役に立ちましたか?

解決

でも、あなたのパイプラインの終了(およびthustがstdout=~fdPipe[1]クローズ)の最初のコマンドの後に、親がまだ開いfdPipe[1]ています。

管のもう一方のエンドポイントがまだ開いているので、

このように、パイプラインの第2のコマンドは、EOFを取得したことがないstdin=~fdPipe[0]を有している。

あなたは各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}操作のいずれかを実行する前にclosedup2重複のケースを処理する必要があります。これは、誰かが閉じ標準入力や標準出力を使用してシェルを起動するために管理している場合に発生する可能性があり、ここでのコードで大きな混乱になります。

編集

   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が指摘するように、引数リストは終わりを示すためにNULLポインタを持っている必要があります:

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」に変更します
  • execvp の後に 'exit(1);' を追加

一つの潜在的な問題はCMDARGSはそれの最後にゴミを持っているかもしれないということです。あなたが)(execvpのためにそれを渡す前にNULLポインタとその配列を終了することになっています。

はgrepはSTDINを受け入れているように見えます、しかし、それは(まだ)すべての問題を引き起こしていない可能性がありますので。

パイプからのファイル記述子は、参照計数し、各フォークでインクリメントされます。すべてのフォークのために、あなたはゼロに参照カウントを減らし、パイプを閉じることができるようにするために、両方の記述子のクローズを発行する必要があります。私は推測している。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top