Answers:
sleep infinity
完全按照建议进行操作,并且不会虐待猫。
sleep infinity
尽管对于Linux而言,BSD 是一件很酷的事情,但BSD(或至少是OS X)也不了解。但是,while true; do sleep 86400; done
应该是一个适当的替代品。
infinity
在C中从“字符串”转换为double
。然后将double
其截断为允许的最大值timespec
,这意味着非常多的秒数(取决于体系结构),但从理论上讲是有限的。
tail
不阻塞一如既往:对于所有问题,答案都是简短,易于理解,易于遵循且完全错误。这里tail -f /dev/null
属于这一类;)
如果您一起看它,strace tail -f /dev/null
您会发现,该解决方案远非阻塞!它可能比问题中的sleep
解决方案还要糟糕,因为它使用(在Linux下)inotify
系统之类的宝贵资源。还有其他写入/dev/null
make 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"
读取时不使用循环会有点草率,但是您可以根据需要多次重用它fifo
,并使read
使用来 s终端touch "$HOME/.pause.fifo"
(如果有多个读取等待,则立即终止所有连接)。
pause()
syscall对于无限阻塞,有一个Linux内核调用,称为 pause()
它可以满足我们的要求:永远等待(直到信号到达)。但是,目前尚无用户空间程序。
创建这样的程序很容易。下面是一个片段,以创建一个名为一个非常小的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
python
如果您不想自己编译某些东西,但是已经python
安装了,可以在Linux下使用它:
python -c 'while 1: import ctypes; ctypes.CDLL(None).pause()'
(注意:使用 exec python -c ...
用于替换当前外壳,这将释放一个PID。还可以通过一些IO重定向来改进该解决方案,从而释放未使用的FD。这取决于您。)
这是如何工作的(我认为):ctypes.CDLL(None)
加载标准C库并pause()
在其他一些循环中运行该函数。比C版本效率低,但可以。
保持循环睡眠。这很容易理解,非常便携,并且在大多数情况下会阻塞。
trap
(将shell修改为信号的行为)或背景(允许shell截获来自终端的信号,例如Strg + C)。这样sleep infinity
就足够了(就像exec sleep infinity
是最后一条语句一样。请看一下区别用法strace -ffDI4 bash -c 'YOURCODEHERE'
)。循环睡眠会更好,因为sleep
在某些情况下可能会返回。例如,您不希望X11在上突然关闭killall sleep
,仅因为.xstartup
结束sleep infinity
而不是睡眠循环。
strace
单凭我无法证明在中确实编译了一些循环代码这一事实sleep
,并且我不想等待24天就进行测试(或反编译/bin/sleep
)。如果没有确凿的数学证明,那么似乎最好是防御性编程。也不要信任任何东西:killall -9 sleep
也许这看起来很丑,但是为什么不运行cat
它并让它永远等待输入呢?
cat
吧?mkfifo pipe && cat pipe
TL; DR: sleep infinity
实际上休眠了允许的最大时间,这是有限的。
想知道为什么在任何地方都没有记录,我费心地阅读了GNU coreutils的资源。发现它的执行大致如下:
strtod
在第一个参数上使用C stdlib中的命令将'infinity'转换为double precision。因此,假设IEEE 754双精度,则将64位正无穷大值存储在seconds
变量中。xnanosleep(seconds)
(在gnulib中找到),这又调用dtotimespec(seconds)
(也在gnulib中)将转换double
为struct timespec
。struct timespec
只是一对数字:整数部分(以秒为单位)和小数部分(以纳秒为单位)。幼稚地转换正无穷大为整数会导致不确定的行为(请参见C标准的6.3.1.4节),因此它将截断为TYPE_MAXIMUM (time_t)
。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_t
和time_t
是long
; 使用LP64数据模型,因此使用sizeof(long)
8(64位)。
结果是: TIME_T_MAX = 9223372036854775807
。
也就是说:sleep infinite
导致实际睡眠时间为9223372036836854775807秒(10 ^ 11年)。对于32位linux系统(sizeof(long)
为4(32位)):2147483647秒(68年;另请参见2038年问题)。
编辑:显然,nanoseconds
调用的函数不是直接进行syscall,而是与OS相关的包装器(也在gnulib中定义)。
这里有一个额外的步骤,其结果是:对于一些系统中HAVE_BUG_BIG_NANOSLEEP
是true
睡眠被截断至24天,然后在循环中调用。一些(或全部?)Linux发行版就是这种情况。请注意,如果configure -time测试成功(源),。
尤其是24 * 24 * 60 * 60 = 2073600 seconds
(加上999999999纳秒);但这是为了遵守指定的总睡眠时间而循环调用的。因此,先前的结论仍然有效。
总之,最终的睡眠时间 即使产生的实际经过时间不是便携式的也不是无限的,而是足以满足所有实际目的。这取决于操作系统和体系结构。
要回答原始问题,这显然已经足够好了,但是如果由于某种原因(一个资源非常有限的系统)您确实想要避免一个无用的额外倒数计时器,我想最正确的选择是使用cat
其他答案中描述的方法。
sleep infinity
现在将永久地永久休眠而不会循环:lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
如何发送SIGSTOP向自己怎么办?
这应该暂停该过程,直到收到SIGCONT。在您的情况下:永远不会。
kill -STOP "$$";
# grace time for signal delivery
sleep 60;
让我解释一下为什么sleep infinity
有效,尽管没有记录。jp48的答案也很有用。
最重要的事情是:通过指定inf
或infinity
(都不区分大小写),您可以在实现允许的最长时间内睡眠(即HUGE_VAL
and 的较小值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 strtod
,strtod()
将字符串转换为type的值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;
}
因此,INF
和INFINITY
(都不区分大小写)被视为HUGE_VAL
。
HUGE_VAL
家庭
让我们使用N1570作为C标准。HUGE_VAL
,HUGE_VALF
并且HUGE_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.c,xnanosleep(s)
基本上是这样做的:
struct timespec ts_sleep = dtotimespec (s);
nanosleep (&ts_sleep, NULL);
dtotimespec()
此函数将type的参数转换为type 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;
}
我最近需要这样做。我想出了以下函数,它将使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
而不是终止窗口管理器,而尝试使用--replace
或运行新的窗口管理器-replace
。
--replace
我总是会收到类似的警告another window manager is already running
。对我来说,这没有多大意义。
while :; do read; done
没有等待孩子睡觉的过程。
stdin
如果仍然碰巧连接到,则会吃掉它tty
。如果您将其与< /dev/null
忙循环一起运行。在某些情况下可能有一定用处,所以我不反对。