ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

POSIX Timer で純粋に時間を測る

man7.org

sevp.sigev_notifySIGEV_NONE にし、なおかつ非常に先の時間を expiration に指定すれば、その時間までの残り時間を timer_gettime(2) で取得できる。

あとは引き算をすればOK。「非常に先の時間」以上の時間を計測できないことになるが、 10億秒(31年)などを指定すれば用は足りそう。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <error.h>
#include <sys/types.h>
#include <sys/wait.h>

// timer_settime(2) などの spec.it_value.tv_sec にセットできる最大値は
// KTIME_SEC_MAX というカーネル定数で決まる模様で、 LONG_MAX より小さな値になる。
// see: https://elixir.bootlin.com/linux/v4.15/source/include/linux/time64.h#L40
#define TIMER_MAX 1000000000L

int main(int argc, char *argv[]) {
  struct sigevent ev;
  struct itimerspec spec;
  ev.sigev_notify = SIGEV_NONE;
  timer_t t;

  if(timer_create(CLOCK_REALTIME, &ev, &t) < 0) {
    perror("timer_create");
    exit(1);
  }

  spec.it_interval.tv_sec = 0;
  spec.it_interval.tv_nsec = 0;
  spec.it_value.tv_sec = TIMER_MAX;
  spec.it_value.tv_nsec = 0;
  if(timer_settime(t, 0, &spec, NULL) < 0) {
    perror("timer_create");
    exit(1);
  }

  char *run[] = {
    "bash",
    "-i",
    NULL
  };
  int status;
  pid_t pid = fork();
  if(pid < 0) {
    perror("fork");
    exit(1);
  }
  if(pid == 0)
    if(execv("/bin/bash", run) < 0) {
      perror("execv");
      exit(1);
    }

  if(waitpid(pid, &status, 0) < 0) {
    perror("waitpid");
    exit(1);
  }

  struct itimerspec retval;
  int ret = timer_gettime(t, &retval);
  if(ret < 0) {
    perror("timer_gettime");
    exit(1);
  } else {
    long sec, nsec;
    if(!retval.it_value.tv_nsec) {
      sec = TIMER_MAX - retval.it_value.tv_sec;
      nsec = 0L;
    } else {
      sec = TIMER_MAX - retval.it_value.tv_sec - 1;
      nsec = 1000000000 - retval.it_value.tv_nsec;
    }
    printf("bash process ran in <%ld.%09ld> (s)\n", sec, nsec);
  }

  exit(0);
}
$ gcc timer.c -o timer -lrt
$ ./timer
$ sleep 2
$ exit
exit
bash process ran in <3.432966856> (s)

今日も雑です。