Fastest Way to Get System Uptime in Python in Linux

Fastest way to get system uptime in Python in Linux

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

get system uptime with Python on a Raspberry Pi

I found another solution: https://www.raspberrypi.org/forums/viewtopic.php?t=164276

#!/usr/bin/python3

import shlex, subprocess
cmd = "uptime -p"
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE)
output = p.communicate()

Thanks for your posted ideas.
print (output)

How to retrieve the process start time (or uptime) in python

If you are doing it from within the python program you're trying to measure, you could do something like this:

import time
# at the beginning of the script
startTime = time.time()
# ...
def getUptime():
"""
Returns the number of seconds since the program started.
"""
# do return startTime if you just want the process start time
return time.time() - startTime

Otherwise, you have no choice but to parse ps or go into /proc/pid. A nice bashy way of getting the elapsed time is:

ps -eo pid,etime | grep $YOUR_PID | awk '{print $2}'

This will only print the elapsed time in the following format, so it should be quite easy to parse:

days-HH:MM:SS

(if it's been running for less than a day, it's just HH:MM:SS)

The start time is available like this:

ps -eo pid,stime | grep $YOUR_PID | awk '{print $2}'

Unfortunately, if your process didn't start today, this will only give you the date that it started, rather than the time.

The best way of doing this is to get the elapsed time and the current time and just do a bit of math. The following is a python script that takes a PID as an argument and does the above for you, printing out the start date and time of the process:

import sys
import datetime
import time
import subprocess

# call like this: python startTime.py $PID

pid = sys.argv[1]
proc = subprocess.Popen(['ps','-eo','pid,etime'], stdout=subprocess.PIPE)
# get data from stdout
proc.wait()
results = proc.stdout.readlines()
# parse data (should only be one)
for result in results:
try:
result.strip()
if result.split()[0] == pid:
pidInfo = result.split()[1]
# stop after the first one we find
break
except IndexError:
pass # ignore it
else:
# didn't find one
print "Process PID", pid, "doesn't seem to exist!"
sys.exit(0)
pidInfo = [result.split()[1] for result in results
if result.split()[0] == pid][0]
pidInfo = pidInfo.partition("-")
if pidInfo[1] == '-':
# there is a day
days = int(pidInfo[0])
rest = pidInfo[2].split(":")
hours = int(rest[0])
minutes = int(rest[1])
seconds = int(rest[2])
else:
days = 0
rest = pidInfo[0].split(":")
if len(rest) == 3:
hours = int(rest[0])
minutes = int(rest[1])
seconds = int(rest[2])
elif len(rest) == 2:
hours = 0
minutes = int(rest[0])
seconds = int(rest[1])
else:
hours = 0
minutes = 0
seconds = int(rest[0])

# get the start time
secondsSinceStart = days*24*3600 + hours*3600 + minutes*60 + seconds
# unix time (in seconds) of start
startTime = time.time() - secondsSinceStart
# final result
print "Process started on",
print datetime.datetime.fromtimestamp(startTime).strftime("%a %b %d at %I:%M:%S %p")

Getting the linux time since power on

you can use "tuptime| grep -i 'system life'" in the cmd instead of "tuptime"

How can I get an equivalent of Python threading.Timer that uses system uptime?

Update

What I am doing now is to monkey patch threading._time with a monotonic function from the monotonic package on PyPI.

import threading
import monotonic

threading._time = monotonic.monotonic

Original answer

I ended up extending threading.Timer to use system uptime.

class Timer(threading._Timer):

def __init__(self, *args, **kwargs):
super(Timer, self).__init__(*args, **kwargs)

# only works on Linux
self._libc = ctypes.CDLL('libc.so.6')
self._buf = ctypes.create_string_buffer(128)

def uptime(self):
self._libc.sysinfo(self._buf)
return struct.unpack_from('@l', self._buf.raw)[0]

def run(self):
start_time = self.uptime()
while not self.finished.is_set():
time.sleep(0.1)
if self.uptime() - start_time > self.interval:
self.function(*self.args, **self.kwargs)
break
self.finished.set()

How do I get Linux system's up-time?

#include <stdio.h>
#include <sys/sysinfo.h>

