Transcript タイマ関数
Linux Device Driver 輪講
7. 時の流れ
ACE suzuk
table of contents
1.
2.
3.
4.
5.
6.
時間の経過
現在時刻を知る
実行を遅らせる
カーネルタイマ
タスクレット
作業待ち列
はじめに
ドライバ作成のために必要な実装
デバイス制御,メモリ管理,ハードウェアアクセス
タイミング
時間経過を計測する,時刻を比較する
現在時刻を知る
特定の時間だけ操作を遅らせる
非同期関数が特定の時間が経過した後に処理する
ようにスケジュールする
この章では、タイミングをどう扱うのかについて説明
する
1.時間の経過
カーネルはタイマ割り込みを使い時間の流れ
を管理する
タイミングハードウェアで一定間隔で生成される
プラットフォームでは毎秒100または1000の割り込
みが実行される
Linux2.6では250を使う
内部カーネルカウンタ
タイマ割り込みにより1加算される
システム起動時に0に初期化される
カウンタは64ビットの変数「jiffies_64」
1.1 jiffiesカウンタを使う
<linux/jiffies.h>でカウンタとユーティリティ
関数が宣言されている
ユーティリティ関数
カウンタ値比較関数
時の表現変換関数
timespec_to_jiffies(struct timespec *value)
ユーザ空間では実際のクロック周波数はほぼ隠さ
れている
1.2 プロセッサ固有のレジスタ
クロックカウンタ(クロック周期のカウンタ)
高解像度の時間管理タスクを実現する信頼できる
唯一の手段
カウンタレジスタに記録されている
X86ではTSCが該当する
<asm/msr.h>(マシン固有レジスタ)のrdsc関
数で取得
<asm/timex.h>のget_cycles関数でも取得で
きる
2. 現在時刻を知る
カーネルコードはjiffies値を調べて現在時刻
取得できる
Jiffies値は起動してからの経過時間
時間間隔を測るにはjiffies取得で大抵間に合う
短い時間経過を厳密に計る場合プロセッサ固有のレ
ジスタが役に立つ
実時刻をjiffiesに変換するカーネル関数
<linux/time.h>のmktime関数
カーネル空間で絶対的なタイムスタンプ取得関数
<linux/time.h>のdo_gettimeofday関数
3. 実行を遅らせる
1クロックティックから十分に長い遅延
システムクロックを使った実装
非常に短い遅延
ソフトウェアループ実装
3.1 長い遅延 ビジーwait
それほどの精度なしにクロックティックの整数
倍の時間遅延する場合
Jiffyカウンタを監視するループ
while(time_before(jiffies, j1)){
cpu_relax();
}
この種の実装は可能であれば絶対に避けるべき
ビジーループはシステムの性能を著しく低下させる
3.1 長い遅延 プロセッサの譲渡
必要ないときにCPUを明示的に開放する
<linux/sched.h>のschedule関数を呼び出す
while(time_before(jiffies, j1)){
schedule();
}
scheduleでプロセスがプロセッサを解放すると,
実行をすぐに取り戻せる保障がない
scheduleを呼び出すのはドライバのニーズに対する
安全な解決策ではない
3.1 長い遅延 タイムアウト
遅延を実装する最良の手法はカーネルに遅
延処理を依頼する
<linux/wait.h>のwait_event_timeout関数
指定したタイムアウトが満了した後にリターンする
<linux/sched.h>のschedule_timeout関数
イベント待ち列が必要ない
3.1 短い遅延
ハードウェアの遅延を扱う場合(数十マイクロ
秒以下の精度)
<linux/delay.h>のndelay, udelay, mdelay
関数を使う
ブート時に計算されたプロセッサ速度に基づくソフト
ウェアループで実装されている(busy wait!!)
3 遅延まとめ
要求よりも長く待っても良い場合は
schedule_timeout, msleep, ssleep
非常に短い時間待つ場合は
ndelay, udelay, mdelay
4.カーネルタイマとは
スケジュールに使うカーネルのデータ構造体
ユーザの指定時間に指定した引き数を使い,ユー
ザ定義関数をカーネルに実行させる
実際は,ソフトウェア割り込みの結果
タイマ関数(ユーザ定義関数)は5.4.2で説明し
たすべての点でアトミックを保障する必要がある
<linux/timer.h>, kernel/timer.c
4. カーネルタイマの用途
クロックティックを利用し,未来の特定の時刻にタイマ
ハンドラの実行をスケジュールする
後で起こるアクションをスケジュールする必要があり,その時
が来るまでカレントプロセスをロックしない手法
フロッピーディスクのモーターをオフにする
ハードウェア割り込みできない時に,状態を一定の
間隔でチェックすることでデバイスをポーリングする
(自分自身を再スケジューリングできる)
例:連射パッドの制御
タイマ関数のルール
ユーザ空間へはアクセスできない
割り込みで起動するため,currentポインタ
(カレントプロセス)は使えない
割り込みコンテキスト実行を確かめるには
in_interrup()呼び出しが使える
in_atomic()もあるよ!
スリープ関数を呼び出せない
sleep,schedule(), wait_event(),スリープす
る可能性のある関数
Kmalloc()はスリープするかもしれないからダメ
4.1 タイマのAPI
<linux/timer.h>のタイマAPI達
void init_timer(struct timer_list *timer);
void add_timver(…);
新しいタイマーをスケジュールする
void del_timer(…);
int mod_timer(…);
タイマの終了時間を変更する
int del_timer_sync(…);
リターンしたと時、タイマがどのCPUでも実行されない
ことを保証する
int timer_pendint(…);
現在タイマがスケジュールされているか否かをチェッ
ク
4.2 カーネルタイマの実装
実装は興味深い!ぜひ見てみよう!!
機能要件,前提条件
タイマの管理は可能な限り軽い処理であること
アクティブなタイマ数が増えても対応できる設計
長い遅延のタイマはごく稀で,ほとんどのタイマは
長くても数秒または数分以内に満了する
タイマハンドらはタイマを登録したのと同じCPUで
実行される
カーネル開発者たちの解決策
そうだ,CPU毎にデータ構造体を扱おう!
5. タスクレット(tasklet)
タスクレットとは?
スケジュールすることで,後でカーネルが選択した
時間に実行できる
カーネルタイマのように特定時間に実行できない
タスクレットAPI
void tasklet_schedule(struct tasklet_struct *t);
など
ただのタスクのリンクリスト
6. 作業待ち列(workqueue)
タスクレットの違い
スリープ関数が使える!
スケジュールしたCPUと別CPUで実行できる
デフォルトでは同一CPUで実行する
カーネルコードは作業待ち列関数の実行を,時間
指定して遅延させることができる
つまり,アトミックである必要がない
6.作業待ち列の動作
<linux/workqueue.h>
Struct workqueu_struct構造体
各作業待ち列には1つ以上の専用プロセス(カー
ネルスレッド)がある
専用プロセスが登録された関数を実行する
つまり,ドライバ自身の作業待ち列を作成できる
6.1 共有待ち列
カーネルが提供する共有待ち列
他の誰かと共有した待ち列を使う
タスクを待ち列に登録するだけであれば効率的
struct work_structを扱う