1

fork() を使用してプロセスのリングをセットアップし、リングを介してメッセージを渡すというコーディングの割り当てがあります。さて、この時点で明らかな問題は、最初のプロセスから直接接続された子プロセスにメッセージを渡すことができないことです。(最初にテストとして 1 つのメッセージ パスを実行するだけです)ただし、リングが正しく機能していない可能性もあります。私が使用しているメッセージパッシングテストが本質的にサンプルコードそのままであることを考えると、これが事実であったとしても驚かないでしょうが、リングを形成するコードもそうです...

だから、私の質問は、私のコードで何が間違っているのかを整理するのを手伝ってくれる人はいますか?

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

int msg[2];
const int MAX = 100;

int main(int argc, char *argv[]) {

    int master, i, child, num, pid, ppid, counter, loops;
    char buffer[MAX];
    num = atoi(argv[1]);
    loops = atoi(argv[2]);
    counter = 0;

    master = (int)getpid();

    //check num arguments
    if (argc != 4) {
        fprintf(stderr, "%s\n", "incorrect # arguments");
    }
    pipe(msg);                      //create pipe
    dup2(msg[0], STDIN_FILENO);     //duplicate pipes
    dup2(msg[1], STDOUT_FILENO);
    close(msg[0]);                  //close ends of pipe
    close(msg[1]);

    //create other processes
    for(i=1; i<num; i++) {
        pipe(msg);                  //create new pipe

        //create new process
        child = fork();             //parent has child id
        pid = (int)getpid();        //has own pid
        ppid = (int)getppid();      //has parent pid

        //if parent, fix output
        if(child > 0){
            dup2(msg[1], STDOUT_FILENO);
        } else {
            dup2(msg[0], STDIN_FILENO);
        }
        close(msg[0]);
        close(msg[1]);
        if(child){
            break;
        }
    }

    //simple output
    fprintf(stderr, "process %d with id %d and parent id %d\n", i, pid, ppid);

    //message passing
    //if master, establish trasnfer
    if (pid == master) {
        //parent
        close(msg[0]);  //closes its read end
        char buffer[MAX];
        fprintf(stderr, "Parent: Waiting for input\n");
        while(1) {
            scanf("%s", buffer);
            if (strcmp(buffer, "exit")==0) {
               break;
            }
            write(msg[1], buffer, MAX);
        }
        close(msg[1]);  //closes it write end
    } else {
        //child
        close(msg[1]);  //closes its write end
        char buffer[MAX];
        fprintf(stderr, "Child: Waiting for pipe\n");
        while(read(msg[0], buffer, MAX) > 0) {
            fprintf(stderr, "Received: %s\n", buffer);
            buffer[0] = '\0';
        }
        close(msg[0]);  //closes its read end        
    }

    //special stuff for master node
    if(master == pid){
        fprintf(stderr, "%s\n", "i am the master");
        //special stuff
    } else {
        fprintf(stderr, "%s\n", "i am a child");
        //nothing really?
    }

    wait(2); //let all processes finish.
    exit(0);
}

講師か TA に聞いてみようと思っていたのですが、2 人とも町を離れ、課題の期限までメールから離れることにしました。これがうまくいかなくても、それで終わりというわけではありませんが、不完全なコーディング課題でコースを開始することは避けたいと思います。

4

4 に答える 4

2

IMHO、コマンドライン引数を参照する前に、コマンドライン引数を確認することをお勧めします(つまり、コードは次のようになります

if (argc != 4) {
    ...
}

int num = atol(argv[1]);

)

私はあなたのコードを明確に理解していません。焦って申し訳ありません。以下は、検証するコード スニペットを含む私のソリューションです。リングを確立するために、最初のメモ プロセスは、IPC を初期化するマスター プロセスとスレーブの 2 つのタイプに分類されます。最初に、2 つのパイプが必要です。1 つはマスターが読み取り用で、もう 1 つはマスターが書き込み用です。初期化後、2 つのプロセスはこのようにパイプで接続されます (親は P、子は C)。

C:+----------+-----------+
  |          |           |
  | Read End | Write End |
  |          |           |
  +----------+-----------+
         |\            \
           \            \
            \            \
             \            \
              \            \
P:+----------+-----------+  \
  |          |           |   \
  | Read End | Write End |    \
  |          |           |     \
  +----------+-----------+      |
       /|                       |
        |                       |
        +-----------------------+

再帰的に、新しいプロセスが分岐したときに、接続を次のように進化させます。

   C2:+----------+-----------+
      |          |           |
      | Read End | Write End |
      |          |           |
      +----------+-----------+
             |\                \
               \                \
                \                \
                 \                \
                  \                \
   C1:+----------+-----------+      |
      |          |           |      |
      | Read End | Write End |      |
      |          |           |      |
      +----------+-----------+      |
             |\                     |
               \                    |
                \                   |
                 \                  |
                  \                 |
    P:+----------+-----------+      |
      |          |           |      |
      | Read End | Write End |      |
      |          |           |      |
      +----------+-----------+      |
           /|                       |
            |                       |
            +-----------------------+

