我使用startx
来启动X,在其中运行.xinitrc
。在我的.xinitrc
中,使用/usr/bin/mywm
启动了窗口管理器。现在,如果我杀死我的WM(例如为了测试其他WM),X也会终止,因为.xinitrc
脚本已经到达了EOF。
所以我在.xinitrc
的末尾添加了这个:
while true; do sleep 10000; done
这样,如果我结束我的WM,X就不会终止。现在我的问题是:如何进行无限睡眠而不是循环睡眠?是否有一种命令可以冻结脚本?
我使用startx
来启动X,在其中运行.xinitrc
。在我的.xinitrc
中,使用/usr/bin/mywm
启动了窗口管理器。现在,如果我杀死我的WM(例如为了测试其他WM),X也会终止,因为.xinitrc
脚本已经到达了EOF。
所以我在.xinitrc
的末尾添加了这个:
while true; do sleep 10000; done
这样,如果我结束我的WM,X就不会终止。现在我的问题是:如何进行无限睡眠而不是循环睡眠?是否有一种命令可以冻结脚本?
sleep infinity
恰如其名,而且不需要滥用cat。
sleep infinity
,尽管对于Linux来说这是一个很酷的东西值得了解。然而,while true; do sleep 86400; done
应该是一个足够好的替代方案。 - Ivan Xtimespec
,这意味着非常大的秒数(与计算机结构有关),但理论上是有限的。 - jp48tail
不会阻塞一如既往:对于任何问题,总有一个简短易懂、易于跟随但完全错误的答案。在这里,tail -f /dev/null
就属于这个范畴 ;)
如果你使用 strace tail -f /dev/null
查看它,你会发现这个解决方案远非阻塞!在 Linux 下,它甚至比问题中的 sleep
解决方案更糟糕,因为它使用了宝贵的资源,如 inotify
系统。同时,其他写入 /dev/null
的进程也会让 tail
循环。(在我的 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
并使用touch "$HOME/.pause.fifo"
使read
终止(如果有多个读取正在等待,则所有读取都会同时终止)。
pause()
系统调用对于无限阻塞,有一个名为pause()
的Linux系统调用可以实现我们想要的功能:永久等待(直到收到信号)。但是目前还没有用户空间程序可以使用它。
pause
,它可以无限期地暂停(需要像gcc
这样的C编译器,并使用diet
等工具来生成小型二进制文件):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版本高效,但仍能正常工作。
继续使用循环休眠。它易于理解,非常便携,并且大部分时间都会阻塞。
trap
(它会修改shell对信号的行为)或后台运行(允许shell拦截终端发送的信号,如 Strg+C)。 因此,只需使用 sleep infinity
就足够了(如果它是最后一个语句,则行为类似于 exec sleep infinity
。要查看差异,请使用 strace -ffDI4 bash -c 'YOURCODEHERE'
)。循环睡眠更好,因为在某些情况下,sleep
可能会返回。例如,您不希望 X11 在 killall sleep
上突然关闭,只是因为.xstartup
结束于 sleep infinity
而不是循环睡眠。 - Tino/bin/sleep
并不像你所说的那样被限制在24天。如果您能更新一下就太好了。目前在Linux上,这段代码是活跃的。它将单个的nanosleep()
系统调用限制为24天,但会在循环中调用它们。因此,sleep infinity
不应该在24天后退出。正无穷大的double
会被转换为struct timespec
。在GDB中查看rpl_nanosleep
,在Ubuntu 16.04上,infinity
会被转换为{ tv_sec = 9223372036854775807, tv_nsec = 999999999 }
。 - nh2sleep infinity
将会真正地休眠无限长的时间而不会循环执行:https://lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html - Vladimir Panteleevcat
并让它永远等待输入呢?cat
命令? mkfifo pipe && cat pipe
- Michał Trybussleep infinity
可以正常工作。之前(以及其他系统中),实现是实际上睡眠允许的最长时间,这是有限的。
想知道为什么这个没有被记录在任何地方,我花时间阅读了GNU coreutils的来源,发现它大致执行以下操作:
strtod
函数将第一个参数转换为双精度浮点数,以便将“无穷大”转换为双精度值。假设采用IEEE 754双精度表示法,则64位的正无穷大在seconds
变量中存储。xnanosleep(seconds)
函数,它进而调用gnulib中的dtotimespec(seconds)
函数,将其从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
,在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
导致的实际休眠时间为9223372036854775807秒(10^11年)。对于32位Linux系统(sizeof(long)
为4(32位)):2147483647秒(68年;另见year 2038 problem)。
编辑:显然,调用的nanoseconds
函数不是直接的系统调用,而是一个依赖于操作系统的包装器(也在gnulib中定义)。
结果多了一步:对于一些HAVE_BUG_BIG_NANOSLEEP
为true
的系统,睡眠时间被截断为24天,然后在循环中调用。这对于一些(或全部?)Linux发行版是适用的。请注意,如果configure-time测试成功,则可能不使用此包装器(源代码)。
特别地,这将是24 * 24 * 60 * 60 = 2073600秒
(加上999999999纳秒);但是这会在循环中调用以尊重指定的总睡眠时间。因此,先前的结论仍然有效。
cat
方法。
编辑:最近的GNU coreutils版本将尝试使用pause
系统调用(如果可用)而不是循环。当在Linux(可能还有BSD)中针对这些更新版本时,先前的参数就不再有效。
这是一个重要且有效的关注点:
sleep infinity
是 GNU coreutils 的扩展,不在 POSIX 中考虑。GNU 的实现也支持时间持续时间的“花式”语法,例如 sleep 1h 5.2s
,而 POSIX 只允许正整数(例如,sleep 0.5
不 允许)。FANCY_SLEEP
和 FLOAT_DURATION
进行编译)。strtod
行为与 C 和 POSIX 兼容(即在符合 C99 的实现中,strtod("infinity", 0)
总是有效的,参见 §7.20.1.3)。sleep infinity
现在将会真正地永久休眠而无需循环:https://lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html - Vladimir Panteleevsleep infinity
看起来最优雅,但有时由于某些原因它不起作用。在这种情况下,您可以尝试其他阻塞命令,例如 cat
, read
, tail -f /dev/null
, grep a
等。
tail -f /dev/null
的优点之一是不会占用标准输入。出于这个原因,我使用了它。 - Sudo Bashdouble 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]
转换为浮点数,并使用转换函数cl_strtod()
将其存储到*s
中。
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
家族HUGE_VAL
、HUGE_VALF
和HUGE_VALL
在§7.12-3中定义。
在§7.12.1-5中:宏
HUGE_VAL
展开为正的双精度常量表达式,不一定可表示为浮点数。宏
HUGE_VALF
HUGE_VALL
分别是HUGE_VAL
的float和long double模拟。
HUGE_VAL
、HUGE_VALF
和HUGE_VALL
可以是支持无穷大的实现中的正无穷大。
如果浮点结果溢出并且默认舍入生效,则函数根据返回类型返回宏
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),因此我们自然会假设类型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;
}
那么向自身发送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
等待后台进程,这将一直阻塞,因为后台进程已经被停止了wait
会恢复给定的进程。 - qoomonwait
命令中删除$!
来修复了我的命令。现在它在zsh
中也可以工作了。 - qoomon不要杀死窗口管理器,尝试使用--replace
或-replace
(如果可用)来运行新的窗口管理器。
--replace
,我总是会收到警告,如“另一个窗口管理器已经在运行”。但那对我来说没有太多意义。 - watain