中斷學習之下半部深入理解

2021-08-20 09:25:51 字數 3947 閱讀 5646

對於軟中斷,實現還是很完美的。每乙個中斷上半部都對應乙個軟中斷,可以在多核上併發進行。以塊裝置軟中斷為例,初始化如下

blk_dev_init

=>for_each_possible_cpu(i)//每個cpu都部署

init_list_head(&per_cpu(blk_cpu_done, i));

=>open_softirq(block_softirq, blk_done_softirq, null);

=>softirq_vec[nr].data = data;

=>softirq_vec[nr].action = action;

上半部交接

blk_complete_request

=>cpu_list = &__get_cpu_var(blk_cpu_done);

=>list_add_tail(&req->donelist, cpu_list);//每個完成的req都加到佇列裡面

=>raise_softirq_irqoff(block_softirq);//喚醒軟中斷

=>__raise_softirq_irqoff(nr);

=>or_softirq_pending(1ul << (nr)); //置pending位

=>wakeup_softirqd()

=>struct task_struct *tsk = __get_cpu_var(ksoftirqd);

=>wake_up_process(tsk);//喚醒ksoftirqd

軟中斷粉墨登場

ksoftirqd

=>set_current_state(task_interruptible);

=>while (!kthread_should_stop())

preempt_disable();//關閉搶占計數

if (!local_softirq_pending()) //如果沒有pending計數

schedule();//切換出去

preempt_disable();

__set_current_state(task_running);

while (local_softirq_pending())//有pending計數的話

do_softirq();

=>if (local_softirq_pending())

=>curctx = current_thread_info();//切換到軟中斷上下文

irqctx = softirq_ctx[smp_processor_id()];

irqctx->tinfo.task = curctx->task;

irqctx->tinfo.previous_esp = current_stack_pointer;

/* build the stack frame on the softirq stack */

isp = (u32*) ((char*)irqctx + sizeof(*irqctx));

=>call __do_softirq

=>h = softirq_vec;

=>do

rcu_bh_qsctr_inc(cpu);

}h++;

pending >>= 1;

} while (pending);

preempt_enable_no_resched();

cond_resched();

preempt_disable();

preempt_enable();

set_current_state(task_interruptible);

=>__set_current_state(task_running);

軟中斷成本很高,linux也就定義了6種,塊裝置和網口是典型,中斷頻繁觸發,軟中斷隨之頻繁,但是任務本身不重。如果中斷不是超級頻繁,下半部不是超級複雜。那麼,tasklet是個很好的選擇。

tasklet不管有多少個cpu,1種型別只有1個,沒有佇列。以鍵盤中斷為例,定義如下:

declare_tasklet_disabled(keyboard_tasklet, kbd_bh, 0);
鍵盤中斷交接給tasklet

request_irq(ec3104_irq_ser4, ec3104_keyb_interrupt, 0, "keyboard", null);

=>ec3104_keyb_interrupt

=>e5_receive(k);

=>handle_keyboard_event(k->packet[1]);

=>tasklet_schedule(&keyboard_tasklet);

tasklet執行

tasklet_schedule(&keyboard_tasklet);

=>if (!test_and_set_bit(tasklet_state_sched, &t->state))//當tasklet已經處於排程狀態但是還沒有執行時,新來的同型別的tasklet丟棄掉,因為tasklet只有1個全域性變數

__tasklet_schedule(t);

=>t->next = __get_cpu_var(tasklet_vec).list;//將tasklet加入到tasklet裡面,這裡面的tasklet是不同型別tasklet的集合

__get_cpu_var(tasklet_vec).list = t;

=>raise_softirq_irqoff(tasklet_softirq);

=......=>tasklet_action

=>list = __get_cpu_var(tasklet_vec).list;//把佇列裡面的tasklet全部摘除

__get_cpu_var(tasklet_vec).list = null;

=>while (list)//序列執行所有的tasklet

struct tasklet_struct *t = list;

list = list->next;

if (tasklet_trylock(t))

tasklet_trylock

=>return !test_and_set_bit(tasklet_state_run, &(t)->state);//是否處於run狀態

test_and_clear_bit(tasklet_state_sched, &t->state)//取消tasklet的排程狀態,這個時候如果新來乙個同型別的tasklet,則能掛上去

t->func(t->data);//執行tasklet

tasklet_unlock(t);//解除run狀態

continue;//回到while (list)繼續序列執行下乙個tasklet

local_irq_disable();//如果tasklet處於run狀態,則把佇列重新掛回去

t->next = __get_cpu_var(tasklet_vec).list;

__get_cpu_var(tasklet_vec).list = t;

__raise_softirq_irqoff(tasklet_softirq);

local_irq_enable();

由於tasklet只有1個,處於排程態的話只能有1個,所以不需要重入。所以在鍵盤,滑鼠等處理任務不是很繁重的情況下很合適。

如果中斷不是很繁重,但是下半部需要處理的任務很重很繁瑣,不是那麼緊急的話,wq就更合適了。

linux 軟中斷機制分析

linux 核心軟中斷詳解

總結一下linux軟中斷的實現

kernel中斷分析七——tasklet

深入分析tasklet機制

nginx伺服器上軟中斷過高問題如何解決

軟中斷與硬中斷    作者的部落格不錯

中斷管理之下半部軟中斷

linux中斷管理中有個非常重要的設計理念就是上下半部機制 上半部就是硬體中斷管理 中斷設計為上下半部的原因如下 1.硬體中斷處理程式以非同步方式進行 它會打斷其他重要的 執行 因此為了避免被打斷的程式停止時間太長 硬體中斷處理程式必須盡快完成 2.硬體中斷處理程式通常在關閉中斷的情況下執行 關閉中...

學習筆記之中斷下半部

中斷下半部 由於中斷處理力求時間短,所以引出了下半部。tasklet工作在中斷上下文,處理中不允許休眠,而workqueue工作在程序上下文,這是不同點。1,softirq 處理比較快,但是核心級別的機制,需要修改整個核心原始碼,不推薦也不常用 2,tasklet 內部實現實際呼叫了softirq ...

效能測試必備知識(9) 深入理解「軟中斷」

做效能測試的必備知識系列,可以看下面鏈結的文章哦 比如說你訂了乙份外賣,但是不確定外賣什麼時候送到,也沒有別的方法了解外賣的進度,但是,配送員送外賣是不等人的,到了你這兒沒人取的話,就直接走人了 所以你只能苦苦等著,時不時去門口看看外賣送到沒,而不能幹其他事情 不過呢,如果在訂外賣的時候,你就跟配送...