2013年02月05日 情報科学類 オペレーティングシステム II 筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2012/2013-02-05
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
図? 割り込み処理の前半部分と後半部分
割り込みハンドラ(前半部)と後半部の役割分担の目安。
注意1: Tasklet は、task 構造体とはまったく関係ない。名前がよくない。
注意2: Softirq という用語を、割り込み処理の後半部という意味で使う人もい る。
注意3: 伝統的なUnixでは、top half は、システム・コールから派生する上位 層の処理、bottom half は、割り込みから派生する下位層の処理の意味で使わ れることがある。Linux では、top half, bottom half は、割り込み処理の前 半部分と後半部分の意味に使う。
図? ハードウェアの割り込みにおけるハンドラの実行
図? Softirqでのハンドラの実行
ハードウェアの割り込みlinux-3.6.8/include/linux/interrupt.h 442: struct softirq_action 443: { 444: void (*action)(struct softirq_action *); 445: }; linux-3.6.8/kernel/softirq.c 55: static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
void softirq_handler(struct softirq_action *a) { ... }
この変数のビットnを1にする(raise)には、次の関数を使う。
void raise_softirq(unsigned int n) まず、割り込みが禁止し、 n で指定された Softirq を実行可能にし、 再び割り込みを許可する。 void raise_softirq_irqoff(unsigned int n) n で指定された Softirq を実行可能にする。 割り込みハンドラによる実行を想定。 割り込みハンドラは、割り込み禁止で実行されている。概念的には、次のようになっている。
unsigned int softirq_pending; raise_softirq(int n) { softirq_pending |= (1 << n); }実際には、変数softirq_pendingは、プロセッサごとに固有(local)の変数になっ ている。 次の関数でアクセスする。
linux-3.6.8/kernel/softirq.c 207: asmlinkage void __do_softirq(void) 208: { 209: struct softirq_action *h; 210: __u32 pending; ... 222: pending = local_softirq_pending(); ... 232: set_softirq_pending(0); 233: 234: local_irq_enable(); 235: 236: h = softirq_vec; 237: 238: do { 239: if (pending & 1) { ... 246: h->action(h); ... 258: } 259: h++; 260: pending >>= 1; 261: } while (pending); ... 277: }
linux-3.6.8/include/linux/interrupt.h 417: enum 418: { 419: HI_SOFTIRQ=0, 420: TIMER_SOFTIRQ, 421: NET_TX_SOFTIRQ, 422: NET_RX_SOFTIRQ, 423: BLOCK_SOFTIRQ, 424: BLOCK_IOPOLL_SOFTIRQ, 425: TASKLET_SOFTIRQ, 426: SCHED_SOFTIRQ, 427: HRTIMER_SOFTIRQ, 428: RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ 429: 430: NR_SOFTIRQS 431: };Softirq の主な利用場所
linux-3.6.8/net/core/dev.c 6579: static int __init net_dev_init(void) 6580: { ... 6642: open_softirq(NET_TX_SOFTIRQ, net_tx_action); 6643: open_softirq(NET_RX_SOFTIRQ, net_rx_action); ... 6651: } 3005: static void net_tx_action(struct softirq_action *h) 3006: { ... 3061: } 3903: static void net_rx_action(struct softirq_action *h) 3904: { ... 3987: }
$ ps alx
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
...
1 0 3 2 20 0 0 0 run_ks S ? 0:00 [ksoftirqd/0]
$
問題: Softirq を優先すべきか、普通のユーザ・プロセスを優先すべきか。
Tasklet で1つの仕事は次のような、struct tasklet_struct で表現される。
linux-3.6.8/include/linux/interrupt.h 503: struct tasklet_struct 504: { 505: struct tasklet_struct *next; 506: unsigned long state; 507: atomic_t count; 508: void (*func)(unsigned long); 509: unsigned long data; 510: };
図? Taskletにおける仕事のキュー
DECLARE_TASKLET(name, func, data) 有効な(count==0) の struct tasklet_struct を宣言する DECLARE_TASKLET_DISABLED(name, func, data) 無効な(count==1) の struct tasklet_struct を宣言する
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);その他に、生成消滅有効無効に関して次のような操作がある。
void tasklet_handler(unsigned long data) { .... }
void tasklet_schedule(struct tasklet_struct *t) Tasklet t を通常の優先度でスケジュールする void tasklet_hi_schedule(struct tasklet_struct *t) Tasklet t を高優先度でスケジュールするすると、それは「そのうちに」1度だけ実行される。
linux-3.6.8/drivers/net/wireless/ath/ath9k/ath9k.h 657: struct ath_softc { ... 664: struct tasklet_struct intr_tq; 665: struct tasklet_struct bcon_tasklet; ... 734: }; linux-3.6.8/drivers/net/wireless/ath/ath9k/init.c 561: tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); 562: tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, 563: (unsigned long)sc);
linux-3.6.8/drivers/net/wireless/ath/ath9k/main.c 429: irqreturn_t ath_isr(int irq, void *dev) 430: { ... 476: ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ ... 489: if (status & SCHED_INTR) 490: sched = true; ... 522: if (status & ATH9K_INT_SWBA) 523: tasklet_schedule(&sc->bcon_tasklet); ... 550: if (sched) { ... 553: tasklet_schedule(&sc->intr_tq); 554: } 555: 556: return IRQ_HANDLED; ... 559: } 361: void ath9k_tasklet(unsigned long data) 362: { ... 427: } linux-3.6.8/drivers/net/wireless/ath/ath9k/beacon.c 310: void ath9k_beacon_tasklet(unsigned long data) 311: { ... 408: }
キューにつながれる仕事は、Tasklet の仕事とほとんど同じで、関数へのポイ ンタ func と data からなる。処理の主体が、ワーカ・スレッドと呼ばれるカー ネル・レベルのスレッドである所が違う。
$ ps alx
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
...
1 0 5 2 0 -20 0 0 worker S< ? 0:00 [kworker/0:0H]
1 0 6 2 20 0 0 0 worker S ? 0:00 [kworker/u:0]
1 0 7 2 0 -20 0 0 worker S< ? 0:00 [kworker/u:0H]
1 0 27 2 20 0 0 0 worker S ? 0:00 [kworker/u:1]
1 0 367 2 0 -20 0 0 worker S< ? 0:00 [kworker/0:1H]
1 0 650 2 0 -20 0 0 worker S< ? 0:00 [kworker/u:1H]
...
1 0 29461 2 20 0 0 0 worker S ? 0:00 [kworker/0:1]
1 0 29482 2 20 0 0 0 worker S ? 0:00 [kworker/0:2]
1 0 29503 2 20 0 0 0 worker S ? 0:00 [kworker/0:0]
1 0 29601 2 20 0 0 0 worker S ? 0:00 [kworker/0:3]
$
汎用の Work Queue のワーカ・スレッドの他に、専用のワーカ・スレッドを作
ることもできる。
linux-3.6.8/include/linux/workqueue.h 18: typedef void (*work_func_t)(struct work_struct *work); 79: struct work_struct { 80: atomic_long_t data; 81: struct list_head entry; 82: work_func_t func; ... 86: };
struct work_struct my_work; ... INIT_WORK(&my_work,my_work_handler);
void my_work_handler(struct work_struct *work) { ... }
schedule_work(&work);この結果、INIT_WORK() で設定したハンドラがワーカ・スレッドにより「その うち」に呼び出される。
schedule_work() では、即座に実行される可能性もある。少し後に実行したい (間を取りたい)時には、次の関数を呼ぶ。
schedule_delayed_work(&work,ticks);ticks は、どのくらい間をとるか。単位は、jiffies (今後の授業で取り上げる)。 多くのシステムで10ミリ秒-1ミリ秒で、設定によって異なる。
void f(int arg1, int arg2) { 省略; }これを実現するために、どのような Tasklet のハンドラと初期化コードを書け ばよいか。以下の空欄を埋めなさい。
void tasklet_handler(unsigned long data) { /* Tasklet ハンドラ */ int arg1, arg2; arg1 = 省略; arg2 = 省略; /*空欄(a)*/ その他の仕事; } DECLARE_TASKLET(/*空欄(b)*/, /*空欄(c)*/, 0); /* 構造体の初期化 */注意: 構造体の名前は、次の問題の解答で利用する。それらしいものを付けな さい。
irqreturn_t irq_handler(int irq, void *dev) { /*空欄(d)*/ return IRQ_HANDLED; }