4

独自のシェルを作成しようとしています。フォークは正しく行われていると思いますが、正しくパイプする方法がわかりません。ヘルプやヒントをいただければ幸いです。

基本的に私のパイプは機能しておらず、プロセス間でデータを適切に送信する方法を見つけようと何年も費やしてきました。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h" // from Steven's book Advanced programing in the UNIX Enviroment


extern int makeargv(char *, char * , char ***);
int main()  
{   char **argp;
    int i,j,vpret;
    char buf[80];
    pid_t pid;
    int pnum;   
        //pipe number
    int ploc[16];   
        //pipe location 
        //(this has a max of 12 possible pipes)
    int targ;
    int fdleft[2], fdright[2];




    printf("            <(^_^)> \nHello \n I am your console and I am here to help you \n");
    printf("    If you dont need me anymore just say \"bye\" ");
    fflush(stdout);
    write(1,"\n(>^_^)> ",8);
    while(strcmp(fgets(buf, 80, stdin), "bye\n")!=0)
    {        
        j=makeargv(buf," \n",&argp); //this breaks the line up and returns the number of commands 
        pnum = 0;
        ploc[0] = 0;
        if (j > 16) j = 16;
        for (i=0;i<j;i++)
        {
            if ( strcmp(argp[i], "|") == 0)
            {
                argp[i]= NULL;
                ploc[pnum+1] = (i+1);
                pnum++;
            }

        } 

        for (i = 0; i < (pnum+1); i++) 
        {   
            pipe(fdright);
            if (i != 0)
            {   
                dup2(fdright[1], fdleft[0]);            
            }
            pid = fork();


            switch (pid)
            {
                case -1:
                    err_sys("fork failed");
                    break;
                case 0: // child
                    if (i != pnum)
                    {
                        dup2(fdright[1],1);
                    }
                    if ( i != 0);
                    {
                        dup2(fdright[0],0); 
                    }
                    //printf("(^o^) running pipe[%i]\n" , i);
                    targ =(ploc[i]) ;
                    execvp(argp[targ],&argp[targ]);
                    write(1,"(-_-) I'm sorry the exec failed \n",33);
                    exit(1);
                default:

                    dup2(fdleft[1], fdright[1]);
                    waitpid(pid,NULL, 0);
                    close(fdright[0]);
                    close(fdright[1]);
                    //waitpid(pid,NULL, 0);
            }
        }   
        //waitpid(pid, NULL, 0);
        write(1,"\n(>^_^)> ",8);
    }
    printf("  v(^o^)^ BYE BYE!\n");

}

ありがとう

4

1 に答える 1

3

