• <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 那誰 閱讀(4703) 評論(1)  編輯 收藏 引用 所屬分類: linux kernel

            評論

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

            好底層哦 呵呵
            2010-01-02 09:05 | 忘憂三毛
            亚洲?V乱码久久精品蜜桃| 精品久久久久久国产三级 | 亚洲国产二区三区久久| 久久精品中文字幕久久| 午夜精品久久久久久久无码| 无码人妻少妇久久中文字幕蜜桃| 国内精品伊人久久久久AV影院| 久久高清一级毛片| 久久精品亚洲日本波多野结衣| 99久久99久久精品国产| 亚洲va国产va天堂va久久| 久久99精品久久久久久秒播| 亚洲乱码精品久久久久..| 久久97久久97精品免视看| 99久久人妻无码精品系列蜜桃| 亚洲国产日韩欧美久久| 99久久久久| 精品亚洲综合久久中文字幕| 久久成人国产精品免费软件| 日日狠狠久久偷偷色综合96蜜桃| 国产99久久精品一区二区| 99久久精品免费看国产一区二区三区 | 久久精品国产男包| 久久久久国产精品三级网| 国产麻豆精品久久一二三| 国内高清久久久久久| 亚洲国产精品综合久久一线| 久久久噜噜噜久久中文字幕色伊伊| 女人香蕉久久**毛片精品| 国内精品伊人久久久久| 国产精品18久久久久久vr | 久久国产免费观看精品3| 97久久国产综合精品女不卡 | 久久ww精品w免费人成| 久久人做人爽一区二区三区| 思思久久99热免费精品6| 久久综合精品国产一区二区三区 | 浪潮AV色综合久久天堂| 久久亚洲精品无码AV红樱桃| 久久亚洲欧美国产精品| 久久国产免费观看精品|