• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            那誰(shuí)的技術(shù)博客

            感興趣領(lǐng)域:高性能服務(wù)器編程,存儲(chǔ),算法,Linux內(nèi)核
            隨筆 - 210, 文章 - 0, 評(píng)論 - 1183, 引用 - 0
            數(shù)據(jù)加載中……

            linux內(nèi)核V2.6.11學(xué)習(xí)筆記(6)--中斷處理

            每個(gè)中斷處理的函數(shù)存放在entry.S中的interrupt數(shù)組中,該數(shù)組有NR_IRQS個(gè)元素.
            每個(gè)元素做的工作有:
            ENTRY(irq_entries_start)
            .rept NR_IRQS
                ALIGN
            1:    pushl $vector-256
                jmp common_interrupt
            .data
                .
            long 1b
            .text
            vector
            =vector+1
            .endr

                ALIGN
            common_interrupt:
                SAVE_ALL
                movl 
            %esp,%eax
                call do_IRQ
                jmp ret_from_intr

            首先將中斷向量- 256保存在棧中
            其中的SAVE_ALL做的工作包括:
            #define SAVE_ALL \
                cld; \
                pushl 
            %es; \
                pushl 
            %ds; \
                pushl 
            %eax; \
                pushl 
            %ebp; \
                pushl 
            %edi; \
                pushl 
            %esi; \
                pushl 
            %edx; \
                pushl 
            %ecx; \
                pushl 
            %ebx; \
                movl $(__USER_DS), 
            %edx; \
                movl 
            %edx, %ds; \
                movl 
            %edx, %es;

            也就是保存一些寄存器, 然后調(diào)用do_IRQ函數(shù):

            do_IRQ函數(shù)首先調(diào)用irq_enter()函數(shù):
            #define irq_enter()                    \
                
            do {                        \
                    account_system_vtime(current);        \
                    add_preempt_count(HARDIRQ_OFFSET);    \
                } 
            while (0)

            其中要注意的是函數(shù)add_preempt_count, 它改變的是當(dāng)前進(jìn)程中thread_info中的成員preempt_count,它是一個(gè)32位的字段,分為幾個(gè)部分,:
            0-7位: 搶占計(jì)數(shù)器, 最大值255
            8-15位: 軟中斷計(jì)數(shù)器, 最大值255
            16-27位: 硬中斷計(jì)數(shù)器, 最大值4096
            28位: PREEMPT_ACTIVE標(biāo)志

            因此,在hardirq.h中定義了幾個(gè)宏:
            #define PREEMPT_BITS    8
            #define SOFTIRQ_BITS    8
            #define HARDIRQ_BITS    12

            #define PREEMPT_SHIFT    0
            #define SOFTIRQ_SHIFT    (PREEMPT_SHIFT + PREEMPT_BITS)
            #define HARDIRQ_SHIFT    (SOFTIRQ_SHIFT + SOFTIRQ_BITS)

            #define PREEMPT_OFFSET    (1UL << PREEMPT_SHIFT)
            #define SOFTIRQ_OFFSET    (1UL << SOFTIRQ_SHIFT)
            #define HARDIRQ_OFFSET    (1UL << HARDIRQ_SHIFT)

            因此, 函數(shù)調(diào)用add_preempt_count(HARDIRQ_OFFSET)是增加其中硬中斷的計(jì)數(shù).

            回到do_IRQ函數(shù)調(diào)用中,接下來(lái):
            #ifdef CONFIG_4KSTACKS

                curctx 
            = (union irq_ctx *) current_thread_info();
                irqctx 
            = hardirq_ctx[smp_processor_id()];

                
            /*
                 * this is where we switch to the IRQ stack. However, if we are
                 * already using the IRQ stack (because we interrupted a hardirq
                 * handler) we can't do that and just have to keep using the
                 * current stack (which is the irq stack already after all)
                 
            */
                
            if (curctx != irqctx) {
                    
            int arg1, arg2, ebx;

                    
            /* build the stack frame on the IRQ stack */
                    isp 
            = (u32*) ((char*)irqctx + sizeof(*irqctx));
                    irqctx
            ->tinfo.task = curctx->tinfo.task;
                    irqctx
            ->tinfo.previous_esp = current_stack_pointer;

                    asm 
            volatile(
                        
            "       xchgl   %%ebx,%%esp      \n"
                        
            "       call    __do_IRQ         \n"
                        
            "       movl   %%ebx,%%esp      \n"
                        : 
            "=a" (arg1), "=d" (arg2), "=b" (ebx)
                        :  
            "0" (irq),   "1" (regs),  "2" (isp)
                        : 
            "memory""cc""ecx"
                    );
                } 
            else
            #endif

            這段代碼僅在線程棧大小是4K的情況下被調(diào)用, 有一個(gè)名為hardirq_ctx的數(shù)組保存硬中斷的請(qǐng)求棧,它的定義是:
            union irq_ctx {
                
            struct thread_info      tinfo;
                u32                     stack[THREAD_SIZE
            /sizeof(u32)];
            };

            static union irq_ctx *hardirq_ctx[NR_CPUS];
            static union irq_ctx *softirq_ctx[NR_CPUS];
            也就是說(shuō), 這兩個(gè)數(shù)組的元素?cái)?shù)量由CPU數(shù)量來(lái)決定.
            在系統(tǒng)初始化的時(shí)候, 調(diào)用函數(shù)irq_ctx_init, 分別把這兩個(gè)數(shù)組中的元素(irq_ctx *類(lèi)型指針)指向hardirq_stack和softirq_stack:
            static char softirq_stack[NR_CPUS * THREAD_SIZE]
                    __attribute__((__aligned__(THREAD_SIZE)));

            static char hardirq_stack[NR_CPUS * THREAD_SIZE]
                    __attribute__((__aligned__(THREAD_SIZE)));
            因此, 上面的那段do_IRQ函數(shù)中的代碼做的工作比較當(dāng)前thread_info描述符地址(通過(guò)調(diào)用current_thread_info()函數(shù))與hardirq_ctx
            的內(nèi)容, 如果相同, 說(shuō)明內(nèi)核已經(jīng)在使用硬件中斷請(qǐng)求棧了, 否則如果不相等那么就要切換內(nèi)核棧,需要保存當(dāng)前進(jìn)程描述符指針和esp寄存器.

            接著, do_IRQ函數(shù)調(diào)用__do_IRQ函數(shù),這個(gè)函數(shù)的主要工作有:
            // 加鎖
            spin_lock(&(irq_desc[irq].lock));
            // 響應(yīng)
            irq_desc[irq].handler->ack(irq);

            // 當(dāng)前狀態(tài)既不是IRQ_REPLAY:The IRQ line has been disabled but the previous IRQ occurrence has not yet been acknowledged to the PIC
            // 也不是IRQ_WAITING:The kernel is using the IRQ line while performing a hardware device probe; moreover, the corresponding interrupt has not been raised
            irq_desc[irq].status &= ~(IRQ_REPLAY | IRQ_WAITING);

            // 當(dāng)前狀態(tài)為IRQ_PENDING:An IRQ has occurred on the line; its occurrence has been acknowledged to the PIC, but it has not yet been serviced by the kernel
            irq_desc[irq].status |= IRQ_PENDING;

            if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)) // 如果當(dāng)前狀態(tài)不是IRQ_DISABLED 或者 IRQ_INPROGRESS
                        && irq_desc[irq].action) {                            // action指針有效
                    irq_desc[irq].status |= IRQ_INPROGRESS;                    // 設(shè)置當(dāng)前當(dāng)前狀態(tài)為IRQ_INPROGRESS: A handler for the IRQ is being executed
                    do {
                        irq_desc[irq].status 
            &= ~IRQ_PENDING;                // 設(shè)置當(dāng)前當(dāng)前狀態(tài)不是IRQ_PENDING,因?yàn)橄旅嬉_(kāi)始處理了
                        spin_unlock(&(irq_desc[irq].lock));
                        handle_IRQ_event(irq, regs, irq_desc[irq].action);    
            // 處理事件
                        spin_lock(&(irq_desc[irq].lock));
                    } 
            while (irq_desc[irq].status & IRQ_PENDING);            // 如果當(dāng)前狀態(tài)還是IRQ_PENDING循環(huán)繼續(xù)
                    irq_desc[irq].status &= ~IRQ_INPROGRESS;                // 設(shè)置當(dāng)前狀態(tài)不是IRQ_INPROGRESS
            }

            irq_desc[irq].handler
            ->end(irq);
            spin_unlock(
            &(irq_desc[irq].lock));

            在循環(huán)處理IRQ請(qǐng)求的時(shí)候, 最開(kāi)始要設(shè)置狀態(tài)為 IRQ_INPROGRESS同時(shí)不是IRQ_PENDING, 這個(gè)循環(huán)處理IRQ請(qǐng)求的過(guò)程在當(dāng)前狀態(tài)是IRQ_PENDING則一直進(jìn)行下去,
            當(dāng)該循環(huán)處理完畢之后, 再將狀態(tài)設(shè)置為IRQ_INPROGRESS.

            在從__do_IRQ函數(shù)返回后, 調(diào)用irq_exit函數(shù):
            void irq_exit(void)
            {
                account_system_vtime(current);
                sub_preempt_count(IRQ_EXIT_OFFSET);
                
            if (!in_interrupt() && local_softirq_pending())
                    invoke_softirq();
                preempt_enable_no_resched();
            }

            該函數(shù)首先調(diào)用sub_preempt_count減少搶占計(jì)數(shù), 然后如果當(dāng)前不在中斷狀態(tài)以及當(dāng)前有未處理的軟中斷(softirq)則調(diào)用invoke_softirq函數(shù)(其實(shí)就是do_softirq函數(shù))
            處理軟中斷,最后調(diào)用preempt_enable_no_resched允許內(nèi)核搶占.

            posted on 2009-05-03 16:09 那誰(shuí) 閱讀(4697) 評(píng)論(1)  編輯 收藏 引用 所屬分類(lèi): linux kernel

            評(píng)論

            # re: linux內(nèi)核V2.6.11學(xué)習(xí)筆記(6)--中斷處理  回復(fù)  更多評(píng)論   

            好底層哦 呵呵
            2010-01-02 09:05 | 忘憂三毛
            久久久久亚洲AV片无码下载蜜桃| 亚洲精品美女久久久久99小说| 亚洲精品无码专区久久同性男| 欧美成a人片免费看久久| 久久久久亚洲Av无码专| 亚洲综合久久综合激情久久| 久久露脸国产精品| 久久国产精品国产自线拍免费 | 狠狠色丁香久久婷婷综合五月| 久久久久av无码免费网| 久久亚洲精品成人AV| 久久天天躁夜夜躁狠狠躁2022| 国产一级持黄大片99久久| 久久亚洲国产精品成人AV秋霞 | 亚洲中文字幕无码久久2020| 久久精品国产精品青草| 国内精品人妻无码久久久影院| 精品熟女少妇AV免费久久| 伊人色综合九久久天天蜜桃| 色欲综合久久躁天天躁| 亚洲v国产v天堂a无码久久| 午夜精品久久久久久| 精品久久久久久久久免费影院| 2021国产精品午夜久久| 热综合一本伊人久久精品| 免费精品久久天干天干| 久久人与动人物a级毛片| 国产精品久久国产精品99盘| 久久夜色精品国产亚洲| 久久久久亚洲爆乳少妇无| 久久这里的只有是精品23| 亚洲嫩草影院久久精品| 久久久精品日本一区二区三区| 99久久精品国内| 久久天天躁狠狠躁夜夜不卡| 久久久久亚洲AV成人网人人网站| 亚洲午夜久久久| 亚洲欧美成人久久综合中文网| av无码久久久久不卡免费网站| 久久精品国产色蜜蜜麻豆| 久久久久国产|