TIMERFD_CREATE(2) | Linux Programmer's Manual | TIMERFD_CREATE(2) |
名前
timerfd_create, timerfd_settime, timerfd_gettime -ファイルディスクリプタ経由で通知するタイマー書式
#include <sys/timerfd.h>
int timerfd_create(int clockid , int flags );
int timerfd_settime(int fd , int flags ,
const struct itimerspec * new_value ,
struct itimerspec * old_value );
int timerfd_gettime(int fd , struct itimerspec * curr_value );
説明
これらのシステムコールは、満了通知をファイルディスクリプタ経由で配送するタイマーの生成と操作を行う。これらは、 setitimer(2) や timer_create(2) を用いる方法の代わりとなるものであり、このファイルディスクリプタを select(2), poll(2), epoll(7) で監視できるという利点がある。timerfd_create()
timerfd_create() は新規のタイマーオブジェクトを生成し、そのタイマーを参照するファイルディスクリプタを返す。 clockid 引き数は、タイマーの進捗を管理するためのクロックを指定するもので、 CLOCK_REALTIME か CLOCK_MONOTONIC のいずれかでなければならない。 CLOCK_REALTIME はシステム全体で使用されるクロックで、このクロックは変更可能である。 CLOCK_MONOTONIC は変更されることのないクロックで、(システム時刻の手動での変更などの) システムクロックの不連続な変化の影響を受けない。これらのクロックの現在の値は clock_gettime(2) を使って取得できる。- TFD_NONBLOCK
- 新しく生成されるオープンファイル記述 (open file description) の O_NONBLOCK ファイルステータスフラグをセットする。このフラグを使うことで、 O_NONBLOCK をセットするために fcntl(2) を追加で呼び出す必要がなくなる。
- TFD_CLOEXEC
- 新しいファイルディスクリプタに対して close-on-exec ( FD_CLOEXEC) フラグをセットする。このフラグが役に立つ理由については、 open(2) の O_CLOEXEC フラグの説明を参照のこと。
バージョン 2.6.26 以前の Linux では、 flags 引き数は未使用であり、0 を指定しなければならない。
timerfd_settime()
timerfd_settime() は、ファイルディスクリプタ fd により参照されるタイマーを開始したり停止したりする。
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};
new_value.it_value はタイマーの満了時間の初期値を、秒とナノ秒で指定する。 new_value.it_value のフィールドのうち少なくとも一方に 0 以外の値を設定すると、タイマーが開始される。両方のフィールドに 0 を設定すると、タイマーが停止する。
new_value.it_interval はタイマーの一回目の満了後に繰り返しタイマーの満了間隔を、秒とナノ秒で指定する。 new_value.it_interval のフィールドのうち少なくとも一方に 0 以外の値を設定すると、繰り返しタイマーが有効になる。両方のフィールドに 0 を設定した場合、タイマーは new_value.it_value で指定された時間後に、一回だけ満了して停止する。
flags 引き数には 0 か TFD_TIMER_ABSTIME を指定する。 0 は相対時刻タイマーを意味し、 new_value.it_value では clockid で指定されたクロックの現在の値からの相対的な時刻を指定する。 TFD_TIMER_ABSTIME は絶対時刻タイマーを意味し、 new_value.it_interval では clockid で指定されたクロックの絶対時刻を指定する。つまり、クロックの値が new_value.it_interval で指定された時刻に達したら、タイマーが満了する。
old_value 引き数が NULL でない場合、 old_value 引き数が指す itimerspec 構造体は、 timerfd_settime() を呼び出した時点でのタイマーの設定を返すのに使用される。下記の timerfd_gettime() の説明を参照。
timerfd_gettime()
timerfd_gettime() は、ファイルディスクリプタ fd で参照されるタイマーの現在の設定が入った itimerspec 構造体を、 curr_value に格納して返す。タイマー・ファイルディスクリプタに対する操作
timerfd_create() が返すファイルディスクリプタは以下の操作をサポートしている。- read(2)
- timerfd_settime() を使ってタイマーの設定が最後変更されて以降、または read(2) の呼び出しに最後に成功して以降に、タイマーの満了が一回以上発生していれば、 read(2) に渡されたバッファに、タイマー満了回数を示す 8 バイトの unsigned 型の整数 ( uint64_t) が返される (返される値はホストバイトオーダ、つまりそのホストマシンにおける整数の通常のバイトオーダである)。
- read(2) を行った時点でタイマーの満了が発生していなければ、 read(2) は停止 (block) する、もしくはファイルディスクリプタが非停止 (nonblocking) に設定されている場合はエラー EAGAIN で失敗する (非停止モードにするには、 fcntl(2) の F_SETFL 命令で O_NONBLOCK フラグをセットする)。
- 渡されたバッファの大きさが 8 バイト未満の場合、 read(2) はエラー EINVAL で失敗する。
- poll(2), select(2) (と同様の操作)
- 一つ以上のタイマー満了が発生していれば、ファイルディスクリプタは読み出し可能となる ( select(2) の readfds 引き数や poll(2) の POLLIN フラグ)。
- このファイルディスクリプタは、他のファイルディスクリプタ多重 API である pselect(2), ppoll(2), epoll(7) もサポートしている。
- close(2)
- ファイルディスクリプタがそれ以降は必要なくなった際には、クローズすべきである。同じ timer オブジェクトに関連付けられたファイルディスクリプタが全てクローズされると、そのタイマーは解除され、そのオブジェクト用の資源がカーネルにより解放される。
fork(2) での扱い
fork(2) が行われると、子プロセスは timerfd_create() により生成されたファイルディスクリプタのコピーを継承する。そのファイルディスクリプタは、親プロセスの対応するファイルディスクリプタと同じタイマーオブジェクトを参照しており、子プロセスの read(2) でも同じタイマーの満了に関する情報が返される。execve(2) での扱い
execve(2) の前後で timerfd_create() により生成されたファイルディスクリプタは保持され、タイマーが開始されていた場合にはタイマーの満了が発生し続ける。返り値
成功すると、 timerfd_create() は新しいファイルディスクリプタを返す。エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。エラー
timerfd_create() は以下のエラーで失敗する可能性がある。- EINVAL
- clockid 引き数が CLOCK_MONOTONIC でも CLOCK_REALTIME でもない。
- EINVAL
- flags が無効である。もしくは、Linux 2.6.26 以前の場合には flags が 0 以外である。
- EMFILE
- オープン済みのファイルディスクリプタの数がプロセスあたりの上限に達していた。
- ENFILE
- オープン済みのファイル総数がシステム全体の上限に達していた。
- ENODEV
- (カーネル内の) 無名 inode デバイスをマウントできなかった。
- ENOMEM
- タイマーを作成するのに十分なカーネルメモリがなかった。
timerfd_settime() と timerfd_gettime() は以下のエラーで失敗する可能性がある。
- EBADF
- fd が有効なファイルディスクリプタでない。
- EFAULT
- new_value, old_value, curr_value が有効なポインタではない。
- EINVAL
- fd が有効な timerfd ファイルディスクリプタでない。
timerfd_settime() は以下のエラーで失敗することもある。
- EINVAL
- new_value が適切に初期化されていない ( tv_nsec の一つが 0 から 999,999,999 までの範囲に入っていない)。
- EINVAL
- flags が無効である。
バージョン
これらのシステムコールはカーネル 2.6.25 以降の Linux で利用可能である。ライブラリ側のサポートはバージョン 2.8 以降の glibc で提供されている。準拠
これらのシステムコールは Linux 固有である。バグ
現在のところ、 timerfd_create() が対応している clockid の種類は timer_create(2) よりも少ない。例
以下のプログラムは、タイマーを作成し、その進捗をモニターするものである。このプログラムは最大で 3 個のコマンドライン引き数を取り、第一引き数ではタイマーの満了時間の初期値 (秒数単位) を、第二引き数ではタイマーの間隔 (秒数単位) を、第三引き数ではタイマーが何回満了したらプログラムが終了するかを指定する。第二引き数と第三引き数は省略可能である。
$ a.out 3 1 100
0.000: timer started
3.000: read: 1; total=1
4.000: read: 1; total=2
^Z # type control-Z to suspend the program
[1]+ Stopped ./timerfd3_demo 3 1 100
$ fg # Resume execution after a few seconds
a.out 3 1 100
9.660: read: 5; total=7
10.000: read: 1; total=8
11.000: read: 1; total=9
^C # type control-C to suspend the program
プログラムのソース
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void
print_elapsed_time(void)
{
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;
if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
handle_error("clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
handle_error("clock_gettime");
secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
int
main(int argc, char *argv[])
{
struct itimerspec new_value;
int max_exp, fd;
struct timespec now;
uint64_t exp, tot_exp;
ssize_t s;
if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
handle_error("clock_gettime");
/* Create a CLOCK_REALTIME absolute timer with initial
expiration and interval as specified in command line */
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
new_value.it_value.tv_nsec = now.tv_nsec;
if (argc == 2) {
new_value.it_interval.tv_sec = 0;
max_exp = 1;
} else {
new_value.it_interval.tv_sec = atoi(argv[2]);
max_exp = atoi(argv[3]);
}
new_value.it_interval.tv_nsec = 0;
fd = timerfd_create(CLOCK_REALTIME, 0);
if (fd == -1)
handle_error("timerfd_create");
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
handle_error("timerfd_settime");
print_elapsed_time();
printf("timer started\n");
for (tot_exp = 0; tot_exp < max_exp;) {
s = read(fd, &exp, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("read");
tot_exp += exp;
print_elapsed_time();
printf("read: %llu; total=%llu\n",
(unsigned long long) exp,
(unsigned long long) tot_exp);
}
exit(EXIT_SUCCESS);
}
関連項目
eventfd(2), poll(2), read(2), select(2), setitimer(2), signalfd(2), timer_create(2), timer_gettime(2), timer_settime(2), epoll(7), time(7)この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.51 の一部である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。2011-09-14 | Linux |