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

时间:2010-05-29 13:12:14

标签: linux bash sleep infinite

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

的末尾添加了这个
while true; do sleep 10000; done

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

祝你好运

11 个答案:

答案 0 :(得分:247)

sleep infinity完全符合其建议,并且不会虐待猫。

答案 1 :(得分:64)

也许这看起来很丑陋,但为什么不运行cat让它等待输入呢?

答案 2 :(得分:50)

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阻止,我们需要等待fifo之类的东西,它永远不会返回任何东西。在bash 4中,有一个命令可以为我们提供这样的fifocoproc。如果我们还等待阻止read(我们的coproc),我们就完成了。遗憾的是,这需要保持打开两个PID和fifo

具有名为fifo

的变体

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

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

在阅读中不使用循环有点草率,但您可以根据需要重复使用此fifo,并使用read生成touch "$HOME/.pause.fifo" s终止(如果有不止一次读取等待,所有都立即终止。)

或使用Linux pause()系统调用

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

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

我给你的建议:

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

答案 3 :(得分:20)

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

想知道为什么没有在任何地方记录这一点,我打扰阅读sources from GNU coreutils,我发现它大致执行以下内容:

  1. 在第一个参数上使用C stdlib中的strtod将“无穷大”转换为双精度。因此,假设IEEE 754双精度,64位正无穷大值存储在seconds变量中。
  2. 调用xnanosleep(seconds)found in gnulib),然后调用dtotimespec(seconds)also in 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。
  5. 这是Linux内核中的TIME_T_MAX,其定义为(time.h):

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

    请注意,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年;另见year 2038 problem)。

    编辑:显然调用的nanoseconds函数不是直接的系统调用,而是依赖于操作系统的包装器(也是defined in gnulib)。

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

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

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

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

答案 4 :(得分:8)

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

答案 5 :(得分:5)

如何向自己发送SIGSTOP呢?

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

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

答案 6 :(得分:4)

让我解释一下sleep infinity的工作原理,尽管它没有记载。 jp48's answer也很有用。

最重要的事情:通过指定infinfinity(不区分大小写),您可以在实现允许的最长时间内睡眠(即HUGE_VAL的较小值和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_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;
  }

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

HUGE_VAL家庭

让我们使用N1570作为C标准。 {§12-3

中定义了HUGE_VALHUGE_VALFHUGE_VALL


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

在支持无限性的实现中,HUGE_VALHUGE_VALFHUGE_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);

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;
}

答案 7 :(得分:2)

我最近需要这样做。我提出了以下函数,它将允许bash永远无需调用任何外部程序就可以睡眠:

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

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

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

int calculateTriangles(int n) {    
    if(n < 0)   return 1;    
    if(n == 0)  return 0;
    return ((2*n -2) *3) + calculateTriangles(n-2);   
}

There's a writeup with excessive details on my blog here

答案 8 :(得分:0)

如果可用,请尝试使用--replace-replace运行新的窗口管理器,而不是杀死窗口管理器。

答案 9 :(得分:0)

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

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

故障

  • while :; do sleep 1; done &在后​​台创建虚拟进程
  • kill -STOP $!停止后台进程
  • wait $!等待后台进程,它将永远被阻止,因为后台进程之前已被停止

答案 10 :(得分:-2)

while :; do read; done

无需等待孩子睡觉过程。