时间命名空间应该如何使用?

JoL*_*JoL 6 linux clock namespace unshare

我想我可以做这样的事情:

\n
sudo unshare -T bash -c \'date -s "$1" && foobar\' sh "$(date -d -1day)"\n
Run Code Online (Sandbox Code Playgroud)\n

所以foobar会看到与系统其他部分不同的系统时间。但是,似乎不包含系统时间的变化。它改变了整个系统的系统时间。

\n

这篇 LWN 文章似乎表明这个命名空间是为了我试图赋予它的用途而设计的。

\n
\n

当在根时间命名空间之外调用时,调整系统时间的系统调用将调整特定于命名空间的偏移量。

\n
\n

看着strace date -s ...,我看到了其他输出:

\n
clock_settime(CLOCK_REALTIME, {tv_sec=1619044910, tv_nsec=0}) = 0\n
Run Code Online (Sandbox Code Playgroud)\n

然而,阅读time_namespaces(7)

\n
\n

这会影响针对这些时钟进行测量的各种 API,包括:clock_gettime(2)、clock_nanosleep(2)、nanosleep(2)、timer_settime(2)、timerfd_settime(2) 和 /proc/uptime。

\n
\n

我看到它没有提到clock_settime(2)。“包括”一词告诉我这可能不是完整的列表,但也许是。

\n

我也不懂--boottime/ --monotonic。看着clock_settime(2),我看到:

\n
\n

CLOCK_MONOTONIC 不可设置的系统范围时钟,表示自 \xe2\x80\x94 以来的单调时间,如 POSIX\xe2\x80\x94 所描述的“过去的某个未指定点”。在 Linux 上,该点对应于系统自启动以来运行的秒数。

\n
\n
\n

CLOCK_BOOTTIME(自 Linux 2.6.39 起;特定于 Linux)不可设置的系统范围时钟,与 CLOCK_MONOTONIC 相同,但它还包括系统挂起的任何时间。

\n
\n

然而,当尝试它们时,它们似乎并没有改变uptime

\n
$ uptime -s\n2021-04-10 10:30:45\n$ sudo unshare -T --boottime 1000000000 uptime -s\n2021-04-10 10:30:45\n$ sudo unshare -T --monotonic 1000000000 uptime -s\n2021-04-10 10:30:45\n$ sudo unshare -T --boottime -100000 uptime -s\n2021-04-10 10:30:45\n$ sudo unshare -T --monotonic -100000 uptime -s\n2021-04-10 10:30:45\n
Run Code Online (Sandbox Code Playgroud)\n

我从中看到strace uptime它读取/proc/uptime而不是调用clock_gettime(2),并且/proc/uptime似乎不受unshare调用及其偏移量的影响,尽管文档time_namespaces(7)说它会影响/proc/uptime我上面引用的内容。

\n

这个命名空间应该如何使用?我似乎找不到任何会受到影响的命令unshare --time

\n

LL3*_*LL3 5

我认为您的推理中有三个要点需要澄清:

\n
\n

第一个是取消共享时间命名空间会影响从那时由调用unshare(2). 调用过程本身不受影响。这有点像 PID 命名空间,但与迄今为止的其他命名空间类型不同。然而,调用进程仍然可以进入新创建的时间命名空间,只是如果它想这样做,那么它也必须setns(2)(即nsenter(1)用 CLI 的说法)自己进入其中。

\n

所有这些意味着unshare -T您一直在运行的命令从未真正将这些命令移动到新创建的时间命名空间中。您只需添加-f选项即可unshare(1)使其将指定的命令作为其子命令而不是execve(2)其自身运行。这样指定的命令将存在于该时间命名空间中。

\n

当然,正如您所做的正确一样,您还需要指定--boottime和/或--monotonic选项来“扭曲”时间命名空间对这些时钟的视觉,否则子时间命名空间将具有与它的父级。

\n

因此,总而言之,以您的尝试为例,在我的机器上:

\n
$ sudo unshare -T --boottime 1000000000 uptime -s\n2021-04-23 11:07:10\n$ sudo unshare -fT --boottime 1000000000 uptime -s\n1989-08-15 09:20:30\n$\n
Run Code Online (Sandbox Code Playgroud)\n

除了使用这些方便的选项之外,您还可以/proc/self/timens_offsets手动设置文件,只要在生成任何子项之前执行此操作即可。与上面的“手动”等效的内容类似于:

\n
$ sudo unshare -T dash -c \'echo "boottime 1000000000 0" > /proc/self/timens_offsets; uptime -s\'\n1989-08-15 09:20:30\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我dash只是使用一个更精简的外壳,它肯定不会为其自己的引导程序生成子级,并且还具有内置回显(以免为其生成子级)来设置自己的timens_offsets文件。从那时起,运行的所有后续命令都dash将看到“扭曲的”启动时间。

\n

但如果您愿意这样做,则不会exec uptime -s,因为这会替换dash\xc2\xa0withuptime因此仍然存在于父时间命名空间中,除非nsenter(1)事先也这样做了。考虑:

\n
$ sudo unshare -T dash -c \'echo "boottime 1000000000 0" > /proc/self/timens_offsets; exec uptime -s\'\n2021-04-23 11:07:10\n$ sudo unshare -T dash -c \'echo "boottime 1000000000 0" > /proc/self/timens_offsets; exec nsenter --time=/proc/self/ns/time_for_children uptime -s\'\n1989-08-15 09:20:30\n$\n
Run Code Online (Sandbox Code Playgroud)\n
\n

我认为需要澄清的第二点date是具体关于该命令的。

\n

请注意,仅从内核 v5.11(以及当前最新的 v5.12-rc8)开始CLOCK_BOOTIME,并且CLOCK_MONOTONIC可以在新的时间命名空间中“扭曲”,正如以下注释中所述time_namespaces(7)

\n
\n

请注意,时间命名空间不会虚拟化 CLOCK_REALTIME\n时钟。由于内核内的复杂性和开销的原因,避免了该时钟的虚拟化。

\n
\n

但是,正如您从命令中可以注意到的那样,date它专门作用于时钟。这意味着,在我写这篇文章时,该命令仍然不受它所在的时间命名空间的影响。CLOCK_REALTIMEstrace date -s ...date

\n

确实受时间命名空间影响的命令的一个简单示例是,因为它引用CLOCK_MONOTONICdmesg。尝试类似的方法:

\n
$ sudo unshare -fT --monotonic 1000000000 dmesg -T\n
Run Code Online (Sandbox Code Playgroud)\n
\n

第三点似乎是关于这句话:

\n
\n

当在根时间命名空间之外调用时,调整系统时间的系统调用将调整特定于命名空间的偏移量。

\n
\n

不可否认,这有点误导,因为(目前)显然不可能任意更改时间命名空间之后的时间视图,仅仅因为 和CLOCK_MONOTONIC实际上CLOCK_BOOTTIME都是不可更改的。这两个时钟的意思是更改的。

\n

因此,唯一允许的操作是在任何进程加入该时间命名空间之前将它们“引导”到某个偏移量(与初始时间命名空间相关) ,以便任何进程都不会经历这两个时钟的跳跃(甚至不是向前)。

\n

这就是为什么unshare(2)不将调用进程移动到新创建的时间命名空间中的原因:这样它(或其他进程)就有机会指定“引导”偏移量,实际上在任何一个进程进入时间命名空间后都无法更改该偏移量。时间命名空间。计算正确的偏移量显然是一个微妙的操作,这是“命名空间管理器”的工作。

\n