• <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>

            那誰的技術博客

            感興趣領域:高性能服務器編程,存儲,算法,Linux內核
            隨筆 - 210, 文章 - 0, 評論 - 1183, 引用 - 0
            數據加載中……

            linux內核V2.6.11學習筆記(6)--中斷處理

            每個中斷處理的函數存放在entry.S中的interrupt數組中,該數組有NR_IRQS個元素.
            每個元素做的工作有:
            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;

            也就是保存一些寄存器, 然后調用do_IRQ函數:

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

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

            因此,在hardirq.h中定義了幾個宏:
            #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)

            因此, 函數調用add_preempt_count(HARDIRQ_OFFSET)是增加其中硬中斷的計數.

            回到do_IRQ函數調用中,接下來:
            #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的情況下被調用, 有一個名為hardirq_ctx的數組保存硬中斷的請求棧,它的定義是:
            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];
            也就是說, 這兩個數組的元素數量由CPU數量來決定.
            在系統初始化的時候, 調用函數irq_ctx_init, 分別把這兩個數組中的元素(irq_ctx *類型指針)指向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函數中的代碼做的工作比較當前thread_info描述符地址(通過調用current_thread_info()函數)與hardirq_ctx
            的內容, 如果相同, 說明內核已經在使用硬件中斷請求棧了, 否則如果不相等那么就要切換內核棧,需要保存當前進程描述符指針和esp寄存器.

            接著, do_IRQ函數調用__do_IRQ函數,這個函數的主要工作有:
            // 加鎖
            spin_lock(&(irq_desc[irq].lock));
            // 響應
            irq_desc[irq].handler->ack(irq);

            // 當前狀態既不是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);

            // 當前狀態為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)) // 如果當前狀態不是IRQ_DISABLED 或者 IRQ_INPROGRESS
                        && irq_desc[irq].action) {                            // action指針有效
                    irq_desc[irq].status |= IRQ_INPROGRESS;                    // 設置當前當前狀態為IRQ_INPROGRESS: A handler for the IRQ is being executed
                    do {
                        irq_desc[irq].status 
            &= ~IRQ_PENDING;                // 設置當前當前狀態不是IRQ_PENDING,因為下面要開始處理了
                        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);            // 如果當前狀態還是IRQ_PENDING循環繼續
                    irq_desc[irq].status &= ~IRQ_INPROGRESS;                // 設置當前狀態不是IRQ_INPROGRESS
            }

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

            在循環處理IRQ請求的時候, 最開始要設置狀態為 IRQ_INPROGRESS同時不是IRQ_PENDING, 這個循環處理IRQ請求的過程在當前狀態是IRQ_PENDING則一直進行下去,
            當該循環處理完畢之后, 再將狀態設置為IRQ_INPROGRESS.

            在從__do_IRQ函數返回后, 調用irq_exit函數:
            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();
            }

            該函數首先調用sub_preempt_count減少搶占計數, 然后如果當前不在中斷狀態以及當前有未處理的軟中斷(softirq)則調用invoke_softirq函數(其實就是do_softirq函數)
            處理軟中斷,最后調用preempt_enable_no_resched允許內核搶占.

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

            評論

            # re: linux內核V2.6.11學習筆記(6)--中斷處理  回復  更多評論   

            好底層哦 呵呵
            2010-01-02 09:05 | 忘憂三毛
            国产精品福利一区二区久久| 亚洲?V乱码久久精品蜜桃 | 久久精品国产秦先生| 99久久精品日本一区二区免费| 国产精品成人99久久久久91gav| 亚洲欧美成人久久综合中文网| 丰满少妇人妻久久久久久| 中文字幕热久久久久久久| 久久精品无码一区二区WWW| 国产精品久久久久影院嫩草| 精品久久久久久无码专区| 久久99免费视频| 香港aa三级久久三级老师2021国产三级精品三级在 | 亚洲中文精品久久久久久不卡| 精品熟女少妇av免费久久| 国产亚洲精久久久久久无码77777| 亚洲国产精品综合久久一线| 国产精品久久久久久久| 久久久久亚洲av综合波多野结衣 | 久久亚洲AV成人无码软件| 无码人妻久久一区二区三区免费| 国产一区二区三精品久久久无广告| 久久91精品国产91久| 国产激情久久久久影院老熟女免费| 久久国产AVJUST麻豆| 91久久福利国产成人精品| 久久偷看各类wc女厕嘘嘘| 狠狠色丁香婷综合久久| 久久综合久久伊人| 久久大香萑太香蕉av| 国产L精品国产亚洲区久久| 亚洲女久久久噜噜噜熟女| 国产精品99久久久久久猫咪| 99久久婷婷国产综合亚洲| 久久中文骚妇内射| 久久精品亚洲精品国产色婷| 青青草原综合久久大伊人精品| 亚洲一区中文字幕久久| 久久久无码精品亚洲日韩蜜臀浪潮 | 伊人久久精品无码二区麻豆| 久久青青色综合|