したがって、C1 と C2 の間に別のパイプが必要で、I/O をリダイレクトします。次の ASCII アートはこれを示しています。

                                  C2:+----------+-----------+
                                     |          |           |
                                     | Read End | WriteEnd3 |
                                     |          |           |
                                     +----------+-----------+
                                             |\           \
                                               \           \
                                                \           \
                                                 \           \
   C1:+----------+-----------+       +----------+-----------+ |
      |          |           |       |          |           | |
      | Read End | WriteEnd1 |       | Read End | WriteEnd2 | |
      |          |           |       |          |           | |
      +----------+-----------+       +----------+-----------+ |
             |\            \              /|                  |
               \            \              |                  |
                \            \             +------------------+
                 \            \
                  \            \
    P:+----------+-----------+  \
      |          |           |   \
      | Read End | Write End |    \
      |          |           |     \
      +----------+-----------+      |
           /|                       |
            |                       |
            +-----------------------+

リングを形成するには、WriteEnd2をWriteEnd1に複製する必要があります。これにより、child2 は child1 から読み取ることができます。また、マスターに書き込むには、 WriteEnd1をWriteEnd3の代わりに使用する必要があります。

サンプル コードを含めますが、単純にコピー アンド ペーストするのではなく、宿題を完了するために何が起こっているのかをよりよく理解する必要があります :)。

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

#define BUF_LEN 32

int  input_pipe[2];
int output_pipe[2];

char buf[BUF_LEN];
const char msg[] = "hello world.\n";
int str_len = sizeof(msg);

int status;

int main(int argc, char **argv) {
    /* master input channel */
    int master_input;
    int ring_pid = 0;

    if (argc != 2) {
        fprintf(stdout, "Argument error!\n");
        fflush(stdout);

        exit(-1);
    }

    int total_processes = atoi(argv[1]);

    if (pipe(input_pipe) || pipe(output_pipe)) {
        fprintf(stdout, "Cannot create pipes at master. Program exits.\n");
        fflush(stdout);

        exit(-1);
    } else {
        /* fork child process */
        pid_t pid = fork();
        if (pid) {
            /* dup read end of input pipe and write end of output pipe. */
            int input_fd  = dup(input_pipe[0]);
            int output_fd = dup(output_pipe[1]);

            close(input_pipe[0]);
            close(input_pipe[1]);
            close(output_pipe[0]);
            close(output_pipe[1]);

            /* parent process */
            fprintf(stdout, "Send message to downstream: %s", msg);
            fflush(stdout);
            write(output_fd, msg, str_len + 1);

            read(input_fd, buf, BUF_LEN);
            fprintf(stdout, "Recieve message from upstream: %s", buf);
            fflush(stdout);

            waitpid(pid, &status, 0);
        } else {
            /* child process */
            /* write end of master's input pipe */
            master_input  = dup(input_pipe[1]);

            int input_fd  = dup(output_pipe[0]);
            int output_fd = -1;

            /* increase ring number */
            while (++ring_pid < total_processes) {
                int tmp_pipe[2];

                if (pipe(tmp_pipe)) {
                    fprintf(stdout, "Cannot create pipes at master. Program exits.\n");
                    fflush(stdout);

                    exit(-1);
                } else {
                    /* pid of sub-process's child */
                    pid_t spid = fork();
                    if (spid) {
                        /* drop read end of the pipe, we read from parent process */
                        close(tmp_pipe[0]);
                        /* output of last process directs to master's input */
                        if (ring_pid == total_processes - 1) {
                            output_fd = dup(master_input);
                        } else {
                            output_fd = dup(tmp_pipe[1]);
                        }
                        close(tmp_pipe[1]);
                        /* receive and send message */
                        read(input_fd, buf, BUF_LEN);
                        fprintf(stdout, "Read from upstream: %s", buf);

                        fprintf(stdout, "Write to downstream: %s", buf);
                        write(output_fd, buf, BUF_LEN);

                        waitpid(spid, &status, 0);
                        break;
                    } else {
                        /* for child process, the input is read end of the new pipe. */
                        input_fd = dup(tmp_pipe[0]);
                        close(tmp_pipe[0]);
                    }
                }
            }
        }
    }
}
于 2012-09-24T17:51:37.873 に答える
0

アルゴリズムを変更する必要があります。ここでのロジックは、n 個の子がある場合、n + 1 個のパイプが必要であるということです。

次に、パイプの端を適切なデータ構造に格納する必要があります。私は 2 次元配列を使用しました。各行は 1 つのパイプを意味し、列 0 は特定のパイプの読み取り側であり、列 1 は書き込み側です (この配列を動的にすることを検討してください)。

ここで、すべての子に対して、1 つの読み取り側ともう 1 つの書き込み側の 2 つの端が必要です。ここで、これらの端は 2 つの異なるパイプから来ます。これがロジックです。