int main(void)
{

struct sysinfo sys_info;
sysinfo(&sys_info);
printf("Seconds since uptime: %lu\n", sys_info.uptime);

return 0;
}

This should get you started. If you need more help, type man sysinfo into the terminal.

What API do I call to get the system uptime?

The system call you're looking for is sysinfo().

It's defined in sys/sysinfo.h

Its signature is:
int sysinfo(struct sysinfo *info)

Since kernel 2.4, the structure has looked like this:

struct sysinfo {
long uptime; /* Seconds since boot */
unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
unsigned long totalram; /* Total usable main memory size */
unsigned long freeram; /* Available memory size */
unsigned long sharedram; /* Amount of shared memory */
unsigned long bufferram; /* Memory used by buffers */
unsigned long totalswap; /* Total swap space size */
unsigned long freeswap; /* swap space still available */
unsigned short procs; /* Number of current processes */
unsigned long totalhigh; /* Total high memory size */
unsigned long freehigh; /* Available high memory size */
unsigned int mem_unit; /* Memory unit size in bytes */
char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding for libc5 */
};

Have fun!

Bash format uptime to show days, hours, minutes

My uptime produces output that looks like:

$ uptime
12:49:10 up 25 days, 21:30, 28 users, load average: 0.50, 0.66, 0.52

To convert that to your format:

$ uptime | awk -F'( |,|:)+' '{print $6,$7",",$8,"hours,",$9,"minutes."}'
25 days, 21 hours, 34 minutes.

How it works

  • -F'( |,|:)+'

    awk divides its input up into fields. This tells awk to use any combination of one or more of space, comma, or colon as the field separator.

  • print $6,$7",",$8,"hours,",$9,"minutes."

    This tells awk to print the sixth field and seventh fields (separated by a space) followed by a comma, the 8th field, the string hours, the ninth field, and, lastly, the string minutes..

Handling computers with short uptimes using sed

Starting from a reboot, my uptime produces output like:

 03:14:20 up 1 min,  2 users,  load average: 2.28, 1.29, 0.50
04:12:29 up 59 min, 5 users, load average: 0.06, 0.08, 0.48
05:14:09 up 2:01, 5 users, load average: 0.13, 0.10, 0.45
03:13:19 up 1 day, 0 min, 8 users, load average: 0.01, 0.04, 0.05
04:13:19 up 1 day, 1:00, 8 users, load average: 0.02, 0.05, 0.21
12:49:10 up 25 days, 21:30, 28 users, load average: 0.50, 0.66, 0.52

The following sed command handles these formats:

uptime | sed -E 's/^[^,]*up *//; s/, *[[:digit:]]* users.*//; s/min/minutes/; s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/' 

With the above times, this produces:

1 minutes
59 minutes
2 hours, 1 minutes
1 day, 0 minutes
1 day, 1 hours, 0 minutes
25 days, 21 hours, 30 minutes

How it works

  • -E turns on extended regular expression syntax. (On older GNU seds, use -r in place of -E)

  • s/^[^,]*up *//

    This substitutes command removes all text up to up.

  • s/, *[[:digit:]]* users.*//

    This substitute command removes the user count and all text which follows it.

  • s/min/minutes/

    This replaces min with minutes.

  • s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/'

    If the line contains a time in the hh:mm format, this separates the hours from the minutes and replaces it with hh hours, mm minutes.

Handling computers with short uptimes using awk

uptime | awk -F'( |,|:)+' '{d=h=m=0; if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}'

On the same test cases as above, this produces:

0 days, 0 hours, 1 minutes.
0 days, 0 hours, 59 minutes.
0 days, 2 hours, 1 minutes.
1 days, 0 hours, 0 minutes.
1 days, 1 hours, 0 minutes.
25 days, 21 hours, 30 minutes.

For those who prefer awk code spread out over multiple lines:

uptime | awk -F'( |,|:)+' '{
d=h=m=0;
if ($7=="min")
m=$6;
else {
if ($7~/^day/) { d=$6; h=$8; m=$9}
else {h=$6;m=$7}
}
}
{
print d+0,"days,",h+0,"hours,",m+0,"minutes."
}'


Related Topics



Leave a reply



Submit