我用startx
启动 X 来评估我的.xinitrc
. 在我.xinitrc
的窗口管理器中,我使用/usr/bin/mywm
. 现在,如果我杀死我的 WM(为了测试其他 WM),X 也会终止,因为.xinitrc
脚本到达 EOF。所以我在我的末尾添加了这个.xinitrc
:
while true; do sleep 10000; done
这样,如果我杀死我的 WM,X 就不会终止。现在我的问题是:我怎样才能进行无限睡眠而不是循环睡眠?有没有类似冻结脚本的命令?
sleep infinity
完全按照它的建议行事,并且在没有虐待猫的情况下工作。
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"
在读取时不使用循环有点草率,但是您可以随意重复使用它fifo
并read
使用 s 终止touch "$HOME/.pause.fifo"
(如果有多个读取等待,则立即终止所有)。
pause()
系统调用对于无限阻塞,有一个 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 ...
用于替换当前的 shell,这会释放一个 PID。解决方案也可以通过一些 IO 重定向来改进,释放未使用的 FD。这取决于您。)
这是如何工作ctypes.CDLL(None)
的(我认为):加载标准 C 库并pause()
在一些额外的循环中运行其中的函数。效率低于 C 版本,但有效。
保持循环睡眠。它很容易理解,非常便携,并且大部分时间都会阻塞。
也许这看起来很难看,但为什么不直接运行cat
并让它永远等待输入呢?
TL; DR:sleep infinity
实际上睡眠允许的最长时间,这是有限的。
想知道为什么这在任何地方都没有记录,我费心阅读GNU coreutils 的源代码,发现它大致执行如下:
strtod
from C stdlib 将 'infinity' 转换为双精度值。因此,假设 IEEE 754 双精度,64 位正无穷大值存储在seconds
变量中。xnanosleep(seconds)
(在 gnulib 中找到),这反过来又调用dtotimespec(seconds)
(也在 gnulib 中)从 todouble
转换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
is__kernel_time_t
和time_t
is 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_NANOSLEEP
被true
截断为 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.2s
POSIX 只允许正整数(例如sleep 0.5
,不允许)。FANCY_SLEEP
和编译FLOAT_DURATION
)。strtod
行为与 C 和 POSIX 兼容(即strtod("infinity", 0)
在符合 C99 的实现中始终有效,请参阅 §7.20.1.3)。sleep infinity
看起来最优雅,但有时由于某种原因它不起作用。在这种情况下,您可以尝试其他阻止命令,例如cat
、read
、tail -f /dev/null
等grep a
。
让我解释一下为什么sleep infinity
它没有记录在案。jp48的回答也很有用。
最重要的一点:通过指定inf
or 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()
将字符串转换为 类型的值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()
此函数将类型的参数转换为类型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;
}
给自己发送一个SIGSTOP怎么样?
这应该暂停进程,直到收到 SIGCONT。在你的情况下:从不。
kill -STOP "$$";
# grace time for signal delivery
sleep 60;
我最近需要这样做。我想出了以下函数,它可以让 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
这种方法不会消耗任何资源来保持进程处于活动状态。
while :; do :; done & kill -STOP $! && wait
while :; do :; done &
在后台创建一个虚拟进程kill -STOP $!
停止后台进程wait
等待后台进程,这将永远阻塞,导致后台进程之前停止与其杀死窗口管理器,不如尝试使用--replace
或运行新的窗口管理器(-replace
如果可用)。
while :; do read; done
无需等待孩子睡眠过程。