web-dev-qa-db-ja.com

Bash:無限のスリープ(無限のブロッキング)

startxを使用してXを起動し、.xinitrcを評価します。 .xinitrcで、/usr/bin/mywmを使用してウィンドウマネージャーを起動します。さて、WMを(他のWMをテストするために)強制終了すると、.xinitrcスクリプトがEOFに達したため、Xも終了します。 .xinitrcの最後にこれを追加しました:

while true; do sleep 10000; done

このように、WMを殺してもXは終了しません。ここで私の質問:スリープをループする代わりに無限スリープを実行するにはどうすればよいですか?スクリプトをフリーズするようなコマンドがありますか?

宜しくお願いします

127
watain

sleep infinityは、猫が虐待することなく、提案したとおりに機能します。

263
Donarsson

これはthisいように思えるかもしれませんが、なぜcatを実行して入力を永遠に待たせないのでしょうか?

67
Michał Trybus

tailはブロックしません

いつものように、すべてに答えは短く、理解しやすく、従いやすく、完全に間違っています。ここで_tail -f /dev/null_はこのカテゴリに分類されます;)

_strace tail -f /dev/null_で見ると、この解決策はブロッキングにはほど遠いことに気づくでしょう! (_Linux)sleepシステムのような貴重なリソースを使用するため、問題のinotifyソリューションよりもさらにひどいでしょう。 _/dev/null_に書き込む他のプロセスもtailループを作成します。 (私のUbuntu64 16.10では、すでにビジーなシステムで毎秒数十のシステムコールが追加されます。)

質問はブロッキングコマンドに対するものでした

残念ながら、そのようなことはありません..

読む:これをシェルで直接アーカイブする方法がわかりません。

すべて(_sleep infinity_さえ)は何らかのシグナルによって中断される可能性があります。したがって、例外的に返されないことを本当に確認したい場合は、sleepで既に行ったように、ループで実行する必要があります。 (Linuxの場合)_/bin/sleep_は24日に制限されていることに注意してください(_strace sleep infinity_を見てください)。

_while :; do sleep 2073600; done
_

sleepは24日よりも高い値で内部的にループすると信じていますが、これは意味します:ブロッキングではなく、非常に遅いループです。このループを外側に移動してみませんか?)

..しかし、あなたは名前のないfifoでかなり近づくことができます

プロセスに送信されるシグナルがない限り、本当にブロックするものを作成できます。以下は_bash 4_、2つのPID、1つのfifoを使用しています。

_bash -c 'coproc { exec >&-; read; }; eval exec "${COPROC[0]}<&-"; wait'
_

必要に応じて、straceでこれが本当にブロックされることを確認できます。

_strace -ff bash -c '..see above..'
_

これがどのように構築されたか

readは、入力データがない場合にブロックします(他の回答を参照)。ただし、tty(別名。stdin)は、ユーザーがログアウトすると閉じられるため、通常は適切なソースではありません。また、ttyから入力を盗む可能性があります。よくない。

readをブロックするには、何も返さないfifoのようなものを待つ必要があります。 _bash 4_には、fifocoprocを正確に提供できるコマンドがあります。ブロッキングread(これはcoprocです)も待機する場合、完了です。悲しいことに、これは2つのPIDとfifoを開いたままにする必要があります。

名前付きfifoを持つバリアント

名前付きfifoを使用しない場合は、次のようにできます。

_mkfifo "$HOME/.pause.fifo" 2>/dev/null; read <"$HOME/.pause.fifo"
_

読み取りでループを使用しないことは少しずさんですが、このfifoを好きなだけ再利用し、readsを_touch "$HOME/.pause.fifo"_を使用して終端することができます単一の読み取り待機、すべてが一度に終了します)。

または、Linux pause() syscallを使用します

無限ブロッキングには、pause()と呼ばれるLinuxカーネル呼び出しがあります。これは、必要な処理を行います:永遠に待機します(シグナルが到着するまで)。ただし、このためのユーザー空間プログラムはまだありません。

C

このようなプログラムの作成は簡単です。 pauseと呼ばれる非常に小さなLinuxプログラムを作成するためのスニペットは、無期限に一時停止します(dietgccなどが必要です)。

_printf '#include <unistd.h>\nint main(){for(;;)pause();}' > pause.c;
diet -Os cc pause.c -o pause;
strip -s pause;
ls -al pause
_

python

自分で何かをコンパイルしたくないが、pythonがインストールされている場合、Linuxでこれを使用できます。

_python -c 'while 1: import ctypes; ctypes.CDLL(None).pause()'
_

(注:現在のシェルを置き換えるには_exec python -c ..._を使用します。これによりPIDが1つ解放されます。ソリューションはIOリダイレクトも使用して改善できます。 )

これがどのように機能するか(私は思う):ctypes.CDLL(None)は標準Cライブラリをロードし、追加のループ内でpause()関数を実行します。 Cバージョンよりも効率的ではありませんが、動作します。

あなたへの私の推薦:

ループスリープ状態を維持します。理解しやすく、移植性が高く、ほとんどの場合ブロックされます。

