鼻悪魔

鼻悪魔

ファイル入力があります。

$ cat input
1echo 12345

次のプログラムがあります

初版

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

int main() {
  system("/bin/bash -i");
  return 0;
}

今これを実行すると

$ gcc -o program program.c
$ ./program < input
bash: line 1: 1echo: command not found
$ exit

すべてが期待どおりに動作します。

getchar()これで、ファイル入力の最初の文字を無視したいのでsystem()

2番目のバージョン:

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

int main() {
  getchar();
  system("/bin/bash -i");
  return 0;
}

驚くべきことに、bashは入力がないかのようにすぐに終了します。

$ gcc -o program program.c
$ ./program < input
$ exit

質問Bashが入力を受け取らないのはなぜですか?

ノート いくつか試してみたところ、基本プロセスで新しいサブプロセスをフォークすると問題が解決することがわかりました。

3番目のバージョン

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

int main() {
  getchar();
  if (fork() > 0) {
    system("/bin/bash -i");
    wait(NULL);
  }
  return 0;
}

$ gcc -o program program.c
$ ./program < input
$ 12345
$ exit

オペレーティングシステムUbuntu 16.04 64ビット、gcc 5.4

ベストアンサー1

ファイルストリーム次のように定義されます。:

参照された対話型デバイスがないことが確認できる場合にのみ完全にバッファリングされます。

標準入力にリダイレクトするので、stdinは非対話型なのでバッファリングされます。

getcharバッファをストリームから埋め、そのバイトを消費してからバイトを返すストリーム関数。systemただfork-execを実行してくださいそのため、子プロセスは開いているすべてのファイル記述子をそのまま継承します。標準入力から読み取ろうとすると、親bashプロセスですべての内容を読み取ったため、すでにファイルの末尾にあることがわかります。


あなたの場合、その単一バイトを子プロセスに渡す前にのみ消費しようとしているので、次のようになります。

このsetvbuf()関数は、ストリームが指すストリームが開かれたファイルに関連付けられた後、ストリームで他の操作が実行される前に使用できます。

したがって、以前に適切な呼び出しを追加してくださいgetchar()

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

int main() {
  setvbuf(stdin, NULL, _IONBF, 0 );
  getchar();
  system("/bin/bash -i");
  return 0;
}

stdinバッファなし()に設定して目的の_IONBF操作を実行します。getchar単一バイトのみが読み取られ、残りの入力は子プロセスで使用できます。使用する方が良いかもしれませんread代わりに、この場合はフルストリームインターフェイスを使用しないでください。


POSIXは、フォーク後の両方のプロセスがハンドルにアクセスできるときに特定の動作を強制します。が、明らかに明示されている

いずれかのプロセスによって実行された唯一の操作が次のいずれかである場合exec関数 [...]、このプロセスではハンドルにアクセスできません。

これはsystem()特別な措置を講じる必要がないことを意味します。なぜならこれはただフォーク実行(fork-exec)です。

回避策に問題がある可能性がありますfork。ハンドルが両側からアクセスできる場合、では初めて:

読み取りを許可するモードでストリームが開き、デフォルトの開かれたファイル記述が見つかるデバイスを参照する場合、アプリケーションは以下を実行する必要があります。fflush()、そうでなければストリームは閉じます。

呼ぶfflush()読み取りストリームでは、次のことを意味します。

デフォルトのオープンファイル記述のファイルオフセットは、ストリーム内のファイルの場所に設定する必要があります。

したがって、記述子の位置はストリームと同じ位置の1バイトにリセットされなければならず、後続の子プロセスはその時点で始まる標準入力を受け取ります。

また、2番目(子)ハンドルの場合:

上記の最初のハンドルの要件に加えて、ファイルオフセットを明示的に変更する関数で以前にアクティブなハンドルを使用した場合、アプリケーションは次のことを行う必要があります。lseek()またはfseek()(ハンドルの種類に応じて)適切な位置に移動します。

私は「適切な場所」がおそらく同じであると思います(もう指定されていませんが)。getchar()「ファイルオフセットを明示的に変更しました」という呼び出しがあるため、この場合に適用する必要があります。この詩の目的は、両方のフォークで作業することが同じ効果を持つ必要があるため、および両方ともfork() > 0同じfork() == 0ように機能する必要があることです。しかし、その四半期では実際には何も起こらないので、これらの規則を親や子にまったく使用してはならないと言うのが妥当です。

正確な結果はおそらくプラットフォームによって異なります。少なくとも「アクセス可能」と見なされる項目、または第1のハンドルと第2のハンドルに対する直接的な指定はない。親プロセスの以前の最も重要なケースがあります。

この開かれたファイル記述子に対するハンドルに対して実行される唯一の追加操作がハンドルを閉じることであれば、操作は必要ありません。

これはプログラムが終了するため、おそらくプログラムに適用されます。そうであれば、を含む残りのすべてのケースをfflush()スキップする必要があり、表示される動作は仕様から外れます。確かに、呼び出しはfork()ハンドルに対してアクションを実行すると見なされますが、明確でも明確でもないので、そうは信じません。要件にも「どちらか」と「または」が十分なので、多くのバリエーションが許可されているようです。

多くの理由により私の考えにあなたが見ている行動はバグかもしれません。、または少なくとも規範の寛大な解釈。私の全体的な解釈は、各場合に1つのブランチが何もしないため、これらのfork規則を適用しないでください。記述子の場所は無視する必要があります。私はこれについてはわかりませんが、最も簡単な読書のようです。


私はfork働くために技術に頼らないでしょう。あなたの3番目のバージョンはここでは動作しません。使用setbuf/setvbuf代わりに。可能であれば私も使用しますpopenあるいは、ストリームとファイル記述子の相互作用のあいまいさに依存するのではなく、必要なフィルタリングを使用してプロセスを明示的に設定することと同じです。

おすすめ記事