Linux下半部机制

一、下半部机制有哪些?

        

二、下半部的执行时机?

        下半部不需要指定明確執行時間,只要把任務推遲一點,讓它們在系統不太忙且中斷恢復後執行就可以了,而且執行期間可以响应所有中斷。

三、下半部之——软中断

1.软中断类型

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high 
   frequency threaded job scheduling. For almost all the purposes 
   tasklets are more than enough. F.e. all serial device BHs et 
   al. should be converted to tasklets, not to softirqs. 
 */  
  
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  
};  

2.软中断初始化 

asmlinkage void __init start_kernel(void)  
{  
    char * command_line;  
    extern struct kernel_param __start___param[], __stop___param[];  
  
    smp_setup_processor_id();  
......  
    softirq_init();//初始化軟中斷  
......  
  
    /* Do the rest non-__init'ed, we're now alive */  
    rest_init();  
}

 3.软中断注册

在softirq_init()中會註冊兩個常用類型的軟中斷

void __init softirq_init(void)  
{  
    int cpu;  
  
    for_each_possible_cpu(cpu) {  
        int i;  
  
        per_cpu(tasklet_vec, cpu).tail =  
            &per_cpu(tasklet_vec, cpu).head;  
        per_cpu(tasklet_hi_vec, cpu).tail =  
            &per_cpu(tasklet_hi_vec, cpu).head;  
        for (i = 0; i < NR_SOFTIRQS; i++)  
            INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));  
    }  
  
    register_hotcpu_notifier(&remote_softirq_cpu_notifier);  
  
    //此處註冊兩個軟中斷  
    open_softirq(TASKLET_SOFTIRQ, tasklet_action);     open_softirq(HI_SOFTIRQ, tasklet_hi_action);  
}  
/*
 * softirq_action結構表示軟中斷,定義在<include/linux/interrupt.h>
 */
struct softirq_action  
{  
    void    (*action)(struct softirq_action *);  
}  

/*
 * 文件<kernel/softirq.c>中定義了32個該結構體的數組:
 */
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  

/*
 * nr : 软中断类型
 * action : 软中断处理函数
 */
void open_softirq(int nr, void (*action)(struct softirq_action *))  
{  
    softirq_vec[nr].action = action;  
}  

4.软中断的执行

 那麼軟中斷註冊完成之後,什麼時候觸發軟中斷處理函數執行呢?通常情況下,中斷處理程序会在返回前標記軟中斷,使其在稍後合適的時候被執行。在下列地方,待處理的軟中斷會被檢查和執行: ###1.處理完一個硬件中斷以後; ###2.在ksoftirqd內核線程中; ###3.在那些顯示檢查和執行待處理的軟中斷的代碼中,如網絡子系統中。

/*
 * Exit an interrupt context. Process softirqs if needed and possible:
 * 退出硬件中断时会唤起软中断执行
 */
void irq_exit(void)
{
	account_system_vtime(current);
	trace_hardirq_exit();
	sub_preempt_count(IRQ_EXIT_OFFSET);
	if (!in_interrupt() && local_softirq_pending())
		invoke_softirq();

#ifdef CONFIG_NO_HZ
	/* Make sure that timer wheel updates are propagated */
	if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
		tick_nohz_stop_sched_tick();
#endif
	preempt_enable_no_resched();
}

無論如何,軟中斷會在do_softirq()(位於<kernel/softirq.c>中)中執行,如果有待處理的軟中斷,do_softirq會循環遍歷每一個,調用他們的軟中斷處理程序。

