1. Introduction
How long a given system has been running and its overall age can be helpful metrics. While the latter starts as soon as the operating system (OS) is installed, the current runtime, also called uptime, is usually measured from a given point in time around the last system boot.
In this tutorial, we explore how Linux calculates the basis for the uptime value of the system. First, we go over duration-since-boot times from several commands. After that, we check how they differ from the origin of the main tool to check uptime. Finally, we delve into the way the Linux kernel calculates and provides an interface to the uptime epoch.
Importantly, any part of the kernel source code can change without notice, so we aim to generalize the concepts while broadly tracing functions and definitions.
We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments unless otherwise specified.
2. Boot Times
The uptime command returns the length of time that a system has been running:
$ uptime
06:56:00 up 20 days, 20:23, 2 users, load average: 0.00, 0.00, 0.00
By definition, the uptime length (after up) is the difference between the boot time and the current moment (first column, 06:56:00) of the current date.
Yet, what does boot time mean in this case?
2.1. Using last
For example, a successful boot might mean that a user has logged in. We can check the login times as stored in /var/run/utmp and /var/log/wtmp via the last command:
$ last
root tty2 Fri Jun 16 06:56 still logged in
baeldung pts/1 192.168.66.6 Fri Jun 16 03:56 - 03:59 (00:03)
reboot system boot 5.10.0-23-amd64 Fri Jun 16 00:10 still running
[...]
root tty2 Thu Jul 17 07:17 - down (00:02)
reboot system boot 5.10.0-18-amd64 Thu Jul 17 07:17 - 07:21 (00:03)
wtmp begins Sun Jul 17 07:17:07 2022
Notably, last not only shows us the login but also the boot times. So, we might assume those to be a good approximation. In fact, the original standard UNIX reads /var/adm/utmpx (similar to /var/run/utmp) and checks for the time of the last reboot.
2.2. Universal Uptime Origin
Still, we get the same data from uptime and commands like top and htop:
$ uptime
06:56:00 up 20 days, 20:23, 2 users, load average: 0.00, 0.00, 0.00
$ top --iteration=1 | head --lines=1
top - 06:56:00 up 20 days, 20:23, 2 users, load average: 0.0
Here, we see the equivalence between the output of uptime and the first line of top as returned for 1 of its –iterations (-n) by head.
Considering this fact, we expect to see a universal source for this particular metric.
3. uptime –since
Our first clue to the uptime epoch comes from the –since or -s flag of uptime:
$ uptime --since
2023-06-16 00:10:10
Running this command, we only see –since when uptime considers the system to be up.
Now, we can perform a comparison between this timestamp and the output of last, for example:
$ last --time-format full | grep 'system boot' | head --lines=1
reboot system boot 5.10.0-23-amd64 Fri Jun 16 00:10:11 2023 still running
In this case, we use a pipeline to show only the row of the last system boot with a full –time-format:
- last, same as before, outputs all entries, but with all available time data
- grep takes the output of last and shows only system boot lines
- head ensures we only see the first line via –lines=1, i.e., the last one chronologically
Notably, there is a discrepancy in the seconds portion of the two times, so these commands don’t seem to get their data from the same source.
4. Uptime Internals and System Boot Time
After getting to know some initial times as well as how they compare, let’s see how uptime gets its time and, in general, how Linux sets the beginning of a boot.
4.1. uptime Tracing
So, to see how uptime actually acquires the initial times and calculates durations, we can use strace:
$ strace -eopen -eopenat uptime
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[...]
openat(AT_FDCWD, "/proc/self/auxv", O_RDONLY) = 3
openat(AT_FDCWD, "/proc/sys/kernel/osrelease", O_RDONLY) = 3
openat(AT_FDCWD, "/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/proc/self/auxv", O_RDONLY) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/proc/uptime", O_RDONLY) = 3
openat(AT_FDCWD, "/var/run/utmp", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 4
06:56:00 up 20 days, 20:23, 2 users, load average: 0.00, 0.00, 0.00
+++ exited with 0 +++
We use -e to filter for both the open() and openat() calls. Omitting the .so libraries from the output, we end up with a number of files from the /proc and /sys pseudo-filesystems.
In particular, one file stands out: /proc/uptime. By definition, /proc/uptime contains the system uptime in seconds. How did this information get there?
4.2. /proc/uptime
As with most core and system functions, uptime tracking needs the kernel to function accurately.
Hence, there is some fairly straightforward code for the kernel uptime support in the fs/proc/uptime.c file of the kernel sources. In particular, let’s explore the proc_uptime_init() function definition in uptime.c:
static int __init proc_uptime_init(void)
{
struct proc_dir_entry *pde;
pde = proc_create_single("uptime", 0, NULL, uptime_proc_show);
pde_make_permanent(pde);
return 0;
}
Taking no parameters, this function only calls proc_create_single(), which creates the actual /proc/uptime file and associates the uptime_proc_show() function to its open() and read() operations.
4.3. Kernel Uptime Support
In short, uptime_proc_show() from uptime.c is linked with /proc/uptime and calls ktime_get_boottime_ts64(), which in turn uses ktime_get_boottime() for the actual boot time.
Since this time is relative to the kernel cycles, looking at the code, we see uptime starts when the kernel is initialized, right after the bootloader gives up control and the kernel image takes over.
5. Summary
In this article, we explored the source of the uptime command time baseline, as well as the general Linux boot time start.
In conclusion, although they may have been equivalent in the past, uptime epoch is now different from system boot entries in the /var/run/utmp, /var/log/wtmp, and the older /var/adm/utmpx log files.