kfx
kfx

Reputation: 8537

Fastest way to get system uptime in Python in Linux

I'm looking for a fast and lightweight way to read system uptime from a Python script. Is there a way to call the sysinfo Linux system call from Python?

So far I've found two other methods for measuring uptime, one that involves running an external processes and one that involves reading a file in /proc.

import subprocess

def uptime1():
    raw = subprocess.check_output('uptime').decode("utf8").replace(',', '')
    days = int(raw.split()[2])
    if 'min' in raw:
        hours = 0
        minutes = int(raw[4])
    else:
        hours, minutes = map(int,raw.split()[4].split(':'))
    totalsecs = ((days * 24 + hours) * 60 + minutes) * 60
    return totalsecs

def uptime2():  
    with open('/proc/uptime', 'r') as f:
        uptime_seconds = float(f.readline().split()[0])
        return uptime_seconds

When comparing the speed, the second method is around 50 times faster. Still, calling a system call directly should be yet another order of magnitude better.

>> import timeit
>> print(timeit.timeit('ut.uptime1()', setup="import uptimecalls as ut", number=1000))
1.7286969429987948
>> print(timeit.timeit('ut.uptime2()', setup="import uptimecalls as ut", number=1000))
0.03355383600865025

Upvotes: 31

Views: 49607

Answers (8)

user149408
user149408

Reputation: 5901

If you want your code to be portable and not require any dependencies from PyPI, you pretty much got it – except your code doesn’t handle all possible output formats from uptime and will break if uptime is less than a day. Here’s an improved version which has been tested on Linux as well as FreeBSD:

def uptime():
    raw = subprocess.check_output('uptime').decode("utf8").replace(',', '')
    # We need to correctly process all forms `raw` can take, including:
    # 12:20  up 20 mins, 1 user, load averages: 0.67, 0.60, 0.48
    # 11:52  up 23:59, 2 users, load averages: 0.64, 0.59, 0.56
    # 11:53  up 1 day, 0 sec, 2 users, load averages: 0.49, 0.55, 0.54
    # 11:54  up 1 day, 1 min, 2 users, load averages: 0.40
    # 11:55  up 1 day, 2 mins, 2 users, load averages: 0.56, 0.52, 0.53
    # 11:52  up 1 day, 23:59, 2 users, load averages: 0.64, 0.59, 0.56
    # 11:53  up 2 days, 0 sec, 2 users, load averages: 0.49, 0.55, 0.54
    # 11:54  up 2 days, 1 min, 2 users, load averages: 0.40, 0.51, 0.53
    # 11:52  up 2 days, 23:59, 2 users, load averages: 0.64, 0.59, 0.56
    if 'day' in raw:
        i_hhmmss = 4
        days = int(raw.split()[2])
    else:
        i_hhmmss = 2
        days = 0
    if 'sec' in raw:
        hours = 0
        minutes = 0
        seconds = int(raw.split()[i_hhmmss])
    elif 'min' in raw:
        hours = 0
        minutes = int(raw.split()[i_hhmmss])
        seconds = 0
    else:
        hours, minutes = map(int,raw.split()[i_hhmmss].split(':'))
        seconds = 0
    totalsecs = ((days * 24 + hours) * 60 + minutes) * 60 + seconds
    return totalsecs

Upvotes: 0

JuniorCompressor
JuniorCompressor

Reputation: 20025

You can try installing psutil with:

pip install psutil

and then use the following fragment of code:

import psutil
import time


def seconds_elapsed():
    return time.time() - psutil.boot_time()


print(seconds_elapsed())

Upvotes: 30

0xDEC0DE
0xDEC0DE

Reputation: 363

Expanding on the previous, this will get you the system uptime as a datetime object:

from datetime import datetime, timedelta

def uptime():
    with open("/proc/uptime", "rb") as f:
        seconds = float(f.readline().split(maxsplit=1)[0])
        return datetime.now() - timedelta(seconds=seconds))

Upvotes: 1

Andrew Hodel
Andrew Hodel

Reputation: 35

import os
print(os.times()[4])

python 2

os.times()
Return a 5-tuple of floating point numbers indicating accumulated (processor or other) times, in seconds. The items are: user time, system time, children’s user time, children’s system time, and elapsed real time since a fixed point in the past, in that order. See the Unix manual page times(2) or the corresponding Windows Platform API documentation. On Windows, only the first two items are filled, the others are zero.

https://docs.python.org/2/library/os.html

python 3

os.times()
Returns the current global process times. The return value is an object with five attributes:

user - user time

system - system time

children_user - user time of all child processes

children_system - system time of all child processes

elapsed - elapsed real time since a fixed point in the past

For backwards compatibility, this object also behaves like a five-tuple containing user, system, children_user, children_system, and elapsed in that order.

See the Unix manual page times(2) and times(3) manual page on Unix or the GetProcessTimes MSDN on Windows. On Windows, only user and system are known; the other attributes are zero.

