Bash:無限のスリープ(無限のブロック)


158

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

while true; do sleep 10000; done

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

宜しくお願いします

回答:


330

sleep infinity それが示唆することを正確に行い、猫を虐待することなく機能します。


16
涼しい。残念ながら、私のbusyboxは理解しません。
ユーザーではないユーザー

12
BSD(または少なくともOS X)はsleep infinityどちらも理解していませんが、Linuxについて学ぶのは素晴らしいことでした。ただし、while true; do sleep 86400; done十分な代用になるはずです。
Ivan X

16
これに関して、私は別の回答で文書化したいくつかの調査を行いました。要約するinfinityと、Cでは「文字列」からに変換されdoubleます。次に、double許可された最大値に切り捨てられますtimespec。これは、非常に長い秒数(アーキテクチャに依存)ですが、理論的には有限です。
jp48 '19

72

tail ブロックしない

いつものように、すべてに対して、短く、理解しやすく、理解しやすく、完全に間違っている答えがあります。ここではtail -f /dev/null、このカテゴリーに入ります。)

strace tail -f /dev/nullあなたがそれをあなたと一緒に見ると、この解決策はブロッキングには程遠いことに気づくでしょう!システムのsleepような貴重なリソースを(Linuxのもとで)使用するため、問題の解決策よりもさらに悪いでしょうinotify。また/dev/nulltailループを作成するために書き込む他のプロセス。(私のUbuntu64 16.10では、既にビジー状態のシステムで1秒あたり10個のシステムコールが追加されます。)

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

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

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

すべて( sleep infinity)が何らかの信号によって中断される可能性があります。そのため、例外が返らないことを本当に確認したい場合は、で既に行ったように、ループで実行する必要がありますsleep。(Linuxでは)/bin/sleep明らかに24日が上限であることに注意してください(を参照してくださいstrace sleep infinity)。したがって、あなたができる最善の方法はおそらく次のとおりです。

while :; do sleep 2073600; done

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

..しかし、無名でかなり近くに来ることができます 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何も返さないaのようなものを待つ必要があります。でbash 4正確なAをご提供できるコマンドがありますfifocoproc。ブロッキングも待機している場合read(これは、coproc)完了です。悲しいことに、これは2つのPIDとを開いたままにする必要がありますfifo

名前付きのバリアント fifo

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

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

読み取りでループを使用しないことは少しずさんですが、fifo好きなだけこれを再利用して、read sターミネートを使用touch "$HOME/.pause.fifo"(1つ以上の読み取り待機がある場合、すべてが一度に終了します)。

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

無限ブロッキングには、Linuxカーネルコールがあります。 pause()があります。ただし、これには(まだ)ユーザー空間プログラムはありません。

C

このようなプログラムの作成は簡単です。ここで呼ばれる非常に小さなLinuxのプログラムを作成するためのスニペットでpause無期限(ニーズ一時停止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リダイレクトでも改善でき、未使用のFDを解放します。これはユーザー次第です。)

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

あなたへの私の推薦:

ぐるぐる眠り続ける。理解しやすく、移植性が高く、ほとんどの場合ブロックされます。


1
@Andrew通常、trap(シェルの動作をシグナルに変更する)もバックグラウンド(シェルがStrg + Cのように端末からシグナルをインターセプトできるようにする)も必要ありません。それでsleep infinity十分です(exec sleep infinity違いが最後のステートメントであるかのように動作しますstrace -ffDI4 bash -c 'YOURCODEHERE'。使用の違いを確認するには)。ループスリープはsleep、特定の状況で戻る可能性があるため、優れています。たとえば、スリープループの代わりに終了するkillall sleepという理由だけで、X11をで突然シャットダウンしたくない場合。.xstartupsleep infinity
ティノ

少しあいまいかもしれませんs6-pauseが、を実行するユーザーランドコマンドでありpause()、オプションでさまざまな信号を無視します。
Patrick

@Tino /bin/sleepは、あなたが言うように24日に制限されていません。更新していただければ幸いです。現在Linuxでは、このコードはアクティブです。個々のnanosleep()システムコールの上限は24日ですが、ループで呼び出します。そのsleep infinityため、24日後に終了しないでください。double正の無限大がに変換されますstruct timespecrpl_nanosleepGDBで見ると、Ubuntu 16.04にinfinity変換さ{ tv_sec = 9223372036854775807, tv_nsec = 999999999 }れます。
nh2

@ nh2 完全にブロックされるのではなく、ループがおそらくループすることはすでにテキストで言及されました。この事実をもう少し明確にするために、少し編集しました。この「たぶん」に注意してください。なぜなら、strace私はでいくつかのループコードがコンパイルされているという事実を証明できず、sleepこれをテスト(または逆コンパイル/bin/sleep)するためだけに24日間待機したくないからです。数学的な証明が難しい場合は、防御的なプログラミングを行う方が常に優れています。また、決して信頼は何も:killall -9 sleep
ティノ

