Linuxカーネルにおいて、周期的な処理を実行する必要がある場面ではタイマー機能が頻繁に利用される。カーネルタイマーはシステムクロック(arch timer)を基盤として動作し、timer_list構造体によって管理される。この構造体はinclude/linux/timer.hで定義されている。```
struct timer_list {
struct hlist_node entry;
unsigned long expires;
/* タイムアウト時刻を示す(ティック単位) */
void (*function)(struct timer_list );
/ タイムアウト時に呼ばれるコールバック */
u32 flags;
...
};
`expires`メンバーはタイマーの満了時刻をティック数で指定する。例えば2秒周期のタイマーを設定したい場合、`jiffies + (2 * HZ)`を指定すればよい。`HZ`は1秒あたりのティック数を表すマクロである。コールバック関数にはタイマー満了時に実行したい処理を記述する。カーネルタイマーを操作するための主要なAPIは以下の通りである。```
void init_timer_key(struct timer_list *timer, ...)
/* タイマー構造体の初期化 */
void add_timer(struct timer_list *timer)
/* カーネルにタイマーを登録して有効化 */
int del_timer(struct timer_list *timer)
/* 登録されているタイマーを削除 */
int mod_timer(struct timer_list *timer, unsigned long expires)
/* タイマーの満了時刻を更新 */
以下に、カーネルタイマーを使用したサンプルドライバコードを示す。コールバック関数内で自身を再スケジュールすることで、周期タイマーを実現している。``` #include <linux/module.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/jiffies.h>
#define INTERVAL_MS 3000
struct timer_ctx { struct timer_list tlist; unsigned int fired_cnt; };
static struct timer_ctx ctx;
static void on_timer_expire(struct timer_list *t) { struct timer_ctx *priv = from_timer(priv, t, tlist);
priv->fired_cnt++;
pr_info("timer fired: count=%u\n", priv->fired_cnt);
/* 次回の満了時刻を設定して周期実行を実現 */
mod_timer(&priv->tlist, jiffies + msecs_to_jiffies(INTERVAL_MS));
}
static int __init timer_sample_init(void) { pr_info("timer sample module loaded\n");
ctx.fired_cnt = 0;
/* timer_setupでコールバックを紐付けて初期化 */
timer_setup(&ctx.tlist, on_timer_expire, 0);
/* 初回の満了時刻を設定 */
mod_timer(&ctx.tlist, jiffies + msecs_to_jiffies(INTERVAL_MS));
return 0;
}
static void __exit timer_sample_exit(void) { /* モジュール解放時にタイマーを確実に削除 */ del_timer_sync(&ctx.tlist); pr_info("timer sample module unloaded\n"); }
module_init(timer_sample_init); module_exit(timer_sample_exit);
MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Linux kernel timer usage example");