/* * setitimer の精度試験 * * Copyright (C) NAKAMURA Minoru <nminoru@nminoru.jp> */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <signal.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/prctl.h> #include <sched.h> #define rdtsc() \ ({uint64_t high, low; \ __asm__ volatile ("lfence\n rdtsc" : "=a" (low), "=d" (high) : : "memory"); \ ((high << 32) | low);}) #define CLOCK_REALTIME (0) enum { REPEAT = 10000, WORK_CPUID = 1, DURATION = 100 // micro second }; static void balast(); int main(int argc, char **argv) { int i; // 低優先度のダミー負荷プロセスを走らせる。 balast(); // setitimer を動かすプロセスを固定する。 cpu_set_t cset; CPU_ZERO(&cset); CPU_SET(WORK_CPUID, &cset); if (sched_setaffinity(0, sizeof(cset), &cset) < 0) { perror("sched_setaffinit"); exit(EXIT_FAILURE); } sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGALRM); // setitimer の結果のシグナルは sigwaitinfo で吸い上げ // るので SIGALRM はブロックする。 sigprocmask(SIG_BLOCK, &sigmask, NULL) ; struct itimerval time; time.it_interval.tv_sec = 0; time.it_interval.tv_usec = 0; time.it_value.tv_sec = 0; time.it_value.tv_usec = DURATION; uint64_t account = 0; for (i=0 ; i<REPEAT ; i++) { struct timespec before; clock_gettime(CLOCK_REALTIME, &before); if (setitimer(ITIMER_REAL, &time, NULL) < 0) { perror("setitime"); exit(EXIT_FAILURE); } int ret; { siginfo_t info; ret = sigwaitinfo(&sigmask, &info); } while(ret != SIGALRM); struct timespec after; clock_gettime(CLOCK_REALTIME, &after); account += (after.tv_sec - before.tv_sec) * 1000000000 + (after.tv_nsec - before.tv_nsec); } // 結果表示 printf("Result: setitimer overrun %.3f (usec)\n", 1.0 / 1000 * account / REPEAT - DURATION); return 0; } /** * 存在するすべての CPU の下に低優先度のダミープロセスを走らせる。 */ static void balast() { int i; int num_processors = (int)sysconf(_SC_NPROCESSORS_CONF); if (num_processors < 0) { perror("sysconf(_SC_NPROCESSORS_CONF)"); exit(EXIT_FAILURE); } for (i=0 ; i<num_processors ; i++) { pid_t pid = fork(); if (pid == 0) { if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) { perror("prctl(PR_SET_PDEATHSIG, SIGKILL)"); exit(EXIT_FAILURE); } nice(+10); cpu_set_t cset; CPU_ZERO(&cset); CPU_SET(i, &cset); if (sched_setaffinity(0, sizeof(cset), &cset) < 0) { perror("sched_setaffinit"); exit(EXIT_FAILURE); } for (;;) { ; } } } }