(笔记)Linux内核学习(五)之中断推后处理机制
一 中斷
?????? 硬件通過中斷與操作系統(tǒng)進行通信,通過對硬件驅(qū)動程序處注冊中斷處理程序,快速響應(yīng)硬件的中斷。
硬件中斷優(yōu)先級很高,打斷當前正在執(zhí)行的程序。有兩種情況:
硬件中斷在中斷處理程序中處理
硬件中斷延后再進行處理
這個具體硬件相關(guān),在中斷處理程序中處理,打斷了當前正在執(zhí)行的程序;所有中斷都將被屏蔽;如果占用時間太長不合適,
造成系統(tǒng)交互性,反應(yīng)能力都會受到影響。 需要在其中判斷平衡:
?????? 如果一個任務(wù)對時間非常敏感,將其放在中斷處理程序中執(zhí)行;
?????? 如果一個人和和硬件相關(guān),將其放在中斷處理程序中執(zhí)行;
?????? 如果一個任務(wù)要保證不被其他中斷打斷,將其放在中斷處理程序中執(zhí)行;
?????? 其余情況考慮延后機制中執(zhí)行——下半部。
二 中斷推后執(zhí)行機制—— 軟中斷
?????? 軟中斷是在編譯期間靜態(tài)分配的,在程序執(zhí)行前將軟中斷假如到表中。
下面看一下這個過程:
加入軟中斷類型:
?????? Linux3.5.3代碼:
enum {HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */NR_SOFTIRQS};?
?
?
?
//軟中斷表:
static struct?softirq_action?softirq_vec[NR_SOFTIRQS]
//軟中斷結(jié)構(gòu)體
struct softirq_action {void (*action)(struct softirq_action *); };?
?
注冊軟中斷處理函數(shù):
void open_softirq(int nr, void (*action)(struct softirq_action *)) {//關(guān)聯(lián)表中對應(yīng)類型softirq_vec[nr].action = action; }?
?
?
?
觸發(fā)軟中斷:? ? ?
void raise_softirq(unsigned int nr) {unsigned long flags;//停止但保存中斷標志local_irq_save(flags);//將相應(yīng)軟中斷掛起狀態(tài)raise_softirq_irqoff(nr);//恢復(fù)中斷l(xiāng)ocal_irq_restore(flags); }?
?
?
?
執(zhí)行軟中斷:? ?
void irq_exit(void) { invoke_softirq(); //do_softirq(); }void __do_softirq(void) {struct softirq_action *h;__u32 pending;int max_restart = MAX_SOFTIRQ_RESTART;int cpu;//獲取CPU軟中斷狀態(tài)標志位 32位代表最多32個軟中斷pending = local_softirq_pending();restart:/* Reset the pending bitmask before enabling irqs */set_softirq_pending(0);local_irq_enable();h = softirq_vec;do {//被觸發(fā)則執(zhí)行軟中斷處理程序if (pending & 1) {h->action(h);}//下一個軟中斷h++;//下一個軟中斷狀態(tài)標志位pending >>= 1;} while (pending);local_irq_disable();pending = local_softirq_pending();if (pending && --max_restart)goto restart;if (pending)wakeup_softirqd();lockdep_softirq_exit();__local_bh_enable(SOFTIRQ_OFFSET); }?
?
?
軟中斷的基本結(jié)構(gòu)如下圖表示:
??????
?
?
三 ?中斷推后執(zhí)行機制——tasklet
?????? 軟中斷中表中有一種類型是:TASKLET_SOFTIRQ
Tasklet就是利用軟中斷實現(xiàn)中斷推后處理機制。通常使用較多的是tasklet而不是軟中斷。
Tasklet數(shù)據(jù)結(jié)構(gòu):???
struct tasklet_struct {//鏈表中下一個taskletstruct tasklet_struct *next;//tasklet狀態(tài)unsigned long state;//引用計數(shù)器atomic_t count;//tasklet處理函數(shù)void (*func)(unsigned long);//處理函數(shù)參數(shù)unsigned long data;};?
?
?
state:
enum {TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ };?
?
?
count:為0允許激活執(zhí)行
聲明tasklet:可以動態(tài)或者靜態(tài)方式
?????? 靜態(tài):
#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 }?
?????? 動態(tài):? ??
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; }?
?
?
同時需要編寫tasklet處理函數(shù)。
調(diào)度tasklet:
? ? ? ?
void tasklet_hi_schedule(struct tasklet_struct *t) {unsigned long flags;local_irq_save(flags);t->next = NULL;*__this_cpu_read(tasklet_vec.tail) = t;__this_cpu_write(tasklet_vec.tail, &(t->next));raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_restore(flags); }?
?
?
執(zhí)行tasklet處理程序:
?????? 繼續(xù)看上面調(diào)度tasklet程序執(zhí)行:
? ? ??
inline void raise_softirq_irqoff(unsigned int nr) {__raise_softirq_irqoff(nr);if (!in_interrupt())wakeup_softirqd(); }//使用ksoftirqd內(nèi)核線程來處理 static void wakeup_softirqd(void) {/* Interrupts are disabled: no need to stop preemption */struct task_struct *tsk = __this_cpu_read(ksoftirqd);if (tsk && tsk->state != TASK_RUNNING)wake_up_process(tsk); }?
?
?
Ksoftirqd內(nèi)核線程:
?????? 軟中斷才被觸發(fā)頻率很高,在處理過程中還會重新觸發(fā)軟中斷;執(zhí)行會導(dǎo)致用戶空間進程無法獲得處理時間處于饑餓狀態(tài);
對重新觸發(fā)的軟中斷立即處理,會導(dǎo)致占據(jù)處理時間過長;不進行立即處理不合適;
對此解決方法:
l? 只要還有被觸發(fā)并等待處理和過程中重新觸發(fā)的軟中斷的軟中斷,本次執(zhí)行就要負責處理;軟中斷立即處理,用戶空間得不到執(zhí)行時間。
l? 不處理過程中觸發(fā)的軟中斷,放到下一個中斷執(zhí)行時機時處理。軟中斷得不到立即處理,系統(tǒng)空閑時造成不合理;保證用戶空間得到執(zhí)行時間。
兩種方式有存在問題,只能在這其中采取這種的方式:
?????? 內(nèi)核使用線程處理軟中斷,線程優(yōu)先級較低,可以被搶占;能夠保證軟中斷被處理,也能保證用戶空間程序得到執(zhí)行時間。
?????? 每個CPU上有存在這樣一個線程:ksoftirqd/0或者ksoftirqd/1……
static __init int spawn_ksoftirqd(void) {void *cpu = (void *)(long)smp_processor_id();int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);……return 0; }early_initcall(spawn_ksoftirqd); static int __cpuinit cpu_callback(struct notifier_block *nfb,unsigned long action,void *hcpu) {int hotcpu = (unsigned long)hcpu;struct task_struct *p;switch (action) {case CPU_UP_PREPARE:case CPU_UP_PREPARE_FROZEN:p = kthread_create_on_node(run_ksoftirqd,hcpu,cpu_to_node(hotcpu),"ksoftirqd/%d", hotcpu);kthread_bind(p, hotcpu);per_cpu(ksoftirqd, hotcpu) = p;break;…… }?
?
四 中斷推后執(zhí)行機制——工作隊列
?????? 工作隊列(work queue)通過內(nèi)核線程將中斷下半部分程序推后執(zhí)行到線程中執(zhí)行,工作隊列可以創(chuàng)建線程來處理相應(yīng)任務(wù)。
?????? 工作隊列創(chuàng)建的線程為工作者線程:worker thread;系統(tǒng)提供默認的線程來處理工作者隊列。
? ? ??
?
工作者線程數(shù)據(jù)結(jié)構(gòu):
? ? ? ?
struct workqueue_struct {unsigned int flags; /* W: WQ_* flags */union {struct cpu_workqueue_struct __percpu *pcpu;struct cpu_workqueue_struct *single;unsigned long v;} cpu_wq; /* I: cwq's */struct list_head list; /* W: list of all workqueues */…… }?
?
?
CPU工作隊列數(shù)據(jù)結(jié)構(gòu):
struct cpu_workqueue_struct {//每個CPU工作隊列信息struct global_cwq *gcwq;
//每個CPU工作隊列struct workqueue_struct *wq; …… };
?
工作數(shù)據(jù)結(jié)構(gòu):
struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;};?
聲明工作隊列:
?????? 靜態(tài):
#define DECLARE_WORK(n, f) \struct work_struct n = __WORK_INITIALIZER(n, f)?
?
?
?????? 動態(tài):
#define INIT_WORK(_work, _func) \do { \__INIT_WORK((_work), (_func), 0); \} while (0)?????????需要編寫工作隊列處理函數(shù):? ? ? ?
typedef void (*work_func_t)(struct work_struct *work);?
?
?
調(diào)度工作隊列:
int schedule_work(struct work_struct *work) {return queue_work(system_wq, work); } int queue_work(struct workqueue_struct *wq, struct work_struct *work) {ret = queue_work_on(get_cpu(), wq, work); }?
?
?
?????? 喚醒工作者隊列線程處理。
執(zhí)行工作者隊列處理程序:
? ? ? ?
static int worker_thread(void *__worker) {do {struct work_struct *work =list_first_entry(&gcwq->worklist,struct work_struct, entry);process_one_work(worker, work);} while (keep_working(gcwq));}?
?
?
可以創(chuàng)建新的工作者隊列和線程來處理。
平衡是個很關(guān)鍵的問題!
總結(jié)
以上是生活随笔為你收集整理的(笔记)Linux内核学习(五)之中断推后处理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: AlamofireJsonToObjec
- 下一篇: CSS3秘笈第三版涵盖HTML5学习笔记
