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

2024-10-28 08:37:00
admin
原创
68
摘要:问题描述:我使用startx来启动 X,它将评估我的.xinitrc。在我的 中,.xinitrc我使用 来启动我的窗口管理器/usr/bin/mywm。现在,如果我终止我的 WM(为了测试其他 WM),X 也会终止,因为脚本.xinitrc已到达 EOF。因此,我在 的末尾添加了以下内容.xinitrc:w...

问题描述:

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

while true; do sleep 10000; done

这样,如果我关闭 WM,X 就不会终止。现在我的问题是:如何实现无限睡眠而不是循环睡眠?是否有类似冻结脚本的命令?


解决方案 1:

sleep infinity如果实施,将永远休眠或休眠最长休眠长度,具体取决于实施情况。(请参阅该问题的其他答案和评论,其中提到了一些变化)

解决方案 2:

tail不阻塞

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

如果您使用 来查看它strace tail -f /dev/null,您会发现这个解决方案远非阻塞!它可能比sleep问题中的解决方案更糟糕,因为它使用(在 Linux 下)inotify系统等宝贵资源。还有其他写入/dev/nullmaketail循环的进程。(在我的 Ubuntu64 16.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(又名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"

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

或者使用 Linuxpause()系统调用

对于无限阻塞,有一个名为的 Linux 系统调用pause()可以实现我们的要求:永远等待(直到信号到达)。但是,目前还没有针对此问题的用户空间程序。

创建这样的程序很容易。下面是创建一个名为 的非常小的 Linux 程序的代码片段pause,该程序会无限期暂停(需要 C 编译器,例如gcc,并使用diet等来生成小的二进制文件):

printf '#include <unistd.h>
int 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:

这可能看起来很丑陋,但为什么不直接运行cat并让它永远等待输入呢?

解决方案 4:

TL;DR:自 GNU coreutils 版本 9 以来,它sleep infinity在 Linux 系统上做了正确的事情。以前(以及其他系统中)的实现实际上是休眠允许的最大时间,这是有限的。


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

  1. 使用strtodC 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)

请注意,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_NANOSLEEP时间会被截断为 24 天,然后在循环中调用。某些(或所有?)Linux 发行版都是这种情况。请注意,如果配置true时测试成功,则可能不会使用此包装器(来源)。

具体来说,应该是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)。


解决方案 5:

sleep infinity看起来非常优雅,但有时​​由于某种原因它不起作用。在这种情况下,您可以尝试其他阻止命令,例如cat,,,等。read`tail -f /dev/null`grep a

解决方案 6:

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

最重要的是:通过指定infinfinity(均不区分大小写),您可以休眠您的实现允许的最长时间(即HUGE_VAL和的较小值TYPE_MAXIMUM(time_t))。

现在让我们深入了解细节。可以从coreutils/src/sleep.csleep中读取命令的源代码。本质上,该函数执行以下操作:

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_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_VALHUGE_VALF并且HUGE_VALL宏在§7.12-3 中定义

该宏

    HUGE_VAL

扩展为正双精度常量表达式,不一定能表示为浮点数。这两个宏

    HUGE_VALF

    HUGE_VALL

分别是 的浮点数和长双精度模拟HUGE_VAL

HUGE_VALHUGE_VALFHUGE_VALL在支持无穷大的实现中可以是正无穷大。

以及 §7.12.1-5

如果浮点结果溢出且默认舍入生效,则函数根据返回类型返回宏的值HUGE_VALHUGE_VALFHUGE_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;
}

解决方案 7:

那么向自己发送SIGSTOP怎么样?

这应该会暂停进程,直到收到 SIGCONT。对于你的情况,永远不会。

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

解决方案 8:

我最近有这个需求。我想出了以下函数,它可以让 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

我的博客上有一篇非常详细的文章

解决方案 9:

这种方法不会消耗任何资源来维持进程活动。

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

分解

  • while :; do :; done &在后台创建一个虚拟进程

  • kill -STOP $!停止后台进程

  • wait等待后台进程,这将永远阻塞,因为后台进程之前已经停止了

笔记

  • 仅在脚本文件内有效。

解决方案 10:

jp48 1 和ynn 2的精彩答案深入研究了sleep infinity的源代码来解释其以前的行为,同时仅仅注意到它最近发生了变化“做了正确的事情”。

以下是我对当前行为改变的深入分析:

  • 该变化并未发生在实用程序所在的coreutils上,sleep而是发生在其使用的GNULib函数上xnanosleep()

  • 它始于2020-02-10 在 GNULib 错误列表上的一场讨论,引用了 StackOverflow 上的这个问题(这太酷了!)关于尾部方法的缺点和同事@Vladimir Panteleev提出的补丁,提出了一种更好的方法。

  • 2020 年 2 月 16 日,该补丁作为提交提交到存储库,并在变更日志中提及

  • coreutils它于 2020-02-25被添加到源代码树中,当时他们将子模块引用更新gnulib为前一天所做的提交。

  • 因此,它于 2020-03-05 在Coreutilsv8.32中发布,这是 8.x 系列的最后一个版本。

  • 变化本身非常简单:

 /* Sleep until the time (call it WAKE_UP_TIME) specified as
    SECONDS seconds after the time this function is called.
 */
 int
 xnanosleep (double seconds)
 {
 #if HAVE_PAUSE
   if (1.0 + TYPE_MAXIMUM (time_t) <= seconds)
     {
       do
         pause ();
       while (errno == EINTR);
 
       /* pause failed (!); fall back on repeated nanosleep calls.  */
     }
 #endif

通过使用pause()系统调用,现在sleep infinity实际上会永远休眠直到收到信号,这可以说是绝对最小的开销。

解决方案 11:

不要终止窗口管理器,而是尝试使用--replace或(-replace如果可用)运行新的窗口管理器。

解决方案 12:

sleep inf可以节省一些输入,它与sleep infinity

解决方案 13:

我们可以使用flock来永远休眠。这就是死锁机制。

flock -sF /tmp flock -eF /tmp true

并且它还可以在指定的时间休眠。

flock -sF /tmp flock -w seconds -eF /tmp true

解决方案 14:

while :; do read; done

无需等待孩子睡觉过程。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用