让我解释一下为什么sleep infinity 有效,尽管它没有记录。 jp48's answer 也很有用。
最重要的是:通过指定inf 或infinity(均不区分大小写),您可以在实现允许的最长时间内休眠(即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 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
分别是 HUGE_VAL 的 float 和 long double 类似物。
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。所有的cmets都是我加的。
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;
}