5/27 (日)
[Linux] Linux で POSIX インターバルタイマー (2)
む〜。 Linux の POSIX インターバルタイマー(もしかるすると setitimer もかも知れない)は オーバーランをしないようだ。
setitimer や POSIX インターバルタイマーは、 設定時間を越えるとシグナルを送信するのがデフォルトの動作だが、 全てのスレッドでシグナルがブロックされていると送信不能になる。 また POSIX インターバルタイマーは、 一つのプロセスに複数のインターバルタイマーを作れるので タイマーが同時発火した場合は 後発のタイマーが送信できなくなる。
シグナルが送信できないインターバルタイマーは、
プロセスがシグナルを受信可能になるまで遅延させられ
オーバーラン(overrun)状態になる(はず)。
このオーバーランド度を timer_getoverrun で調べても
常に 0 が返ってしまう。
外部の動作から判断すると、 2.4 系でも 2.6 系でも以下のように動作するようだ。
- タイマーがリアルタイムシグナルを送信する設定の場合、
timer_create呼び出し時に タイマー送信用のリアルタイムシグナルのキューを一つ予約してしまう。 そのためタイマー満了時には必ずリアルタイムシグナルを送信できる。 - タイマーが(非リアルタイムシグナルの)通常のシグナルは、 各番号毎に同時に一つしか遅延できない。 しかし Linux ではインターバルタイマーから送られてきたシグナルは、 リアルタイムシグナルと同様にキューイングされる(ようだ)。
カーネルのコード中を読む限り periodic timer (it_interval を指定) を使用している場合は、overrun をカウントアップするパスがあるようだ。 こういう動作でいいんだろうか?
P.S.
テストしたのはこんな感じのプログラム。
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
enum { MAX_TIMER = 16 };
int main(int argc, char** argv)
{
int i, ret;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
sigprocmask(SIG_BLOCK, &sigset, NULL);
timer_t timers[MAX_TIMER];
struct sigevent event;
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGALRM;
event.sigev_value.sival_int = 0xCAFEBABE;
for (i=0 ; i<MAX_TIMER ;i++) {
if (timer_create(CLOCK_REALTIME, &event, &timers[i])) {
perror("timer_create");
exit(1);
}
}
struct itimerspec spec;
spec.it_value.tv_sec = 1;
spec.it_value.tv_nsec = 0;
spec.it_interval.tv_sec = 0;
spec.it_interval.tv_nsec = 0;
for (i=0 ; i<MAX_TIMER ;i++) {
ret = timer_settime(timers[i], 0, &spec, NULL);
assert(ret == 0);
}
printf(" Please wait 5 seconds.\n");
sleep(5);
for (i=0 ; i<MAX_TIMER ;i++) {
int value = timer_getoverrun(timers[i]);
printf("Timer[%d] -> %d\n", i, value);
}
return 0;
}
追記:2013/4/6
timer_getoverrun は struct itimerspec の it_interval を 0 以外に設定して、"POSIX per-process timer" をインターバルタイマーとして動作させた場合のみに有効なようだ。
it_interval.tv_sec = 1 とすれば 1 秒おきにタイマーが発火し、例えばシグナルが送信されるようになるが、シグナルがブロック中の場合にはシグナルが送信される破棄される。
ブロックを解除してシグナルが飛び込んだ時に、何回タイマー発火をスキップしたかが overrun のカウンタらしい。
Linux の場合は、siginfo_t の si_overrun にも同じ値が入っているとな。
5/25 (金)
[Linux] Linux で POSIX インターバルタイマー (1)
POSIX インターバルタイマーで設定できる最長の時間を調べているのだが、 かなり変なタイマーだよなぁという話。
timer_settime は struct timerspec 構造体の
秒(tv_sec)とナノ秒(tv_nsec)を与えて時間してでき、
tv_sec と tv_nsec は Linux では signed long 型になっているので、
理論上は LONG_MAX 秒までできるのだが、
- 設定上限(?)を超えた長時間を指定した場合、エラーが返るプラットフォームと返らないプラットフォームがある。
- timer_settime でした後にすぐに timer_gettime でタイマーの残時間を確認すると、 設定時間よりも残時間の方が常に短いプラットフォームと、 時々逆転するプラットフォームがある。
Linux の glibc のコードを見ると
timer_settime は基本的にカーネルに投げているようだ。
カーネルの特性がそのまま出ていると思われる。
手持ちのマシンを何台か計測してみると
| プラットフォーム | settime (秒) | gettime (秒) |
|---|---|---|
| i386 Linux 2.4.31 | 2147483647.000000000 | 2147483646.999994000 |
| i386 Linux 2.6.9 | 2097151.000000000 | 2097151.000587968 |
| ia64 Linux 2.6.9 | 9223372036854775807.000000000 | 2147484746.512603827 |
IA-64/Linux は LONG_MAX まで設定できるが 実際の稼働時間は 2,147,483,647 (0x7FFF,FFFF) で丸まってしまう。 2147,000,000 秒というのは約68年なので 実用上はほとんど問題がどうしたものか。
i386 Linux 2.6.9 (というかRHEL4) で設定可能だったのは 2,097,151 秒(24日)。 この倍は EINVAL でエラーが返ってくる。 設定上限が 24日〜47日だとするとちょっと短すぎる気がするナリ。
P.S.
テストしたのはこんな感じのプログラム。
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
int main(int argc, char** argv)
{
int i, ret;
timer_t timer;
struct sigevent event;
event.sigev_notify = SIGEV_NONE;
if (timer_create(CLOCK_REALTIME, &event, &timer)) {
perror("timer_create");
abort();
}
struct itimerspec spec, old_spec;
spec.it_value.tv_sec = LONG_MAX;
spec.it_value.tv_nsec = 0;
spec.it_interval.tv_sec = 0;
spec.it_interval.tv_nsec = 0;
while(spec.it_value.tv_sec != 0) {
if (timer_settime(timer, 0, &spec, NULL)) {
if (errno == EINVAL) {
spec.it_value.tv_sec /= 2;
continue;
}
perror("timer_settime");
abort();
}
if (timer_gettime(timer, &old_spec)) {
perror("timer_gettime");
abort();
}
printf("settime: %ld.%09ld\ngettime: %ld.%09ld\n\n",
spec.it_value.tv_sec, spec.it_value.tv_nsec,
old_spec.it_value.tv_sec, old_spec.it_value.tv_nsec);
spec.it_value.tv_sec /= 2;
}
return 0;
}
P.P.S.
こっちの日記では timer_max_limit.c を開発している。
5/21 (月)
[Food] うなぎの大黒屋@新横浜
Cadence の研修で吉川さん・松井さんが新横浜に出張中。 定時後に合流して前田さん・私の四人で会社の近くにあるうなぎ屋「大黒屋」で会食す。
某プロジェクトはなんかアレだそうだ。 いつのまにか性能見積もりがアレになっていて、 アレでアレなので詳しくは書けない。 とにかくガンバって〜〜〜 > 関係者の人
写真は上うな丼 2,300円だが値段の割にはうなぎが小さい。 ちょっとはずれかも…
[Work] 残業解禁?
昨年の6月度からの年間累積残業時間が900時間を超過して、 ここ二ヶ月ぐらいは virtual 残業と言うか pseudo 退勤と言うか いや〜んな状態が続いていたのだが、 今日からリセットです。
5/20 (日)
[Food] 広島お好み焼き「のん」@元住吉 (ぐるなび)
元住吉に広島風お好み焼きのお店が出来た。
首都圏のお好み焼きというとどれも大阪風なので、
広島風お好み焼きの店はちょっと珍しい。
ランチは900円でワンドリングが付いてくる。
一食分はかなりボリュームあり。
個人的にはキャベツにはもう少し火を通した方が好み。
[Food] つけめん あびすけ@日吉
日吉のつけ麺「あびすけ」で夕食を。
5/9 (水)
[Compiler] Intel C++ Compiler にバグが…
Intel C++ Compiler を使って生成される命令列をみながら、 IA-64 命令の最適化を考える日々なのだが Itanium 用の icc 9.1 には でっかいバグがあるようだ。
IA-64 には連続した 16 バイトを一度に読み書きする ld16/st16 という命令がある。 これは Montecito 以前のチップには実装されておらず 実行すると不正命令フォルトが発生していたのだが、 Montecito から本当に実装されている。
この特殊な命令を C 言語から生成するために __ld16/__st16 という コンパイラ固有のビルドイン関数が存在している。 ただし実際にこのビルドイン関数を使うと 最適化がフェーズで化け化けのコードが生成される。
これまで実際に使用する人がいなかったから、 テストも十分にされていなかったみたい…