57
Tino

TL; DR:_sleep infinity_は、実際には許容される最大時間(有限です)スリープします。

なぜこれがどこにも文書化されていないのか疑問に思ったので、 sources from GNU coreutils を読むことに悩まされました。

  1. 最初の引数でC stdlibの strtod を使用して、 'infinity'を倍精度に変換します。したがって、IEEE 754の倍精度を仮定すると、64ビットの正の無限大値がseconds変数に格納されます。
  2. xnanosleep(seconds)を呼び出します( gnulibにあります )、これはdtotimespec(seconds)gnulibにもあります )を呼び出してdoubleを_struct timespec_に。
  3. _struct timespec_は、整数部分(秒単位)と小数部分(ナノ秒単位)の単なる数字のペアです。単純に正の無限大を整数に変換すると、未定義の動作になり(C標準の6.3.1.4を参照)、代わりにTYPE_MAXIMUM (time_t)に切り捨てられます。
  4. TYPE_MAXIMUM (time_t)の実際の値は標準では設定されていません(sizeof(time_t)でさえ設定されていません)。例として、最近のLinuxカーネルからx86-64を選択してみましょう。

これは、Linuxカーネルでは_TIME_T_MAX_であり、次のように定義されています( _time.h_

_(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
_

_time_t_は___kernel_time_t_であり、_time_t_はlong;です。 LP64データモデルが使用されるため、sizeof(long)は8(64ビット)です。

結果:_TIME_T_MAX = 9223372036854775807_。

つまり、_sleep infinite_は、9223372036854775807秒(10 ^ 11年)の実際のスリープ時間になります。また、32ビットLinuxシステムの場合(sizeof(long)は4(32ビット)):2147483647秒(68年。 2038年問題 も参照)。


Edit:明らかに、呼び出されるnanoseconds関数は直接syscallではなく、OS依存のラッパーです(また gnulib )。

結果として余分なステップがあります:_HAVE_BUG_BIG_NANOSLEEP_がtrueであるシステムでは、スリープは24日に切り捨てられ、ループで呼び出されます。これは、一部の(またはすべての)Linuxディストリビューションの場合です。 configure-timeテストが成功した場合(- source )、このラッパーは使用できないことに注意してください。

特に、それは_24 * 24 * 60 * 60 = 2073600 seconds_(プラス999999999ナノ秒)になります。ただし、これは、指定された合計スリープ時間を尊重するためにループで呼び出されます。したがって、以前の結論は引き続き有効です。


結論として、結果の睡眠時間は無限ではありませんが、実際の時間経過が移植可能でない場合でも、すべての実用的な目的に十分な長さです。それはOSとアーキテクチャに依存します。

元の質問に答えるために、これは明らかに十分ですが、何らかの理由で(非常にリソースに制約のあるシステム)、無駄な余分なカウントダウンタイマーを避けたい場合は、最も正しい代替方法は、他の回答で説明されているcatメソッドを使用することです。

21
jp48

sleep infinityは最もエレガントに見えますが、何らかの理由で機能しない場合があります。その場合は、catreadtail -f /dev/nullgrep aなどの他のブロックコマンドを試すことができます。

8
Hui Zheng

[〜#〜] sigstop [〜#〜] を自分自身に送信するのはどうですか?

これにより、SIGCONTが受信されるまでプロセスが一時停止します。それはあなたの場合です:決して。

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
5
michuelnik

私は最近これを行う必要がありました。外部プログラムを呼び出さずにbashを永久にスリープ状態にできる次の関数を思い付きました。

snore()
{
    local IFS
    [[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null ||
    {
        # workaround for MacOS and similar systems
        local fifo
        fifo=$(mktemp -u)
        mkfifo -m 700 "$fifo"
        exec {_snore_fd}<>"$fifo"
        rm "$fifo"
    }
    read ${1:+-t "$1"} -u $_snore_fd || :
}

注:以前に毎回ファイル記述子を開いたり閉じたりするこのバージョンを投稿しましたが、一部のシステムではこれを1秒間に数百回行うと最終的にロックされることがわかりました。したがって、新しいソリューションは、関数の呼び出し間でファイル記述子を保持します。 Bashはとにかく終了時にクリーンアップします。

これは/ bin/sleepと同じように呼び出すことができ、要求された時間だけスリープします。パラメータなしで呼び出されると、永久にハングします。

snore 0.1  # sleeps for 0.1 seconds
snore 10   # sleeps for 10 seconds
snore      # sleeps forever

ここに私のブログに過剰な詳細を記載した記事があります

2
bolt

このアプローチは、プロセスを維持するためのリソースを消費しません。

while :; do sleep 1; done & kill -STOP $! && wait $!

壊す

  • while :; do sleep 1; done &バックグラウンドでダミープロセスを作成します
  • kill -STOP $!バックグラウンドプロセスを停止します
  • wait $!バックグラウンドプロセスを待ちます。これは永久にブロックされるため、バックグラウンドプロセスが停止する前に
0
qoomon

ウィンドウマネージャーを強制終了する代わりに、--replace または -replace 可能な場合は。

0