Bash:无限睡眠(无限阻挡)

wat*_*ain 135 linux bash sleep infinite

startx用来启动X来评估我的.xinitrc.在我的.xinitrc开始我的窗口管理器使用/usr/bin/mywm.现在,如果我杀了我的WM(为了测试其他一些WM),X也将因为.xinitrc脚本达到EOF而终止.所以我在最后添加了这个.xinitrc:

while true; do sleep 10000; done
Run Code Online (Sandbox Code Playgroud)

这样,如果我杀了我的WM,X就不会终止.现在我的问题是:如何进行无限次睡眠而不是循环睡眠?有没有一个命令有点像冻结脚本?

最好的祝福

Don*_*son 282

sleep infinity 确切地说它的建议和工作没有虐待猫.

  • 促进动物权利的好方案! (35认同)
  • 凉.不幸的是我的busybox不明白. (14认同)
  • BSD(或至少OS X)也不理解"睡眠无限",尽管学习Linux是一件很酷的事情.然而,'虽然真实; 睡觉86400; 完成`应该是一个充分的替代品. (10认同)
  • 关于这一点,我做了一些研究,我在一个单独的答案中记录.总结一下:`infinity`在C中从"string"转换为`double`.然后将`double`截断为允许的最大值`timespec`,这意味着非常大的秒数(取决于架构),但理论上是有限的. (10认同)
  • 我用谷歌搜索“虐待猫”。不确定我期望找到什么。怎么样:“执行此脚本时没有猫受到伤害” (8认同)
  • 与许多命令的情况一样,BusyBox中包含的`sleep`命令是一个精简版本,只有基本和最广泛使用的功能(睡眠指定的完整秒数),所以如果你需要你的脚本工作在只有BusyBox可用的环境中,你必须使用其他解决方案之一(例如,'cat`,如Michał所建议的). (3认同)
  • FWIW,macOS Big Sur 现在理解“无限睡眠”,尽管 Mojave 不理解。(我跳过了卡特琳娜。) (3认同)
  • `/bin/sleep infinity` 在我正在使用的 Big Sur 上_不起作用_。然而,“gsleep infinity”却可以。 (3认同)

Mic*_*bus 69

也许这看起来很丑陋,但为什么不跑cat,让它等待输入永远?

  • 如果您没有可以读取的吊管,则此功能无效.请指教. (4认同)
  • @Matt,也许制作烟斗和"猫"吧?`mkfifo pipe && cat pipe` (2认同)

Tin*_*ino 61

tail 不阻止

一如既往:对于一切都有一个简短,易于理解,易于遵循和完全错误的答案.这里tail -f /dev/null属于这一类;)

如果你看一下,strace tail -f /dev/null你会注意到,这个解决方案远非阻塞!它可能比问题中的sleep解决方案更糟糕,因为它使用(在Linux下)像inotify系统这样的宝贵资源.还有其他写入/dev/nullmake tail循环的进程.(在我的Ubuntu64 16.10上,这在已经繁忙的系统上每秒增加了10个系统调用.)

问题是阻塞命令

不幸的是,没有这样的事情..

阅读:我不知道有任何方法可以直接将shell归档.

sleep infinity一些信号可以打断一切(偶数).因此,如果你想确定它没有异常返回,它必须在循环中运行,就像你已经为你做的那样sleep.请注意,(在Linux上)/bin/sleep显然是限制在24天(看看strace sleep infinity),因此你可以做的最好的可能是:

while :; do sleep 2073600; done
Run Code Online (Sandbox Code Playgroud)

(请注意,我认为sleep内部循环的值高于24天,但这意味着:它没有阻塞,循环非常缓慢.那么为什么不将此循环移到外面?)

..但你可以来一个未命名的 fifo

只要没有信号发送到进程,您就可以创建真正阻塞的东西.以下使用bash 4,2个PID和1 fifo:

bash -c 'coproc { exec >&-; read; }; eval exec "${COPROC[0]}<&-"; wait'
Run Code Online (Sandbox Code Playgroud)

strace如果你愿意,你可以检查这是否真的阻止:

