softirq、workについてQueue、tasklet学習後のポイントまとめ

18164 ワード

この文書はlinuxバージョンに基づいています:4.14.111
簡単なまとめでsoftirq、work_Queue,taskletの3つの中断下半部の動作原理と区別について,3つの形式の簡単な例を添付した.
 
一、運行原理①softirq:
void __do_softirq(void)
{
    int max_restart = MAX_SOFTIRQ_RESTART;         ///< 10
    struct softirq_action *h;
    ...
    pending = local_softirq_pending();          ///<        pending   ,      softirq      
    __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);  ///<       softirq,    preempt
while ((softirq_bit = ffs(pending))) { ... /* action */ h->action(h); .... } if (pending) { /* softirq softirq , restart , 10 */ if (time_before(jiffies, end) && !need_resched() && --max_restart) goto restart; /* 10 , restart , softirq , */ wakeup_softirqd(); } ... }

softirqリクエストを処理するカーネルスレッドについて:
static void wakeup_softirqd(void)
{
    struct task_struct *tsk = __this_cpu_read(ksoftirqd);

    if (tsk && tsk->state != TASK_RUNNING)
        wake_up_process(tsk);       ///<    ksoftirqd
}

static void run_ksoftirqd(unsigned int cpu)
{
    local_irq_disable();
    if (local_softirq_pending()) {
        __do_softirq();             ///<    __do_softirq
        local_irq_enable();
        cond_resched_rcu_qs();
        return;
    }
    local_irq_enable();
}

ksoftirqdの処理も同様に呼び出し__によって行われることがわかる.do_ソフトウェアqはソフトウェアqを実行します.softirqのトリガ方式:1)通過_do_softirqアクティブトリガは、通常、ハード割り込み終了時、すなわちirq_exitは、invoke_を呼び出すこともできます.softirq() -> __do_ソフトブレークをトリガして後半部のISR(Interrupt Service Routines)を実行する.2)ksoftirqdによる受動トリガ;
② work_Queue:カーネルスレッドに依存しています.後で詳しく説明し、ここにリンクを添付します(SPI Flashで駆動中の kthread 関連操作を例に挙げます).
③tasklet:softirqの1つのactionであり、特殊なsoftirqと理解され、softirq_Initで初期化され、そのコールバック関数はtasklet_Actionは、ここでは説明せず、割り込みコンテキストでも実行されます.
 
二、動作方式の違いsoftirqとtaskletは中断コンテキストで実行され、実行中にsleepスリープ、ブロックなどの操作が発生してはならない、work_Queueはプロセスコンテキストで実行され、スリープ、ブロック、スケジューリングの発生などを行うことができます.
 
三、テスト例及び運行結果
① softirq:
1)第1部はカーネルの修正です.softirqは静的に作成できるため、修正ファイルは/kernel/softirq.cパッチファイルは以下の通りです.
 const char * const softirq_to_name[NR_SOFTIRQS] = {
     "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
-    "TASKLET", "SCHED", "HRTIMER", "RCU"
+    "TASKLET", "SCHED", "HRTIMER", "RCU", "LANCE_TEST"
 };
 
 /*
@@ -441,6 +441,7 @@ void raise_softirq(unsigned int nr)
     raise_softirq_irqoff(nr);
     local_irq_restore(flags);
 }
+EXPORT_SYMBOL(raise_softirq);
 
+static __latent_entropy void softirq_test_action(struct softirq_action *a)
+{
+    printk("This is softirq test.
"); +} + void __init softirq_init(void) { int cpu; @@ -652,6 +658,8 @@ void __init softirq_init(void) open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action); + + open_softirq(LANCE_TEST, softirq_test_action); }

 2)テストモジュールの作成:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

static int softirq_test_init(void)
{
    raise_softirq(LANCE_TEST);
    msleep(1000);
    raise_softirq(LANCE_TEST);
    
    return 0;    
}

static void softirq_test_exit(void)
{
}

module_init(softirq_test_init);
module_exit(softirq_test_exit);
MODULE_LICENSE("GPL");

 3)モジュールを取り付けた後のテスト結果:
cat proc/softirqs 
                    CPU0       CPU1       CPU2       CPU3       
  LANCE_TEST:          0          0          0          0
  
/ # insmod softirq.ko 
[ 9544.236966] This is softirq test.
[ 9545.252842] This is softirq test.

 ② work_queue:
1)テストモジュールの作成:
#include 
#include 
#include 
#include 
#include 
#include 

static struct work_struct lance_work = {0};
static struct workqueue_struct *queue;

void work_queue_cb(void)
{
    printk(KERN_EMERG "This is work_queue test.
"); printk("Cur in interrupt context? %s.
", (in_interrupt() ? "Yes" : "No")); } static int work_queue_init(void) { queue = create_workqueue("work_queue_lance"); INIT_WORK(&lance_work, (typeof(lance_work.func))work_queue_cb); queue_work(queue, &lance_work); msleep(1000); queue_work(queue, &lance_work); return 0; } static void work_queue_exit(void) { destroy_workqueue(queue); } module_init(work_queue_init); module_exit(work_queue_exit); MODULE_LICENSE("GPL");

 2)  モジュールのインストール後のテスト結果:
/ # insmod work_queue.ko 
[12633.889983] This is work_queue test.
[12633.893577] Cur in interrupt context? No.
[12634.917039] This is work_queue test.
/ # [12634.920623] Cur in interrupt context? No.

 ③ tasklet:
1)テストモジュールの作成:
#include 
#include 
#include 
#include 
#include 
#include 

static struct tasklet_struct tasklet = {0};
static struct timer_list timer = {0};

static void task_func(unsigned long data)
{
    printk("This is tasklet test.
"); printk("Cur in interrupt context? %s.
", (in_interrupt() ? "Yes" : "No")); //msleep(1000); ///< } void timer_handler(unsigned long data) { tasklet_schedule(&tasklet); mod_timer(&timer, jiffies+msecs_to_jiffies(2000)); } static int tasklet_test_init(void) { tasklet_init(&tasklet, task_func, 0); init_timer(&timer); timer.function = timer_handler; timer.expires = jiffies + HZ; add_timer(&timer); return 0; } static void tasklet_test_exit(void) { del_timer(&timer); tasklet_disable(&tasklet); } module_init(tasklet_test_init); module_exit(tasklet_test_exit); MODULE_LICENSE("GPL");

 2)  モジュールのインストール後のテスト結果:
/ # insmod tasklet.ko 
/ # [12477.508765] This is tasklet test.
[12477.512089] Cur in interrupt context? Yes.
[12479.524783] This is tasklet test.
[12479.528108] Cur in interrupt context? Yes.

 
四、文末の総括の下で1つの駆動を書く時、もしこの3種類の仕事の方式を選ぶならば
基本的には、スリープ閉塞の必要性がある場合は、work_queueは唯一の選択です.
そうでない場合はtaskletを使用したほうがいいです.性能の向上に専念しなければならない場合はsoftirqを考慮しますが、使用が難しいので、プログラムの再入力性に注意してください.