pause()オプションはperlでかなり簡単に実行できます:perl -MPOSIX -e 'pause()'
tgoodhart

70

多分これは醜いように思われるかもしれませんが、なぜ実行catしてそれが永遠に入力を待つようにしないのですか?


4
これは、そこから読み取る吊り下げパイプがない場合は機能しません。お知らせ下さい。
マットジョイナー

2
@マット、たぶんパイプとcatそれを作る?mkfifo pipe && cat pipe
のMichałTrybus

@twalbergの発言。ただし、次に示すように、すぐに3に再割り当てしてリンクを解除できます
。superuser.com/ a / 633185/762481

32

TL; DR: sleep infinity許容される最大時間を実際にスリープします。これは有限です。

なぜこれがどこにも文書化されていないのではないかと思って、私はGNU coreutilsからソースを読むのを煩わしく思いました、そしてそれが大体以下のように実行されるのを発見しました:

  1. 使用strtod倍精度に変換「無限大」の最初の引数にCのSTDLIBから。したがって、IEEE 754の倍精度を想定すると、64ビットの正の無限大値がseconds変数にます。
  2. 呼び出しxnanosleep(seconds)gnulibで見つかった)、これはターン呼び出すにはdtotimespec(seconds)もgnulib中)から変換するdoublestruct timespecます。
  3. struct timespec整数のペア(秒単位)と小数部分(ナノ秒単位)のペアです。正の無限大を単純に整数に変換すると、未定義の動作が発生するため(C標準の6.3.1.4を参照)、代わりに切り捨てられますTYPE_MAXIMUM (time_t)ます。
  4. の実際の値はTYPE_MAXIMUM (time_t)標準では設定されていません(設定されてsizeof(time_t)いません)。例として、最近のLinuxカーネルからx86-64を選択してみましょう。

これはTIME_T_MAX、次のように定義された(time.h)Linuxカーネルにあります。

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

time_tある__kernel_time_ttime_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年の問題も参照)。


編集nanoseconds呼び出された関数は直接syscallではなく、OS依存のラッパー(gnulibでも定義されている)のようです。

その結果、追加の手順があります。あるシステムでHAVE_BUG_BIG_NANOSLEEPtrue、スリープが24日に切り捨てられてから、ループで呼び出されます。これは一部(またはすべて)のLinuxディストリビューションに当てはまります。このラッパーは、構成時のテストが成功した場合は使用されない場合があることに注意してください(ソース)。

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


結論として、結果として得られる睡眠時間は無限ではなく、すべての実用的な目的に十分なほど高いられる実際の時間の経過がポータブルではない場合でも、。それはOSとアーキテクチャに依存します。

元の質問に答えるために、これは良い十分には明らかであるが、もし何らかの理由で(非常にリソースに制約システム)あなたは本当に私が最も正しい選択肢を使用することだと思い、役に立たない余分なカウントダウンタイマーを避けたいcat方法は、他の回答で説明。


1
次のcoreutilsでは、sleep infinityループせずに実際に永久にスリープします。lists.gnu.org
gnulib

8

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


1
tail -f /dev/nullSaaSプラットフォームでの私にとっても
有効な

2
tail -f /dev/nullstdinを消費しないという利点もあります。そのために使用しました。
Sudo Bash

このオプションを検討している人は、この回答を読んで、このオプションの影響について学ぶ必要があります。
シャドウ

6

SIGSTOPを自分自身に送信するのはどうですか?

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

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

6
信号は非同期です。したがって、以下が発生する可能性があります:a)シェルがkillを呼び出すb)killは、シェルがシグナルSTOPを受信することをカーネルに通知しますc)killは終了してシェルに戻りますd)シェルは続行します(スクリプトが終了するため、おそらく終了します)e)カーネルは最終的に配信する時間を見つけますシェルにSTOPの信号を送る
ユーザーでないユーザー、

1
@temple素晴らしい洞察、信号の非同期性については考えていませんでした。ありがとう!
michuelnik 2014

4

sleep infinity文書化されていませんが、なぜ機能するかを説明しましょう。jp48の回答も参考になります。

最も重要なこと:infまたはinfinity(両方とも大文字と小文字を区別しない)を指定することで、実装が許可する最長時間(つまり、HUGE_VALTYPE_MAXIMUM(time_t))。

それでは、詳細を見ていきましょう。sleepコマンドのソースコードはcoreutils / src / sleep.cから読むことができます。基本的に、関数はこれを行います。

double s; //seconds
xstrtod (argv[i], &p, &s, cl_strtod); //`p` is not essential (just used for error check).
xnanosleep (s);

理解 xstrtod (argv[i], &p, &s, cl_strtod)

xstrtod()

gnulib / lib / xstrtod.cによると、の呼び出しは、変換関数を使用して、xstrtod()文字列argv[i]を浮動小数点値に変換して格納し*sますcl_strtod()

cl_strtod()

分かるようcoreutilsの/ LIB / CL-strtod.ccl_strtod()使用して、浮動小数点値に文字列を変換しますstrtod()