strace -ff bash -c '..see above..'
Run Code Online (Sandbox Code Playgroud)

这是如何构建的

read如果没有输入数据则阻塞(参见其他答案).但是,tty(aka.stdin)通常不是一个好的来源,因为它在用户注销时关闭.它也可能从中窃取一些输入tty.不太好.

为了read阻止,我们需要等待一些fifo永远不会返回的东西.在bash 4那里是可以准确地为我们提供了这样的一个命令fifo:coproc.如果我们也等待阻止read(这是我们的coproc),我们就完成了.遗憾的是,这需要保持打开两个PID和一个fifo.

变体与命名 fifo

如果您不打扰使用命名fifo,可以按如下方式执行此操作:

mkfifo "$HOME/.pause.fifo" 2>/dev/null; read <"$HOME/.pause.fifo"
Run Code Online (Sandbox Code Playgroud)

在读取时不使用循环有点草率,但您可以根据需要重复使用它fiforead使用s终止touch "$HOME/.pause.fifo"(如果有多个读取等待,则所有都立即终止).

或者使用Linux pause()系统调用

对于无限阻塞,有一个称为Linux内核的调用,pause()它可以完成我们想要的任务:永远等待(直到信号到达).但是,目前还没有用户空间程序.

C

创建这样的程序很容易.下面是一个片段,以创建一个名为一个非常小的Linux程序pause,其无限期暂停(需求diet,gcc等):

printf '#include <unistd.h>\nint main(){for(;;)pause();}' > pause.c;
diet -Os cc pause.c -o pause;
strip -s pause;
ls -al pause
Run Code Online (Sandbox Code Playgroud)

python

如果您不想自己编译,但已python安装,可以在Linux下使用:

python -c 'while 1: import ctypes; ctypes.CDLL(None).pause()'
Run Code Online (Sandbox Code Playgroud)

(注意:exec python -c ...用于替换当前shell,这将释放一个PID.解决方案也可以通过一些IO重​​定向进行改进,释放未使用的FD.这取决于您.)

这是如何工作的(我认为):ctypes.CDLL(None)加载标准C库并pause()在一些额外的循环中运行其中的函数.效率低于C版本,但有效.

我给你的建议:

