time_namespaces - overview of Linux time namespaces
Time namespaces virtualize the values of two system clocks:
CLOCK_MONOTONIC (and likewise CLOCK_MONOTONIC_COARSE and CLOCK_MONOTONIC_RAW), a nonsettable clock that represents monotonic time since—as described by POSIX—"some unspecified point in the past".
CLOCK_BOOTTIME (and likewise CLOCK_BOOTTIME_ALARM), a nonsettable clock that is identical to CLOCK_MONOTONIC, except that it also includes any time that the system is suspended.
Thus, the processes in a time namespace share per-namespace values
for these clocks. This affects various APIs that measure against these
clocks, including: clock_gettime(2),
Currently, the only way to create a time namespace is by calling
unshare(2) with the CLONE_NEWTIME
flag. This call creates a new time namespace but does
the calling process in the new namespace. Instead, the calling process's
subsequently created children are placed in the new namespace. This
allows clock offsets (see below) for the new namespace to be set before
the first process is placed in the namespace. The
/proc/[pid]/ns/time_for_children symbolic link shows the time
namespace in which the children of a process will be created. (A process
can use a file descriptor opened on this symbolic link in a call to
setns(2) in order to move into the namespace.)
Associated with each time namespace are offsets, expressed with
respect to the initial time namespace, that define the values of the
monotonic and boot-time clocks in that namespace. These offsets are
exposed via the file
/proc/PID/timens_offsets. Within this
file, the offsets are expressed as lines consisting of three
<clock-id> <offset-secs> <offset-nanosecs>
clock-id is a string that identifies the clock whose
offsets are being shown. This field is either
CLOCK_BOOTTIME. The remaining fields express the offset
(seconds plus nanoseconds) for the clock in this time namespace. These
offsets are expressed relative to the clock values in the initial time
offset-secs value can be negative, subject to
restrictions noted below;
offset-nanosecs is an unsigned
In the initial time namespace, the contents of the
timens_offsets file are as follows:
$ cat /proc/self/timens_offsets monotonic 0 0 boottime 0 0
In a new time namespace that has had no member processes, the clock
offsets can be modified by writing newline-terminated records of the
same form to the
timens_offsets file. The file can be written
to multiple times, but after the first process has been created in or
has entered the namespace, write(2)s on this file fail
with the error EACCES. In order to write to the
timens_offsets file, a process must have the
CAP_SYS_TIME capability in the user namespace that owns
the time namespace.
Writes to the
timens_offsets file can fail with the
offset-nanosecs value is greater than 999,999,999.
clock-id value is not valid.
The caller does not have the CAP_SYS_TIME capability.
offset-secs value is out of range. In particular;
offset-secs can't be set to a value which would make the
current time on the corresponding clock inside the namespace a negative
offset-secs can't be set to a value such that the time
on the corresponding clock inside the namespace would exceed half of the
value of the kernel constant KTIME_SEC_MAX (this limits
the clock value to a maximum of approximately 146 years).
In a new time namespace created by unshare(2), the
contents of the
timens_offsets file are inherited from the time
namespace of the creating process.
Use of time namespaces requires a kernel that is configured with the CONFIG_TIME_NS option.
Note that time namespaces do not virtualize the CLOCK_REALTIME clock. Virtualization of this clock was avoided for reasons of complexity and overhead within the kernel.
For compatibility with the initial implementation, when writing a
clock-id to the
/proc/[pid]/timens_offsets file, the
numerical values of the IDs can be written instead of the symbolic names
show above; i.e., 1 instead of
monotonic, and 7 instead of
boottime. For redability, the use of the symbolic names over
the numbers is preferred.
The motivation for adding time namespaces was to allow the monotonic and boot-time clocks to maintain consistent values during container migration and checkpoint/restore.
The following shell session demonstrates the operation of time namespaces. We begin by displaying the inode number of the time namespace of a shell in the initial time namespace:
$ readlink /proc/$$/ns/time time:
$ uptime --pretty up 21 hours, 17 minutes $ ./clock_times CLOCK_REALTIME : 1585989401.971 (18356 days + 8h 36m 41s) CLOCK_TAI : 1585989438.972 (18356 days + 8h 37m 18s) CLOCK_MONOTONIC: 56338.247 (15h 38m 58s) CLOCK_BOOTTIME : 76633.544 (21h 17m 13s)
We then use unshare(1) to create a time namespace
and execute a bash(1) shell. From the new shell, we use
the built-in echo command to write records to the
timens_offsets file adjusting the offset for the
CLOCK_MONOTONIC clock forward 2 days and the offset for
the CLOCK_BOOTTIME clock forward 7 days:
$ PS1="ns2# " sudo unshare -T -- bash --norc ns2# echo "monotonic $((2*24*60*60)) 0" > /proc/$$/timens_offsets ns2# echo "boottime $((7*24*60*60)) 0" > /proc/$$/timens_offsets
Above, we started the bash(1) shell with the
--norc options so that no start-up scripts were
executed. This ensures that no child processes are created from the
shell before we have a chance to update the
We then use cat(1) to display the contents of the
timens_offsets file. The execution of cat(1)
creates the first process in the new time namespace, after which further
attempts to update the
timens_offsets file produce an
ns2# cat /proc/$$/timens_offsets monotonic 172800 0 boottime 604800 0 ns2# echo "boottime $((9*24*60*60)) 0" > /proc/$$/timens_offsets bash: echo: write error: Permission denied
Continuing in the new namespace, we execute
uptime(1) and the
ns2# uptime --pretty up 1 week, 21 hours, 18 minutes ns2# ./clock_times CLOCK_REALTIME : 1585989457.056 (18356 days + 8h 37m 37s) CLOCK_TAI : 1585989494.057 (18356 days + 8h 38m 14s) CLOCK_MONOTONIC: 229193.332 (2 days + 15h 39m 53s) CLOCK_BOOTTIME : 681488.629 (7 days + 21h 18m 8s)
From the above output, we can see that the monotonic and boot-time clocks have different values in the new time namespace.
/proc/[pid]/ns/time_for_children symbolic links, we see that
the shell is a member of the initial time namespace, but its children
are created in the new namespace.
ns2# readlink /proc/$$/ns/time time: ns2# readlink /proc/$$/ns/time_for_children time: ns2# readlink /proc/self/ns/time # Creates a child process time:
Returning to the shell in the initial time namespace, we see that the
monotonic and boot-time clocks are unaffected by the
timens_offsets changes that were made in the other time
$ uptime --pretty up 21 hours, 19 minutes $ ./clock_times CLOCK_REALTIME : 1585989401.971 (18356 days + 8h 38m 51s) CLOCK_TAI : 1585989438.972 (18356 days + 8h 39m 28s) CLOCK_MONOTONIC: 56338.247 (15h 41m 8s) CLOCK_BOOTTIME : 76633.544 (21h 19m 23s)
This page is part of release 5.10 of the Linux
project. A description of the project, information about reporting bugs,
and the latest version of this page, can be found at