strtod()

よればman 3 strtodstrtod()型の値に文字列を変換しますdouble。マンページは言う

文字列(の最初の部分)の予想される形式は...または(iii)無限大、または...

無限大は次のように定義されます

無限大は、大文字と小文字を区別せず、「INF」または「INFINITY」のいずれかです。

文書は言うが

正しい値でオーバーフローが発生する場合は、プラスまたはマイナスHUGE_VALHUGE_VALFHUGE_VALL)が返されます

、無限がどのように扱われるかは明らかではありません。それでは、ソースコードgnulib / lib / strtod.cを見てみましょう。読みたいのは

else if (c_tolower (*s) == 'i'
         && c_tolower (s[1]) == 'n'
         && c_tolower (s[2]) == 'f')
  {
    s += 3;
    if (c_tolower (*s) == 'i'
        && c_tolower (s[1]) == 'n'
        && c_tolower (s[2]) == 'i'
        && c_tolower (s[3]) == 't'
        && c_tolower (s[4]) == 'y')
      s += 5;
    num = HUGE_VAL;
    errno = saved_errno;
  }

したがって、INFand INFINITY(両方とも大文字と小文字を区別しない)はと見なされHUGE_VALます。

HUGE_VAL 家族

C標準としてN1570を使用します。HUGE_VALHUGE_VALFおよびHUGE_VALLマクロは§7.12-3で定義されています

マクロ
    HUGE_VAL
は正の二重定数式に展開されますが、必ずしも浮動小数点数として表現できるわけではありません。マクロ
    HUGE_VALF
    HUGE_VALL
は、それぞれfloatとlong doubleの類似物ですHUGE_VAL

HUGE_VALHUGE_VALF、およびHUGE_VALL無限大をサポートする実装では、正の無限大することができます。

および§7.12.1-5

フローティング結果がオーバフローし、デフォルトの丸めが有効である場合、この関数は、マクロの値を返しHUGE_VALHUGE_VALFまたはHUGE_VALL戻り型に応じて

理解 xnanosleep (s)

今、私たちはの本質をすべて理解していますxstrtod()。上記の説明から、xnanosleep(s)最初に実際にを見てきたのはxnanosleep(HUGE_VALL)

xnanosleep()

ソースコードgnulib / lib / xnanosleep.cによるとxnanosleep(s)基本的にこれを行います:

struct timespec ts_sleep = dtotimespec (s);
nanosleep (&ts_sleep, NULL);

dtotimespec()

この関数は、型の引数を型doubleのオブジェクトに変換しますstruct timespec。とてもシンプルなので、ソースコードを引用させてください gnulib / lib / dtotimespec.cを。コメントはすべて私が追加しました。

struct timespec
dtotimespec (double sec)
{
  if (! (TYPE_MINIMUM (time_t) < sec)) //underflow case
    return make_timespec (TYPE_MINIMUM (time_t), 0);
  else if (! (sec < 1.0 + TYPE_MAXIMUM (time_t))) //overflow case
    return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1);
  else //normal case (looks complex but does nothing technical)
    {
      time_t s = sec;
      double frac = TIMESPEC_HZ * (sec - s);
      long ns = frac;
      ns += ns < frac;
      s += ns / TIMESPEC_HZ;
      ns %= TIMESPEC_HZ;

      if (ns < 0)
        {
          s--;
          ns += TIMESPEC_HZ;
        }

      return make_timespec (s, ns);
    }
}

以来、time_t整数型(§7.27.1-3を参照)として定義され、我々が型の最大値が仮定自然であるtime_tよりも小さいHUGE_VAL(タイプのdouble)です。つまり、オーバーフローのケースに入るということです。(すべての場合において、手順は基本的に同じであるため、実際にはこの仮定は必要ありません。)

make_timespec()

私たちが登らなければならない最後の壁はmake_timespec()です。幸い、ソースコードgnulib / lib / timespec.hを引用するだけで十分です。

_GL_TIMESPEC_INLINE struct timespec
make_timespec (time_t s, long int ns)
{
  struct timespec r;
  r.tv_sec = s;
  r.tv_nsec = ns;
  return r;
}

2

私は最近これを行う必要がありました。外部プログラムを呼び出さずに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

ここに私のブログに過度の詳細がある記事があります


1

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

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

壊す

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

0

ウィンドウマネージャーを強制終了する代わりに、--replaceまたは-replace使用可能な場合は、新しいウィンドウマネージャーを実行してください。


1
使用する--replaceと、常に次のような警告が表示されanother window manager is already runningます。それは私にはあまり意味がありません。
10

-2
while :; do read; done

子供の睡眠過程を待つ必要はありません。


1
これがstdinまだに接続されている場合、これは問題になりttyます。それをビジーループで実行すると< /dev/nullします。特定の状況ではある程度役立つかもしれないので、反対投票はしません。
ティノ

1
これは非常に悪い考えです。CPUの全スロットを消費するだけです。
Mohammed Noureldin 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.