//使用in_interrupt()來防止軟中斷嵌套和搶佔硬中斷環境 ———— 軟中斷不可以在硬件中斷上下文或者是在軟中斷環境中使用
asmlinkage void do_softirq(void) 
{
    __u32 pending;     
    unsigned long flags;     
    if (in_interrupt())        
        return;             //禁止本地中斷     
    local_irq_save(flags);     
    pending = local_softirq_pending();
 
    //如果有軟中斷要處理,則進入__do_softirq()  
    if (pending) 
        __do_softirq();     
    
    local_irq_restore(flags);
}
asmlinkage void __do_softirq(void)
{
    struct softirq_action* h;
    __u32 pending;
    int max_restart = MAX_SOFTIRQ_RESTART;
    int cpu;

    pending = local_softirq_pending();    //pending用於保留待處理軟中斷32位位圖
    account_system_vtime(current);

    __local_bh_disable((unsigned long)__builtin_return_address(0));
    lockdep_softirq_enter();

    cpu = smp_processor_id();
restart:
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0);

    local_irq_enable();

    h = softirq_vec;

    do {
        if (pending &
            1) {    //如果pending第n位被設置為1,那麼處理第n位對應類型的軟中斷
            int prev_count = preempt_count();
            kstat_incr_softirqs_this_cpu(h - softirq_vec);

            trace_softirq_entry(h, softirq_vec);
            h->action(h);    //執行軟中斷處理函數
            trace_softirq_exit(h, softirq_vec);

            if (unlikely(prev_count != preempt_count())) {
                printk(KERN_ERR "huh, entered softirq %td %s %p"
                       "with preempt_count %08x,"
                       " exited with %08x?\n", h - softirq_vec,
                       softirq_to_name[h - softirq_vec],
                       h->action, prev_count, preempt_count());
                preempt_count() = prev_count;
            }

            rcu_bh_qs(cpu);
        }

        h++;
        pending >>= 1;    //pending右移一位,循環檢查其每一位
    } while (pending);    //直到pending變為0,pending最多32位,所以循環最多執行32次。

    local_irq_disable();

    pending = local_softirq_pending();

    if (pending && --max_restart) {
        goto restart;
    }

    if (pending) {
        wakeup_softirqd();
    }

    lockdep_softirq_exit();

    account_system_vtime(current);
    _local_bh_enable();
}

使用軟中斷必須要在編譯期間靜態註冊,一般只有像網絡這樣對性能要求高的情況才使用軟中斷,文章前面我們也看到,系統中註冊的軟中斷就那麼幾個。大部分時候,使用下半部另外一種機制tasklet的情況更多一些,tasklet可以動態的註冊,可以被看作是一種性能和易用性之間尋求平衡的一種產物。事實上,大部分驅動程序都是用tasklet來實現他們的下半部。

5.软中断处理线程ksoftirqd/n

對於軟中斷,內核會選擇幾個特殊的时机進行處理(常見的是中 斷處理程序返回時)。軟中斷被觸發的頻率有時會很好,而且還可能會自行重複觸發,這帶來的結果就是用戶空間的進程無法獲得足夠的處理器時間。

同時,如果單純的對重複觸發的軟中斷採取不立即處理的策略也是無法接受的。

內核選中的方案是不會立即處理重新觸發的軟中斷,作為改進, 當大量軟中斷出現的時候,內核會喚醒一組內核線程來處理這些負載。這些線程在最低優先級上運行(nice值為19)。這種這種方案能夠保證在軟中斷負擔很 重的時候用戶程序不會因為得不到處理時間而處理飢餓狀態。相應的,也能保證“過量”的軟中斷終究會得到處理。最後,在空閒系統上,這個方案同樣表現良好, 軟中斷處理得非常迅速(因為僅存的內存線程肯定會馬上調度)。為了保證只要有空閒的處理器,它們就會處理軟中斷,所以給每個處理器都分配一個這樣的線程。 所有線程的名字都叫做ksoftirqd/n,區別在於n,它對應的是處理器的編號。一旦該線程被初始化,它就會執行類似下面這樣的死循環:

for(;;){  
    if(!softirq_pending(cpu))//softirq_pending()負責發現是否有待處理的軟中斷  
        schedule();    //沒有待處理軟中斷就喚起調度程序選擇其他可執行進程投入運行  
    set_current_state(TASK_RUNNING);  
    while(softirq_pending(cpu)){  
        do_softirq();//有待處理的軟中斷,ksoftirq調用do_softirq()去處理他。  
        if(need_resched())    //如果有必要的話,每次軟中斷完成之後調用schedule函數讓其他重要進程得到處理機會  
            schedule();  
    }  
  
//當所有需要執行的操作都完成以後,該內核線程將自己設置為 TASK_INTERRUPTIBLE狀態  
    set_current_state(TASK_INTERRUPTIBLE);  
}  

6.软中断例子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

denglin12315

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值