torutkのブログ

ソフトウェア・エンジニアのブログ

バックグラウンドで実行するプロセスの標準入力は・・・

標準入力からキー入力を受けるプログラムを、シェルスクリプト等ターミナル以外からバックグラウンド実行すると、EOFを読み続ける状態になるようです。

ちょっとしたキー入力処理を付けた以下のようなプログラムを用意しました。

int main(int argc, char* argv[]) {
    spawnTask();                  // スレッドを生成しタスク実行
    while (true) {
        std::string entered;
        std::cout >> "command> ";
        std::cin >> entered;      // (1)
        if (entered == "dump") {
            dumpStatus();
        }
    }
}

通常は、(1)でキー入力があるまでブロックします。ところが、スクリプト等からバックグラウンド実行すると、標準入力が閉じられるためか(?)、(1)ではブロックせずに、enteredはから文字列(empty() == true)となってwhile文がまわり続けます。

標準入力をFIFOに結びつける

FIFOに結びつけるとうまくいくかと思い実験しました。

keyinprocess < /tmp/keyin &

上記のスクリプトを実行します。なお、あらかじめmkfifoで/tmp/keyinを作成しておきます。

最初は、echoコマンドでFIFOに文字列を出力してみました。

$ echo dump > /tmp/keyin
$

"dump"がstd::cinから読み込み処理が起動しますが、その後はEOF状態となってstd::cinがブロックせずループ状態となります。
FIFOは書き込み側がクローズすると、読み出し側はEOFとなるので、echoコマンドが1回終了するとクローズとなるからのようです。

次は、コマンドが終了しなければいいと考えて、

$ cat > /tmp/keyin
dump
hello
dump

と実行すると、クローズしてEOFになることはなく、キー入力処理が想定どおりに回ります。

ただ、このcatを終了させると、やはりEOFで無限ループ状態になります。

FIFOは複数プロセスが書き込みできるので、上記のFIFOに複数書き込みを行うプロセスを動かしておくことで、回避できます。

とはいえ、キー入力のループにエラー処理を追加した方がよいですね。

        std::cin >> entered;
        if (entered.empty()) {
            break;
        } else if (entered == "dump") {
            ...