高精度カーネルタイマ hrtimer (hrtimer_nanosleep())のソースを追う
hrtimer_nanosleepはここ。
初期化後起動されるdo_nanosleepがnanosleep処理本体。
[kernel/hrtimer.c]
1580 long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, 1581 const enum hrtimer_mode mode, const clockid_t clockid) 1582 { 1583 struct restart_block *restart; 1584 struct hrtimer_sleeper t; 1585 int ret = 0; 1586 unsigned long slack; 1587 1588 slack = current->timer_slack_ns; 1589 if (rt_task(current)) 1590 slack = 0; 1591 1592 hrtimer_init_on_stack(&t.timer, clockid, mode); 1593 hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack); 1594 if (do_nanosleep(&t, mode)) 1595 goto out; ...
hrttimer_init_sleeperでタイマが切れたあとの動作を設定している。
currentは「現在実行中の (task_struct タイプの) プロセス」を示すマクロ
[参考]→Linux プロセス管理の徹底調査
http://www.ibm.com/developerworks/jp/linux/library/l-linux-process-management/
その後、schedule()で実行権を手放している。
[kernel/hrtimer.c]
1514 static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode) 1515 { 1516 hrtimer_init_sleeper(t, current); 1517 1518 do { 1519 set_current_state(TASK_INTERRUPTIBLE); 1520 hrtimer_start_expires(&t->timer, mode); 1521 if (!hrtimer_active(&t->timer)) 1522 t->task = NULL; 1523 1524 if (likely(t->task)) 1525 schedule(); 1526 1527 hrtimer_cancel(&t->timer); 1528 mode = HRTIMER_MODE_ABS; 1529 1530 } while (t->task && !signal_pending(current)); 1531 1532 __set_current_state(TASK_RUNNING); 1533 1534 return t->task == NULL; 1535 }
hrttimer_init_sleeperでは、タイマが切れたあとに実行されるコールバック関数を設定、自タスク情報を保存している。コールバック関数hrtimer_wakeupでは、保存したタスクを起床している。
1494 static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer) 1495 { 1496 struct hrtimer_sleeper *t = 1497 container_of(timer, struct hrtimer_sleeper, timer); 1498 struct task_struct *task = t->task; 1499 1500 t->task = NULL; 1501 if (task) 1502 wake_up_process(task); 1503 1504 return HRTIMER_NORESTART; 1505 } 1506 1507 void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) 1508 { 1509 sl->timer.function = hrtimer_wakeup; 1510 sl->task = task; 1511 } 1512 EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
ハードウェアから割り込みがあってから、hrtimer_wakeupが起動されるまでの流れ、hrtimerの管理方法についても見てみたほうが良さそう。
◎hrtimerの精度について
hrtimerはtickに依存するカーネルタイマーより高精度なタイマ処理を目的に実装されたものらしい。
[参考]
高精度カーネルタイマ hrtimer のコールバックの仕組み(途中) - big-eyed-hamsterの日記
http://d.hatena.ne.jp/big-eyed-hamster/20091110/1257836767
精度の目安として、以下のようなプログラムでテストしてみた。
for (i = 0; i < 10; i++) { nanosleep(&req, NULL); //1ms gettimeofday(&tv[i], NULL); }
その結果。
[hoho@toto tmp]$ ./a.out 1357796057sec:947192usec 1357796057sec:948259usec 1357796057sec:949325usec 1357796057sec:950392usec 1357796057sec:951473usec 1357796057sec:952542usec 1357796057sec:953611usec 1357796057sec:954683usec 1357796057sec:955749usec 1357796057sec:956816usec
まぁ、だいたい1msくらいで来てるかな。
ちなみに、計測環境はfedora17,core i7,CPU負荷なしの状態でした。
深く考えずに作ったテキトーなソースな上に、CPU負荷とかキャッシュとかその他の考慮すべき事項はあるはずなので、この値から精度は言えないけどだいたいの目安です。
ちなみに、カーネルをビルドする時にHRTがONになっていないと、tick精度(4msくらい?)になっちゃう。
[参考]
LINUXのリアルタイム性能(周期実行の精度、前準備編) - arai9232の日記
http://d.hatena.ne.jp/arai9232/20110102/1293979908
Linux のスリープ処理、タイマ処理の詳細を見る - naoyaのはてなダイアリー
http://d.hatena.ne.jp/naoya/20080122/1200960926