198

我用startx启动 X 来评估我的.xinitrc. 在我.xinitrc的窗口管理器中,我使用/usr/bin/mywm. 现在,如果我杀死我的 WM(为了测试其他 WM),X 也会终止,因为.xinitrc脚本到达 EOF。所以我在我的末尾添加了这个.xinitrc

while true; do sleep 10000; done

这样,如果我杀死我的 WM,X 就不会终止。现在我的问题是:我怎样才能进行无限睡眠而不是循环睡眠?有没有类似冻结脚本的命令?

4

11 回答 11

425

sleep infinity完全按照它的建议行事,并且在没有虐待猫的情况下工作。

于 2014-02-28T16:07:57.027 回答
92

tail不阻塞

一如既往:对于任何事情都有一个简短、易于理解、易于理解且完全错误的答案。这里tail -f /dev/null属于这一类;)

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

问题是针对阻塞命令

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

阅读:我不知道有什么方法可以直接用 shell 存档。

一切(甚至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(aka. stdin) 通常不是一个好的来源,因为它在用户注销时关闭。它也可能会从tty. 不太好。

为了read阻止,我们需要等待像 a这样的东西fifo,它永远不会返回任何东西。有bash 4一个命令可以准确地为我们提供这样的fifo: coproc。如果我们还等待阻塞read(这是我们的coproc),我们就完成了。可悲的是,这需要保持打开两个 PID 和一个fifo.

带有命名的变体fifo

如果您不介意使用 named fifo,您可以按以下方式执行此操作:

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

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

或者使用 Linuxpause()系统调用

对于无限阻塞,有一个 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 ...用于替换当前的 shell,这会释放一个 PID。解决方案也可以通过一些 IO 重定向来改进,释放未使用的 FD。这取决于您。)

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

我给你的建议:

保持循环睡眠。它很容易理解,非常便携,并且大部分时间都会阻塞。

于 2017-01-14T22:13:20.613 回答
72

也许这看起来很难看,但为什么不直接运行cat并让它永远等待输入呢?

于 2010-05-29T13:23:33.307 回答
47

TL; DR:sleep infinity实际上睡眠允许的最长时间,这是有限的。

想知道为什么这在任何地方都没有记录,我费心阅读GNU coreutils 的源代码,发现它大致执行如下:

  1. 在第一个参数上使用strtodfrom C stdlib 将 'infinity' 转换为双精度值。因此,假设 IEEE 754 双精度,64 位正无穷大值存储在seconds变量中。
  2. 调用xnanosleep(seconds)在 gnulib 中找到),这反过来又调用dtotimespec(seconds)也在 gnulib 中)从 todouble转换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。

这是TIME_T_MAX在 Linux 内核中,它被定义time.h为():

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

注意time_tis__kernel_time_ttime_tis 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调用的函数不是直接的系统调用,而是依赖于操作系统的包装器(也在gnulib 中定义)。

结果有一个额外的步骤:对于某些系统,睡眠HAVE_BUG_BIG_NANOSLEEPtrue截断为 24 天,然后在循环中调用。某些(或全部?)Linux 发行版就是这种情况。请注意,如果配置时间测试成功(source),则可能不使用此包装器。

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


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

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

编辑:最近的 GNU coreutils 版本将尝试使用pause系统调用(如果可用)而不是循环。当针对 Linux(可能是 BSD)中的这些较新版本时,前面的论点不再有效。


可移植性

这是一个重要的有效问题:

  • sleep infinity是POSIX 中未考虑的 GNU coreutils 扩展。GNU 的实现还支持持续时间的“花哨”语法,例如sleep 1h 5.2sPOSIX 只允许正整数(例如sleep 0.5不允许)。
  • 一些兼容的实现:GNU coreutils、FreeBSD(至少从8.2版开始?)、Busybox(需要使用选项FANCY_SLEEP和编译FLOAT_DURATION)。
  • strtod行为与 C 和 POSIX 兼容(即strtod("infinity", 0)在符合 C99 的实现中始终有效,请参阅 §7.20.1.3)。

于 2017-07-30T05:14:24.227 回答
10

sleep infinity看起来最优雅,但有时​​由于某种原因它不起作用。在这种情况下,您可以尝试其他阻止命令,例如catreadtail -f /dev/nullgrep a

于 2014-11-25T13:41:44.953 回答
10

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

最重要的一点:通过指定infor infinity(两者都不区分大小写),您可以在实现允许的最长时间内休眠(即HUGE_VALand的较小值TYPE_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.c可以看出,cl_strtod()将字符串转换为浮点值,使用strtod().

strtod()

根据man 3 strtodstrtod()将字符串转换为 类型的值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;
  }

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

HUGE_VAL家庭

让我们使用N1570作为 C 标准。HUGE_VAL,HUGE_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_VAL,HUGE_VALF或的值HUGE_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);

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 节),我们很自然地假设 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;
}
于 2020-01-05T03:47:53.443 回答
7

给自己发送一个SIGSTOP怎么样?

这应该暂停进程,直到收到 SIGCONT。在你的情况下:从不。

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
于 2011-01-29T06:50:38.727 回答
3

我最近需要这样做。我想出了以下函数,它可以让 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 || :
}

注意:我之前发布了一个每次都会打开和关闭文件描述符的版本,但我发现在某些系统上每秒执行数百次最终会锁定。因此,新的解决方案在函数调用之间保留文件描述符。无论如何,Bash 都会在退出时清理它。

这可以像 /bin/sleep 一样调用,它会在请求的时间内休眠。不带参数调用,它将永远挂起。

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

在我的博客上有一篇包含过多细节的文章

于 2017-11-27T22:43:18.833 回答
3

这种方法不会消耗任何资源来保持进程处于活动状态。

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

分解

  • while :; do :; done &在后台创建一个虚拟进程
  • kill -STOP $!停止后台进程
  • wait等待后台进程,这将永远阻塞,导致后台进程之前停止

笔记

  • 仅在脚本文件中起作用。
于 2019-06-26T05:37:01.540 回答
0

与其杀死窗口管理器,不如尝试使用--replace或运行新的窗口管理器(-replace如果可用)。

于 2010-05-29T13:35:22.860 回答
-2
while :; do read; done

无需等待孩子睡眠过程。

于 2016-06-02T08:11:31.587 回答