1

私のプログラムは次のとおりです。

//... init fd[2] as pipe ...
if (child==0){
        close(fd[1]);
        dup2(fd[0], 0);
        execlp("/bin/sh","sh",NULL);
} else {
        close(fd[0]);
        char *line; int nbytes=100; int bytes=0;
        line=(char*) malloc(nbytes+1);
        while ( (bytes = getline((char **)&line,&nbytes,stdin))!= -1 ){
            write(fd[1],line, bytes);
        }
}

これは正常に実行されますが、インタラクティブシェルを強制するために置き換えようとexec("/bin/sh","sh",NULL)するとexec("/bin/sh","sh","-i",NULL)、最初のコマンドを実行した後にプログラムが停止します。

私はパイプを初めて使用するので、理由を理解し、インタラクティブなシェルを機能させるのを手伝ってください...行を読み取って子パイプに渡すコードが少し奇妙だと感じています..それを達成するためのより良い方法はありますか同じ動作?

4

1 に答える 1

1

あなたは子供のclose(fd[0]);後にすべきです。dup2()のように絶対パスまたは相対パスを指定すると、 ;"/bin/sh"を使用しても意味がありません。execlp()裸のファイル名 (プログラム名) の PATH ベースの検索のみを行います。への呼び出しでのキャストはgetline()不要です。このようなキャストは可能な限り避けてください。失敗した場合に備えて、少なくともexit(1);afterを含める必要があります。execlp()診断メッセージも良い考えです。close(fd[1]);子に EOF を示すには、親のループの後にする必要があります。(一度だけ、 からのエラー戻りを検出しなくても問題ありませんmalloc()。ポインタが NULL を保持しているポインタのアドレスをgetline()機能し、メモリ自体を割り当てようとします。もちろん、メイン プログラムがメモリの割り当てに失敗した場合、メイン プログラムgetline()もメモリの割り当てに失敗する可能性が高くなります。)

これらの変化は次のことにつながります。

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

int main(void)
{
    int fd[2];
    pid_t child;

    if (pipe(fd) != 0)
        perror("pipe");
    else if ((child = fork()) < 0)
        perror("fork");
    else if (child == 0)
    {
        close(fd[1]);
        dup2(fd[0], 0);
        close(fd[0]);
        execl("/bin/sh", "sh", NULL);
        perror("oops");
        exit(1);
    }
    else
    {
        close(fd[0]);
        size_t nbytes = 100;
        int bytes = 0;
        char *line = (char*)malloc(nbytes+1);
        while ((bytes = getline(&line, &nbytes, stdin)) != -1)
        {
            write(fd[1], line, bytes);
        }
        close(fd[1]);
    }
    return(0);
}

これは、厳しいコンパイル フラグの下で問題なくコンパイルされます。

gcc -O3 -g -std=c99 -Wall -Wextra xf.c -o xf

(Mac OS X 10.7.3 上で) 上記のコード (オプションshなしで起動) を実行すると、問題なく正常に動作します。-iコマンドを入力すると、シェルがそれらを実行します。「exit」と入力するとシェルは終了しますが、あなたが作成したプログラム (私が呼び出したものxf) は、新しいコマンドを入力するまで終了しません。次に、現在リーダーのないパイプに書き込むときに、SIGPIPE シグナルのために終了します。標準入力が端末ではない (パイプである) ため、このシェルからのプロンプトはありません。

オプションを指定してサブシェルを実行すると、-iどのシェルが端末を担当するかについて、ジョブ制御シェル間で争いが発生するようです。実行すると、次のようになります。

$ ps -f
  UID   PID  PPID   C STIME   TTY           TIME CMD
  503   381   372   0 Wed08PM ttys001    0:00.07 -sh
  503 21908   381   0  9:32PM ttys001    0:00.01 sh
$ ./xf
sh-3.2$ 

[1]+  Stopped(SIGTTIN)        ./xf
$ 
$ ps -f
  UID   PID  PPID   C STIME   TTY           TIME CMD
  503   381   372   0 Wed08PM ttys001    0:00.07 -sh
  503 21908   381   0  9:32PM ttys001    0:00.01 sh
  503 22000 21908   0  9:36PM ttys001    0:00.00 ./xf
  503 22001 22000   0  9:36PM ttys001    0:00.00 sh -i
$ ls
awk.data           osfile-keep.c      pthread-2.c        send.c             xf
const-stuff.c      perl.data          pthread-3.c        so.8854855.sql     xf.c
fifocircle.c       piped-merge-sort.c quine.c            strandsort.c       xf.dSYM
madump.c           powa.c             recv.c             unwrap.c           xxx.sql
makefile           pthread-1.c        regress.c          vap.c              yyy.sql
$ jobs
[1]+  Stopped(SIGTTIN)        ./xf
$ fg %1
./xf
exit
$

(イニシャル-shはターミナル ウィンドウのログイン シェルです。その中で、実行してサブシェルを取得し、プロンプトを区別するようにsh設定しました。)PS1='$ '

AFAICT、sh-3.2$プロンプトはsh -iシェルから来ます。親シェルは入力を読み取っているようで、xfプログラムをバックグラウンドにダンプしましたが、これはあまり文明化されていません。出力にはコマンドが表示されないため、面倒ですps -f。1回の実行でコマンドをリストに表示するpsことができましたが、それは元のシェルの子であり、実行によるものではありませんでした。フォアグラウンドに移動すると、すぐに終了します (おそらく、EOF を示す標準入力から 0 バイトを読み取るため、-1 が返され、すべてがシャットダウンします。シェルが許可する代わりにコマンドを受け取ったためlspssh -ixfxfgetline()exitsh -ishxf端末を制御できます。それはかなり厄介です。どうしてこうなったのかはわかりませんが、あってはならないことだと思います。

于 2012-04-27T04:44:56.110 に答える