保持循环睡眠.它易于理解,非常便携,并且在大多数情况下都会阻止.

  • 在下一个 coreutils 中,“sleep infinity”现在实际上将永远休眠而不循环:https://lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html (4认同)
  • pause()解决方案很棒。谢谢! (3认同)
  • 可能有点晦涩,但是 [`s6-pause`](http://skarnet.org/software/s6-portable-utils/s6-pause.html) 是一个运行 `pause()` 的用户态命令,可选忽略各种信号。 (3认同)
  • 使用 perl 可以很容易地完成暂停()选项:perl -MPOSIX -e 'pause()' (2认同)

jp4*_*p48 24

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在Linux内核中,其定义为(time.h)为:

(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
Run Code Online (Sandbox Code Playgroud)

请注意,time_t__kernel_time_ttime_tlong; 使用LP64数据模型,因此sizeof(long)是8(64位).

结果如下:TIME_T_MAX = 9223372036854775807.

即:sleep infinite导致实际睡眠时间为9223372036854775807秒(10 ^ 11年).对于32位Linux系统(sizeof(long)4(32位)):2147483647秒(68年;另见2038年问题).


编辑:显然,nanoseconds调用的函数不是直接的系统调用,而是依赖操作系统的包装器(也在gnulib中定义).

这里有一个额外的步骤,其结果是:对于一些系统中HAVE_BUG_BIG_NANOSLEEPtrue睡眠被截断至24天,然后在循环中调用.某些(或所有?)Linux发行版就是这种情况.请注意,如果configure -time测试成功(),则可能不使用此包装器.

特别是,那将是24 * 24 * 60 * 60 = 2073600 seconds(加999999999纳秒); 但是这是在循环中调用的,以便遵守指定的总睡眠时间.因此,先前的结论仍然有效.


总之,即使由此产生的实际时间流逝不可移动,所产生的睡眠时间也不是无限的,但对于所有实际目的而言都足够高 ; 这取决于操作系统和架构.

回答原来的问题,这显然已经足够好但是如果由于某种原因(一个资源非常有限的系统)你真的想避免一个无用的额外倒数计时器,我想最正确的选择是使用cat其他答案中描述的方法.

  • 在下一个 coreutils 中,“sleep infinity”现在实际上将永远休眠而不循环:https://lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html (4认同)
  • @PedroA 它应该在 coreutils 9 中。 (2认同)

ynn*_*ynn 9

让我解释一下为什么sleep infinity有效,尽管它没有记录。jp48 的回答也很有用。

最重要的一点:通过指定infinfinity(两者不区分大小写),你可以睡觉时间最长的执行许可证(即较小值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);
Run Code Online (Sandbox Code Playgroud)

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

xstrtod()

根据gnulib/lib/xstrtod.c,调用xstrtod()将字符串转换argv[i]为浮点值并将其存储到*s,使用转换函数cl_strtod()

cl_strtod()

coreutils/lib/cl-strtod.c可以看出,cl_strtod()将字符串转换为浮点值,使用strtod().

strtod()

根据man 3 strtod,strtod()将字符串转换为类型的值double。手册页说

字符串(的初始部分)的预期形式是……或(iii)无穷大,或……

无穷大被定义为

无穷大是“INF”或“INFINITY”,不考虑大小写。

虽然文件告诉

如果正确的值会导致溢出,则返回加号或减号HUGE_VAL( HUGE_VALF, HUGE_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;
  }
Run Code Online (Sandbox Code Playgroud)

因此,INFINFINITY(均不区分大小写)被视为HUGE_VAL

HUGE_VAL 家庭

让我们使用N1570作为 C 标准。HUGE_VALHUGE_VALFHUGE_VALL宏定义在§7.12-3


    HUGE_VAL
扩展为正双常量表达式,不一定可以表示为浮点数。宏
    HUGE_VALF
    HUGE_VALL
分别是 float 和 long double 的类似物HUGE_VAL

HUGE_VAL, HUGE_VALF, 和HUGE_VALL在支持无穷大的实现中可以是正无穷大。

并在 §7.12.1-5

如果浮动结果溢出和默认的舍入有效时,则该函数返回的宏的值HUGE_VALHUGE_VALFHUGE_VALL根据返回类型

理解 xnanosleep (s)

现在我们了解了xstrtod(). 从上面的解释中,很明显xnanosleep(s)我们看到的第一个实际上是xnanosleep(HUGE_VALL)

xnanosleep()

根据源代码gnulib/lib/xnanosleep.cxnanosleep(s)基本上是这样做的:

struct timespec ts_sleep = dtotimespec (s);
nanosleep (&ts_sleep, NULL);
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

由于time_t被定义为整数类型(参见第 7.27.1-3 节),我们很自然地假设 type 的最大值time_t小于HUGE_VAL(of type 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;
}
Run Code Online (Sandbox Code Playgroud)


Hui*_*eng 8

sleep infinity看起来最优雅,但有时​​由于某种原因它不起作用.在这种情况下,你可以尝试其他的拦截命令,如cat,read,tail -f /dev/null,grep a等.

  • 考虑此选项的人应该阅读[此答案](/sf/answers/2915888251/)以了解此选项的后果。 (4认同)
  • `tail -f /dev/null` 对于我来说在 SaaS 平台上也是一个可行的解决方案 (3认同)
  • `tail -f/dev/null`也具有不消耗stdin的优点.因为这个原因我用过它. (2认同)

mic*_*nik 5

如何向自己发送SIGSTOP

这应暂停该过程,直到收到SIGCONT.在你的情况下:永远不会.

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
Run Code Online (Sandbox Code Playgroud)

  • 信号是异步的.所以可能发生以下情况:a)shell调用kill b)kill告诉内核shell应该接收信号STOP c)kill终止并返回shell d)shell继续(可能因脚本结束而终止)e)内核终于找到了传递的时间信号STOP到shell (6认同)