Availability: Unix, Windows.

Changed in version 3.3: Return type changed from a tuple to a tuple-like object with named attributes.

https://docs.python.org/3/library/os.html

Upvotes: -1

j123b567
j123b567

Reputation: 3439

TL;DR

Simple, but not guaranteed to be uptime. Should represent uptime on Linux and Windows Vista+, python 3.3+

time.monotonic()

https://docs.python.org/3/library/time.html#time.monotonic

More correct linux specific, real equivalent to linux uptime, python 3.7+

time.clock_gettime(time.CLOCK_BOOTTIME)

https://docs.python.org/3/library/time.html#time.clock_gettime https://docs.python.org/3/library/time.html#time.CLOCK_BOOTTIME


Long answer

Nobody mention simple monotonic clock. POSIX specifies CLOCK_MONOTONIC to be monotonic clock from unspecified time but Linux specifies it to start at boot

On Linux, python uses CLOCK_MONOTONIC in time.monotonic()

So getting uptime can be as simple as calling

import time
print(time.monotonic())

Fortunately, this will also work for many other platforms as uptime (e.g. on Windows, it uses GetTickCount64)

Problem with CLOCK_MONOTONIC is that it does not count deep sleep. So you can use CLOCK_BOOTTIME instead. This works on Linux kernel from 2.6.39 and python from 3.7. /proc/uptime uses ktime_get_boottime internally which is CLOCK_BOOTTIME time scale. So 1:1 replacement is this.

import time
print(time.clock_gettime(time.CLOCK_BOOTTIME))

Just for comparison of the speed, it is also the fastest. uptime1 does not work for me so testing only other variants

import time
import ctypes
import struct
libc = ctypes.CDLL('libc.so.6')

def uptime2():  
    with open('/proc/uptime', 'r') as f:
        uptime_seconds = float(f.readline().split()[0])
        return uptime_seconds

def uptime3():
    buf = ctypes.create_string_buffer(128)
    if libc.sysinfo(buf) != 0:
        return None

    uptime = struct.unpack_from('@l', buf.raw)[0]
    return uptime

uptime4 = time.monotonic

uptime5 = lambda: time.clock_gettime(time.CLOCK_BOOTTIME)
>>> print(timeit.timeit('ut.uptime2()', setup="import uptimecalls as ut", number=10000))
0.2356316399964271
>>> print(timeit.timeit('ut.uptime3()', setup="import uptimecalls as ut", number=10000))
0.05088816500210669
>>> print(timeit.timeit('ut.uptime4()', setup="import uptimecalls as ut", number=10000))
0.003676328000437934
>>> print(timeit.timeit('ut.uptime5()', setup="import uptimecalls as ut", number=10000))
0.012754358001984656

So it is also the fastest solution.

Upvotes: 19

Lahiru Jayakody
Lahiru Jayakody

Reputation: 5410

Adding an UP TO DATE answer.

This may not be the fastest way. But this is should be the replacement for psutil.boot_time() since I couldn't find boot_time in latest versions of linux psutil lib.

Dependancy:

pip3 install uptime

Usage:

>>> from uptime import uptime
>>> uptime()
49170.129999999997

More info

Upvotes: 8

vpetersson
vpetersson

Reputation: 1750

This frankly seems like a much better solution:

def get_uptime():
    with open('/proc/uptime', 'r') as f:
        uptime_seconds = float(f.readline().split()[0])

    return uptime_seconds

It also has the added benefit of not requiring any additional modules.

Credits: Source

Upvotes: 38

tdelaney
tdelaney

Reputation: 77407

I don't think you can get much faster than using ctypes to call sysinfo() but in my tests, its slower than /proc. Those linux system programmers seem to know what they are doing!

import ctypes
import struct

def uptime3():
    libc = ctypes.CDLL('libc.so.6')
    buf = ctypes.create_string_buffer(4096) # generous buffer to hold
                                            # struct sysinfo
    if libc.sysinfo(buf) != 0:
        print('failed')
        return -1

    uptime = struct.unpack_from('@l', buf.raw)[0]
    return uptime

Running your two tests plus mine on my slow laptop, I got:

>>> print(timeit.timeit('ut.uptime1()', setup="import uptimecalls as ut", number=1000))
5.284219555993332
>>> print(timeit.timeit('ut.uptime2()', setup="import uptimecalls as ut", number=1000))
0.1044210599939106
>>> print(timeit.timeit('ut.uptime3()', setup="import uptimecalls as ut", number=1000))
0.11733305400412064

UPDATE

Most of the time is spent pulling in libc and creating the buffer. If you plan to make the call repeatedly over time, then you can pull those steps out of the function and measure just the system call. In that case, this solution is the clear winner:

uptime1: 5.066633300986723
uptime2: 0.11561189399799332
uptime3: 0.007740753993857652

Upvotes: 12

Related Questions