How to monitor CPU utilization using POSIX clock() function during runtime?
Is there any reliable method how a program can measure its CPU utilization during its runtime?
I probably have to use a POSIX clock() function from time.h. The idea is first to use some milliseconds to set an idle CPU load (step A), then full CPU load (step B), then to start a program and call clock() constantly. Thus, I can calculate CPU utilization relative to a calculated on step A and step B to monitor a CPU utilization in th开发者_StackOverflowe percentage. I assume that all other background processes are ignored.
However, I am not sure, how to implement these step A and step B, say idle() and full_load() functions properly using only C89 and POSIX?
When you say "full_load", and you only need a single CPU or virtual core under load, a simple tight loop will do the trick. Granted, it won't use all the transistors on the chip (i.e., we're not talking about a burn-in test for "full-load"), but it will, for the scheduled time-slice it gets of the CPU, use all the available clock-cycles with no syscalls that would give up control to the kernel and possibly cause the executing thread to be re-scheduled for later. Also you could use an alarm with a signal handler in order to exit from the loop. So that would let you run the loop for approximately a second of execution time (alarms aren't exactly time-accurate ... they're close, but not down to the clock-cycle).
In addition, for the "idle" load portion, you could do the same thing, but using a sigsuspend()
instead of a tight loop, that would wait for the alarm to go off.
So your code could look something like the following:
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
static sig_atomic_t alarm_flag = 1;
void alarm_handler(int arg)
{
alarm_flag = 0;
}
clock_t idle()
{
//setup the alarm flag
alarm_flag = 1;
//setup the signal masks
sigset_t old_signal_set;
sigset_t new_signal_set;
sigemptyset(&old_signal_set);
sigemptyset(&new_signal_set);
//block the alarm signal
sigaddset(&new_signal_set, SIGALRM);
sigprocmask(SIG_BLOCK, &new_signal_set, &old_signal_set);
//setup the alarm
alarm(1);
clock_t time_before = clock();
//sit idle while we wait for the alarm to go off
while(alarm_flag)
sigsuspend(&old_signal_set);
clock_t time_after = clock();
//restore the old signal mask
sigprocmask(SIG_SETMASK, &old_signal_set, NULL);
return time_after - time_before;
}
clock_t full_load()
{
//set the alarm signal
alarm_flag = 1;
//set the 1-second alarm
alarm(1);
clock_t time_before = clock();
//loop until the alarm goes off
while(alarm_flag);
clock_t time_after = clock();
return time_after - time_before;
}
int main()
{
//setup the signal handler for the alarm
sigset(SIGALRM, alarm_handler);
//call the functions
clock_t idle_time = idle();
clock_t load_time = full_load();
//... do whatever else you need to-do with this info
printf("Idle Time: %d\n", (int)idle_time);
printf("Load Time: %d\n", (int)load_time);
return 0;
}
Keep in mind that according to the POSIX standard, there should be 1 million clocks per second as the time-base for the the clock_t
value, so you should see the number returned for the "full_load" that is close to that number since we're going "full-load" for approximately a second. Idle load should be very small (of course). Here's numbers I generated on my Mac Pro:
Idle Time: 31
Load Time: 1000099
So that seems somewhat in-line with what you're looking for as far as knowing how many clock cycles you may see returned from clock()
. I would of course run this multiple times and take an average to get a better indicator of the variance you might see.
I thought this is covered by CLOCK_VIRTUAL (BSD/HP-UX) or CLOCK_PROCESS_CPUTIME_ID on linux for clock_gettime(2).
You do not specify the version of POSIX, but if you are using C89, it must be pretty old. POSIX.1-2001 aka SUSv3 requires C99 and there is also POSIX.1-2008.
Calibration seems unnecessary; just compare the values returned to monotonic (better, but may not be available) or wall clock time. For clock()
, use the CLOCKS_PER_SEC
define (this is guaranteed to be one million if the system supports the XSI option, but not if it is just POSIX).
Consider getrusage()
instead of clock()
for more accuracy and flexibility.
The function getrusage()
was under the XSI option in earlier POSIX versions; the change history says it was introduced in Issue 4 Version 2 (SUSv1) and was moved to the base in Issue 5 (SUSv2) but there are still XSI markings on it. In any case it is pretty common as it is a 4.2BSD function.
Another option is the times()
function.
精彩评论