つまり、パイプ 1 から読み取り終了を取得し、パイプ 2 から書き込み終了を取得する child1 を考えます。これで、child0 はパイプ 1 から書き込みを終了し、child2 はパイプ 2 から読み取りを終了する必要があります。アイデアが明確であることを願っています。

(もう 1 つの基準は、プロセスが標準入力から読み取って標準出力に書き込むフィルターとして動作する必要があることです。これは、標準入力に読み取りと書き込みのためにそれぞれのパイプ記述子を複製する dup2 関数を使用して行われます。と出力)

概念を理解するのに役立ついくつかのコメントを含むサンプル コードを以下に示します。

(ハードコーディングされた値と非常に少ないエラー処理があります。これを追加する必要があります。)

#define MAXCHILD 50
#define NO_OF_CHILDREN 20
#define MAXBUF 100

void child(int readend, int writeend);
int main()
{
int pipefd[MAXCHILD + 1][2];
int noofchildren = 0;
int nchild = NO_OF_CHILDREN;
int ret;
int i ;
char buf[MAXBUF];
for(i = 0; i <= nchild; i++)
{
    ret = pipe(pipefd[i]);
    if (0 > ret)
    {
        printf("Pipe Creation failed\n");
        exit(1);
    }
}
printf("Pipes created\n");

for(i = 0; i < nchild; i++)
{

    ret = fork();
    if (0 == ret)
    {
        child(pipefd[i][0], pipefd[i + 1][1]);
    }
    //These ends are no longer needed in parent.
    close (pipefd[i][0]);
    close(pipefd[i + 1][1]);
}

printf("%d Children  Created\n", nchild);

while(1) 
{
    printf("Enter Some data\n");
    //Now the parent/master reads from the standard input
    fgets(buf, MAXBUF, stdin);
    //Write to the pipe which is connected to the first child.
    write(pipefd[0][1],buf, strlen(buf) + 1);
    //Read from the pipe, which is attached to the last child created
    read(pipefd[nchild][0], buf, MAXBUF);
    printf("Master Read from the pipe - %s\n", buf);
    if (strcmp(buf, "exit\n") == 0)
    {
        break;
    }
} 
//By this time all other children, would have exited
//Let's wait to see this 
while(1)
{
    ret = wait(NULL);
    if (0 > ret) //Returns -ve when no more child is left.
    {
        break;
    }
    noofchildren++;
}
printf("My %d children terminated! Now I am exiting", noofchildren);
exit(0);
}

void child(int readend, int writeend)
{
char buf[MAXBUF];
int ret;
//set up environment
dup2(readend, 0);
dup2(writeend,1);   
while(1)
{
    ret = read(0, buf, MAXBUF );
    if (strcmp(buf, "exit\n") == 0)
    {
        write(1, buf, ret);
        exit(0);
    }
    write(1, buf, ret);
}
}
于 2012-09-24T20:43:37.600 に答える
0

私が提案できることの 1 つは、プロセスをフォークするときにこのスタイルを使用することです。

child_pid = fork();
if (child_pid==0) {
  childProcess(); // isolate the child process logic
  _exit(0); // make sure the execution doesn't escape the if statement
}

これは、コード内のロジックの問題の一部を確認するのに役立つと思います。

于 2012-09-24T14:40:26.170 に答える
0

scanf標準入力から読み取るを使用して、入力を読み取ろうとしています。ただし、最初にしたことの 1 つは、最初のパイプの読み取り側を複製して元の標準入力を閉じることでした。したがって、パイプから入力を読み取ろうとしています。これを修正するには、マスターが後で読み取るために、最初のパイプの読み取り側を変数に保存する必要がありますが、標準入力で複製しないでください。

また、パイプの読み取りまたは書き込みを試みるときは、 に格納されている記述子を使用してmsgいますが、チェーンの作成中にそれらの記述子を閉じました。パイプを複製した標準入出力記述子を読み書きする必要があります。また、読み取り/書き込みの前にパイプチェーンの未使用の端を閉じようとしますが、チェーン全体の使用を開始すると、使用されなくなります。

たとえば、4 つのプロセスのループは次のようになります。

 input(stdin)
   v
+> A -+
|     v
D     B
^     |
+- C <+

すべてのプロセスを作成した後、プロセス A (マスター) に戻るチェーンを除いて、各接続は標準入力または標準出力のいずれかになります。プロセス A は標準入力から読み取り、プロセス B の標準入力に接続されている標準出力に書き込みます。次に、プロセス B は標準入力を読み取り、標準出力に書き込み、メッセージをプロセス C に渡します。プロセス C と D は同じことを行い、メッセージをループ全体に渡して、読み取りポートでプロセス A に戻します。オリジナルパイプの。各プロセスは読み取りと書き込みの両方を担当するため、プロセスが完全に終了するまでパイプを閉じないでください。

于 2012-09-24T14:41:29.587 に答える