さまざまなコメント:

  1. while (strcmp(fgets(buf, 80, stdin), "bye\n")!=0)

    シェルに の代わりに EOF が与えられると、これはクラッシュしbyeます。そのように 2 つの関数呼び出しを組み合わせないでください。すべてを 1 つのループ条件にしたい場合は、次を使用します。

    while (fgets(buf, sizeof(buf), stdin) != 0 && strcmp(buf, "bye\n") != 0)
    

    コマンド ラインの長さの制限については、別の機会に説明します。

  2. あなたはmakeargv()見るために提供していないので、それがうまく機能すると仮定する必要があります。

  3. コマンドとパイプに分割するループは次のとおりです。

    pnum = 0;
    ploc[0] = 0;
    if (j > 16) j = 16;
    for (i = 0; i < j; i++)
    {
        if (strcmp(argp[i], "|") == 0)
        {
            argp[i] = NULL;
            ploc[pnum+1] = (i+1);
            pnum++;
        }
    }
    

    コマンドライン入力があるとしましょう: ls -l | grep lemon. makeargv()5 を返しargp、次のように設定するようです。

    argp[0] = "ls";
    argp[1] = "-l";
    argp[2] = "|";
    argp[3] = "grep";
    argp[4] = "lemon";
    argp[5] = 0;         // Inferred - things will crash sooner or later if wrong
    

    あなたのこのループはあなたに与えるでしょう:

    ploc[0] = 0;
    ploc[1] = 3;
    pnum = 1;
    
  4. コードのfdleft配列にはファイル記述子のペアがありますが、 の呼び出しで配列を使用しているにもかかわらず、配列を初期化していません (pipe()たとえば、 を呼び出します) dup2()

  5. メインforループは、コマンドごとに 1 回ずつ、合計 2 回実行する必要があります。パイプライン内の 3 つ以上のコマンドの一般的なケース (who | grep me | sortたとえば ) では、最初のコマンド ( who) は標準入力を変更しない必要がありますが、その標準出力はwhoとを接続するパイプに送られgrepます。「中間」コマンド (最後から 2 番目のコマンド、またはgrep me例では) は、それぞれ標準入力が前のパイプから取得される必要があり、その標準出力が移動する新しいパイプを作成する必要があります。最後のコマンド (この例では 3 番目のコマンドsort) は、標準入力が最後のパイプから取得され、標準出力が変更されていない必要があります。

    あなたのコードはそれをしないか、どこかに近づきません。

  6. pipe()and then dup()orを使用していずれかの記述子を標準 I/O 記述子にマップする場合は、パイプの両端dup2()閉じる必要があります。十分な通話がありません。close()

  7. 親プロセスは、子プロセスのそれぞれを順番に起動する必要があり、すべての子プロセスを起動した後、子プロセスが終了するのを待ちます。プロセスを編成するにはさまざまな方法があります。親は 1 回フォークできます。子は、パイプラインの主要なコマンドを起動し、最終的に最後のコマンド自体を実行する責任を負う可能性があります。親には直接の子が 1 つしかない (他は孫) ため、1 つのコマンドが終了するのを待つだけで済みます。別の方法は、親がパイプライン内の各プロセスを認識しており、それらがすべて終了するのを待つことです。

  8. 親プロセスがパイプライン内の各コマンドの完了を待ってから残りのコマンドを起動すると、最終的にデッドロックが発生する可能性があります。1 つの子がそのパイプに大量のデータを書き込むため、何らかのプロセスがパイプから読み取るまでカーネルによってブロックされますが、パイプから読み取るプロセスはまだ起動されておらず、親は子が終了するのを待っています。また、マルチプロセッシングと同時実行の利点も失われます。

  9. あなたの「ケース0」には、余分なセミコロンがあります。私のコンパイラはそれについて私に警告しました。警告が表示されなかった場合は、コンパイル警告をさらに使用するか、より優れたコンパイラを入手する必要があります。

    case 0: // child
        if (i != pnum)
        {
            dup2(fdright[1], 1);
        }
        if (i != 0);     // Unwanted semi-colon!
        {
            dup2(fdright[0], 0); 
        }
    
  10. SO のミニシェルのパイプラインについては、次のような多くの質問があります。

    13636252 の答えは、ほぼ一般的です。唯一の難点はchar ***、混乱しがちな使用法であり、非常に厳密に記述されているため、最小限の繰り返しで相互に再帰的な機能を備えています。OTOH、正しく機能し、makeargv()関数もchar ***引数を使用します。


作り直されたコード

