1.ソフト割り込みIRQの概要
ソフト割り込み(SoftIRQ)は、カーネルによって提供される割り込みベースの遅延メカニズムであり、Linuxカーネルは次のソフト割り込みを定義しています。
enum
{
HI_SOFTIRQ=0, /*高优先级的tasklet*/
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ, /*普通tasklet*/
SCHED_SOFTIRQ,
#ifdef CONFIG_HIGH_RES_TIMERS
HRTIMER_SOFTIRQ,
#endif
};
さらに、カーネルはグローバルIRQ配列を維持して、さまざまなソフト割り込みの詳細情報を記録します。
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
グローバル変数のタイプは、すべてのIRQ割り込み処理操作の関数プロトタイプであるstruct softirq_actionです。つまり、システムによって維持されるグローバル変数は、各IRQ割り込み処理関数の情報を記録しますが、その構造は非常に簡単です:
struct softirq_action
{
void (*action)(struct softirq_action *); /*中断处理函数指针*/
void *data; /*传递的参数*/
};
カーネル開発者たちは、カスタムソフト割り込みを追加することを示唆していないので、目的は機構タスクレットの特別なを提供するために、我々はできるタスクレット当社独自の割り込み処理タスクを達成するために。タスクレットは、カーネルによって開発者に提供されるソフト割り込みに基づくタスク遅延メカニズムです。タスクレットは、カーネルによって定義されたいくつかのsoftirqの1つにすぎません。デバイスドライバーは、多くの場合、タスクレットを介して特定の遅延操作を実装します。ソフト割り込みでは、タスクレットは2つのカテゴリに分けられます。1つは優先度の高いHI_SOFTIRQで、もう1つはTASKLET_SOFTIRQです。タスクレットに関する詳細情報を以下に紹介します。
2.タスクレット構造のtasklet_struct
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
シリアル番号 | 変数名 | 効果 |
---|---|---|
1 | 次 | システム内のタスクレットオブジェクトを接続してリンクリストを形成するために使用されます |
2 | 状態 | タスクレットのステータスを記録します。TASKLET_STATE_SCHEDは、タスクレットが送信されたことを示します。TASKLET_STATE_RUNは、タスクレットが実行中であることを示します(SMPの場合のみ) |
3 | カウント | 0:有効、実行をスケジュールできます。1:無効、実行不可。 |
4 | func | 開発者によって実装されたタスクレット実行機関 |
5 | データ | funcに渡されるパラメーター |
3.タスクレットメカニズムの初期化
呼び出すことにより、初期化中のLinuxシステムsoftirq_initの機能HI_SOFTIRQとTASKLET_SOFTIRQはそれぞれ、実行される機能の本体に装着tasklet_hi_actionとtasklet_action:
void __init softirq_init(void)
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
いわゆるインストール実行関数は、IRQグローバル変数のこれら2つの割り込みに対応するメンバーパラメーター(コールバック関数ポインターと渡されたパラメーター)を初期化することであり、この部分はopen_softirq()によって実現されます。open_softirq()関数の実装は非常に簡単です。
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
上記の2つの関数を実行すると、タスクレットメカニズムの初期化タスクが完了します。
softirq_vec [ TASKLET_SOFTIRQ ] .action = tasklet_hi_action ;
softirq_vec [ HI_SOFTIRQ ] .action = tasklet_action ;
4.タスクレットの初期化
タスクレットオブジェクトには、静的モードと動的モードの 2つの初期化メソッドがあります。
4.1タスクレットオブジェクトの静的な初期化
タスクレットオブジェクトの宣言は2つの関数で構成されており、オブジェクト宣言時にタスクレットのオブジェクト名、遅延演算関数func、渡すパラメータを実装する必要があります。
#define DECLARE_TASKLET(name, func, data) \ /*可以被调度执行*/
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \ /*不可以被调度执行*/
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
DECLARE_TASKLETを使用して宣言されたオブジェクトは有効な状態にあり、DECLARE_TASKLET_DISABLEDを使用して宣言されたオブジェクトは無効な状態です。
4.2タスクレットオブジェクトの動的初期化
動的初期化では、tasklet_struct変数を直接定義し、tasklet_init()関数を使用して初期化操作を完了します。
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
5.タスクレットオブジェクトの送信
送信いわゆるタスクレットのオブジェクトは、tasketは実際の宣言に加えtasklet_vec管理リスト、tasklet_vec tasklet_schedule関数によって提出されたすべてのタスクレットのオブジェクトがリストを構成するシステムで使用されるタイプのCPUごとの変数です。(linux-2.6.22)
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = __get_cpu_var(tasklet_vec).list; /*使用头插法插入一个新节点 */
__get_cpu_var(tasklet_vec).list = t;
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))/*调度位为0,再提交;如果为1,则证明已经提交,无需再提交*/
__tasklet_schedule(t);
}
- test_and_set_bit(nr、vaddr)関数:vaddrの値をnrに変更し、vaddrが変更されていないときに値を返します。
- local_irq_save:ローカル割り込み応答をオフにします。
- local_irq_restore:ローカル割り込みをオンにします。
- 注:スケジューリング時の条件は、状態が設定されていないビットTASKLET_STATE_SCHEDであり、タスクレットの実行権限はカウントによって判断され、タスクレットはcount = 0の場合にのみ実行できます。
分析は次のとおりです。
- タスクレットオブジェクトをスケジュールするときに、状態ビットがTASKLET_STATE_SCHEDでない場合は、タスクレットオブジェクトがスケジュールされていないことを意味し、状態値をTASKLET_STATE_SCHEDに設定します(状態値がすでにTASKLET_STATE_SCHEDの場合、スケジュールされていることを示し、再スケジュールする必要はありません);
- ローカル割り込みを閉じて、タスクレットオブジェクトをtasklet_vecリストに挿入します。Linux2.6.22カーネルはヘッド補間法を使用し、一部のLinuxバージョンはテール補間法を使用しており、struct tasklet_headのメンバーは異なります。
- ソフトウェア割り込み要求、raise_softirq_irqoff(TASKLET_SOFTIRQ)を送信して、==「TASKLET_SOFTIRQ」==が現在のプロセッサでの処理を待機していることをカーネルに通知し、整数変数ビットを使用してビットが保留中であることを示しますIRQ操作、0はいいえ、1ははいを意味します。
- ローカル割り込みを再度有効にします。
5.タスクレットの内部実行本体
tasklet_scheduleによってシステムの対応するリンクリストにタスクレットタスクが送信されると、システムはリンクリストをたどり、タスクレットタスクを順番に実行します。2つの割り込みHI_SOFTIRQとTASKLET_SOFTIRQの優先順位は異なるだけなので、もう 1つの割り込みの処理は基本的に同じです。ここでは、TASKLET_SOFTIRQのtasklet_actionのみを紹介します。
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list; /*从tasklet_vec上取下链表,实际是链表的头指针*/
__get_cpu_var(tasklet_vec).list = NULL; /*清空tasklet_vec链表,实际只是清list指针*/
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {/*count=0 使能*/
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))/*清空调度位,返回原来值*/
BUG();/*如果原来为0,属异常情况,应结束退出*/
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;/*重新插入到tasklet_var中*/
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
- ローカル割り込みをオフにすることを前提に、現在のCPUで保留中のtasklet_vecリストヘッドポインターを一時変数リストに移動して記録し、タスクレットリストをクリアしてから、ローカル割り込みを再度有効にします。再ローカル割り込みの目的は、システムがtasklet_vecリストに新しいタスクレットタスクを再スケジュールできるようにすることです。
- リンクされたリストをトラバースする
- tasklet_trylockを使用して、現在のタスクレットタスクが他のCPUで処理されるかどうかを判断します。
- 現在のタスクレットタスクのカウント値を取得します。count= 0の場合、現在のタスク(有効)の実行をスケジュールできることを意味します。値が1の場合、現在のタスクレットタスク(無効化)をスケジュールできないことを意味します。
- 状態のTASKLET_STATE_SCHEDビットを確認します。1の場合は正常に送信されますが、0の場合は、現在の処理機能が送信されていないタスクレットをスケジュールしていることを意味します。これは異常な状況であり、直接終了して戻ります。
- タスクレットで登録された実行本体を実行する
- ロックを解除して、次のタスクレットタスクを再実行します。
- 場合tasklet_trylockタスクが別のCPUで処理することができることを示す0を返しますが、我々は、リスト上のタスクタスクレットtasklet_vecに再提出した後、次のトリガのためにソフト割り込みソフト割り込みの再実行を待つ必要があります。
- tasklet_trylockを使用して、現在のタスクレットタスクが他のCPUで処理されるかどうかを判断します。
- 上記のコードからわかるように、タスクレットタスクが実行される前に、その状態のTASKLET_STATE_SCHEDがクリアされます。つまり、タスクレットが再送信されない限り、次のソフト割り込みはタスクレットタスクを再度実行しません。。これはワンショット機能です。タスクレットタスクが再度サブミットされない限り、1回送信し、1回スケジュールし、実行後にCPUのtasklet_varリストから削除します。
- タスクレットが実行されると、割り込みが再度有効になります。これは、ソフト割り込み設計の本来の意図です。実行中に他のソフトミッドセグメントが到着し、新しいタスクレットもCPUにある場合、新しいタスクレットはtasklet_vecにスケジュールされます。このとき、実行リストのリストには影響せず、タスクセットタスクはTASKLET_STATE_SCHEDビットが実行されていますクリアされ、新しいタスクレットタスクのTASKLET_STATE_SCHEDが有効になっている場合、新しいタスクレットは次のIRQスケジュールで実行されます
6.タスクレットのその他の操作
6.1 tasklet_disable
tasklet_disableおよびtasklet_disable_nosyncは、タスクレットタスクの実行をスケジュールできないようにすることができます。実際、最も重要な操作はatomic_inc(&t-> count)です。count= 0(enable)の場合、タスクレットを呼び出して実行できるためです。
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb__after_atomic_inc();
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
6.2 tasklet_enable
tasklet_enableは、タスクレットオブジェクトのcount-1によって実行されるようにタスクレットをスケジュールできるようにすることです。
注:無効な状態のタスクレットはtasklet_vecに送信できますが、実行はスケジュールされません
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
atomic_dec(&t->count);
}
6.3 tasklet_kill
この関数は、タスクレットオブジェクトのTASKLET_STATE_SCHEDのステータスビットをクリアするために使用されます。これにより、IRQがこのタスクをスケジュールおよび実行できなくなります。現在のタスクレットタスクが実行されている場合、タスクが完了するまでtasklet_killはビジー状態になります。オブジェクトがシステムに送信されたが、実行するようにスケジュールされていない場合、tasklet_killはtasklet_vecリストからタスクレットが削除されるまでスリープします。したがって、この関数はブロックされる可能性のある関数です。
- タスクレットがスケジュールされた後、一度実行されることがわかります。
void tasklet_kill(struct tasklet_struct *t)
{
if (in_interrupt())
printk("Attempt to kill tasklet from interrupt\n");
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
do
yield();
while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
}