これが機能するように作り直されたコードです。err_sys()との実装が含まれますmakeargv()。私makeargv()は、コマンド ラインの単語数が 32 語未満であると単純に想定しています。どう考えても、これは堅牢なコマンド ライン パーサーではありません。入力するls | wcと正しい答えが得られます。who | grep me | sortまた、正しい答えを許可して提供します。lsまた、正しい答えを許可して提供します。ただし、パイプ記号の周りのスペースは重要です (通常のシェルではオプションなので、これも機能するwho|grep me|sortはずですが、このコードでは機能しません。

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

extern int  makeargv(char *line, char *seps, char ***args);
extern void err_sys(const char *msg);
static void dump_argv(char **argv);
static void dump_fds(void);

int main(void)
{
    char buf[80];

    printf("            <(^_^)> \nHello \n I am your console and I am here to help you\n");
    printf("    If you don't need me anymore just say \"bye\"\n");
    fflush(stdout);
    dump_fds();
    printf("(>^_^)> ");
    while (fgets(buf, sizeof(buf), stdin) != 0 && strcmp(buf, "bye\n") != 0)
    {
        pid_t pid;
        char **argp;
        int fdleft[2]  = { -1, -1 };
        int fdright[2] = { -1, -1 };
        int pnum = 0;
        int ploc[16];
        int j = makeargv(buf, " \n", &argp);

        ploc[0] = 0;
        if (j > 16)
            j = 16;
        for (int i = 0; i < j; i++)
        {
            if (strcmp(argp[i], "|") == 0)
            {
                argp[i] = NULL;
                ploc[++pnum] = i+1;
            }
        }

        printf("pnum = %d\n", pnum);
        for (int k = 0; k < pnum+1; k++)
            printf("ploc[%d] = %d\n", k, ploc[k]);

        for (int i = 0; i < pnum+1; i++)
        {
            if (i != pnum)
            {
                if (pnum > 0)
                {
                    if (pipe(fdright) != 0)
                        err_sys("pipe");
                    //printf("%d: fdright = { %d, %d }\n", i, fdright[0], fdright[1]);
                    //dump_fds();
                }
            }

            if ((pid = fork()) < 0)
                err_sys("fork failed");
            else if (pid == 0)
            {
                /* Child */
                int targ;
                //dump_fds();
                if (i != pnum)
                {
                    dup2(fdright[1], 1);
                    close(fdright[0]);
                    close(fdright[1]);
                }
                if (i != 0)
                {
                    dup2(fdleft[0], 0);
                    close(fdleft[0]);
                    close(fdleft[1]);
                }
                targ = ploc[i];
                dump_argv(&argp[targ]);
                dump_fds();
                execvp(argp[targ], &argp[targ]);
                fprintf(stderr, "(-_-) I'm sorry the exec failed\n");
                exit(1);
            }

            if (i != 0)
            {
                //dump_fds();
                //printf("%d: fdleft = { %d, %d }\n", i, fdleft[0], fdleft[1]);
                assert(fdleft[0] != -1 && fdleft[1] != -1);
                close(fdleft[0]);
                close(fdleft[1]);
                //dump_fds();
            }

            printf("PID %d launched\n", pid);
            fdleft[0] = fdright[0];
            fdleft[1] = fdright[1];
        }

        //dump_fds();
        //printf("%d: fdleft = { %d, %d }\n", -1, fdleft[0], fdleft[1]);
        close(fdleft[0]);
        close(fdleft[1]);
        free(argp);
        //dump_fds();

        int corpse;
        int status;
        while ((corpse = waitpid(0, &status, 0)) > 0)
            printf(":-( PID %d status 0x%.4X\n", corpse, status);

        printf("\n(>^_^)> ");
    }
    printf("  v(^o^)^ BYE BYE!\n");
}

static void dump_argv(char **argv)
{
    int n = 0;
    char **args;
    args = argv;
    while (*args++ != 0)
        n++;
    fprintf(stderr, "%d: %d args\n", getpid(), n);
    args = argv;
    while (*args != 0)
        fprintf(stderr, "[%s]\n", *args++);
    fprintf(stderr, "EOA\n");
}

/* Report on open file descriptors (0..19) in process */
static void dump_fds(void)
{
    struct stat b;
    char buffer[32];
    sprintf(buffer, "%d: ", getpid());
    char *str = buffer + strlen(buffer);
    for (int i = 0; i < 20; i++)
        *str++ = (fstat(i, &b) == 0) ? 'o' : '-';
    *str++ = '\n';
    *str = '\0';
    fputs(buffer, stderr);
}

int makeargv(char *line, char *seps, char ***args)
{
    enum { MAX_ARGS = 32 };
    char **argv = malloc(32 * sizeof(char *));  // Lazy!
    if (argv == 0)
        err_sys("out of memory in makeargv()");
    int n;
    char **argp = argv;
    char  *str = line;
    for (n = 0; n < MAX_ARGS - 1; n++)
    {
        str += strspn(str, seps);
        if (*str == '\0')
            break;
        *argp++ = str;
        int len = strcspn(str, seps);
        if (len == 0)
            break;
        str[len] = '\0';
        str += len + 1;
    }
    *argp = 0;
    dump_argv(argv);
    *args = argv;
    return(n);
}

void err_sys(const char *msg)
{
    int errnum = errno;
    char *errmsg = strerror(errnum);
    fprintf(stderr, "%s (%d: %s)\n", msg, errnum, errmsg);
    exit(1);
}

出力例:

$ ./pipes-15673333
            <(^_^)> 
Hello 
 I am your console and I am here to help you
    If you don't need me anymore just say "bye"
29191: ooo-----------------
(>^_^)> who | grep jl | sort
29191: 6 args
[who]
[|]
[grep]
[jl]
[|]
[sort]
EOA
pnum = 2
ploc[0] = 0
ploc[1] = 2
ploc[2] = 5
PID 29194 launched
PID 29195 launched
29194: 1 args
[who]
EOA
PID 29196 launched
29194: ooo-----------------
29195: 2 args
[grep]
[jl]
EOA
29195: ooo-----------------
29196: 1 args
[sort]
EOA
29196: ooo-----------------
:-( PID 29194 status 0x0000
jleffler console  Mar 27 15:11 
jleffler ttys000  Mar 27 16:26 
jleffler ttys001  Mar 27 16:26 
jleffler ttys002  Mar 27 16:26 
jleffler ttys003  Mar 27 16:26 
jleffler ttys004  Mar 27 16:26 
jleffler ttys005  Mar 27 16:26 
:-( PID 29195 status 0x0000
:-( PID 29196 status 0x0000

(>^_^)> ls
29191: 1 args
[ls]
EOA
pnum = 0
ploc[0] = 0
PID 29197 launched
29197: 1 args
[ls]
EOA
29197: ooo-----------------
bash.getopts.update makefile            pipeline.c          pthread-1.c         shuntzeroes.c       timezeromoves.c
cmpfltint.c         mda.c               pipeline.dSYM       pthread-2.c         so.14304827         uint128.c
const-stuff.c       mq-saurabh          pipes-13905948.c    pthread-3.c         so.367309           uname.c
dupdata.sql         mqp-saurabh         pipes-14312939.c    quine.c             so.6964747          unwrap.c
fifocircle.c        multi-pipe-sort.c   pipes-15673333      ranges.sql          so.6965001          xxx.sql
idsdb00246324.ec    multiopts.sh        pipes-15673333.c    recv.c              so.8854855.sql      yyy.sql
incunabulum.c       nextpipe.c          pipes-15673333.dSYM regress.c           strandsort.c
madump.c            pipeline            powa.c              send.c              streplace.c
:-( PID 29197 status 0x0000

(>^_^)> ls -C | wc
29191: 4 args
[ls]
[-C]
[|]
[wc]
EOA
pnum = 1
ploc[0] = 0
ploc[1] = 3
PID 29200 launched
PID 29201 launched
29200: 2 args
29201: 1 args
[ls]
[wc]
[-C]
EOA
EOA
29201: ooo-----------------
29200: ooo-----------------
:-( PID 29200 status 0x0000
      16      46     581
:-( PID 29201 status 0x0000

(>^_^)> bye
  v(^o^)^ BYE BYE!
$
于 2013-03-28T16:25:23.297 に答える