Linux中断处理过程分析



很久以来对于中断处理没有系统化理解和分析,总是有点好似明白感觉,现在进行从头整理一番。

1.中断处理过程总体介绍
中断处理过程,非常简单
1).中断准备(计算中断向量,保存寄存器),
2). 中断处理
3). 中断处理完成返回

代码如下:
        addl $-0x80,(%esp)    
        SAVE_ALL
        call do_IRQ
        jmp ret_from_intr

2.中断处理点详解

1).保存中断环境
当系统运行过程中, 发生中断,中断处理程序需要保存进程上下文寄存器值。原因是这些内容属入运行中进程,当中断处理完成后,某个时间点进程继续运行时,需要恢复保存进程上下文寄存器值。
x86架构保存寄存器代码:SAVE_ALL
x86架构恢复寄存器代码:RESTORE_REGS 4

2).中断处理与嵌套
中断嵌套是指中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行,这个过程就是中断嵌套。
对于Linux不支持中断嵌套,个人观点是对于不同类型中断是可以嵌套的,而对于同种类型的中断,是不可以嵌套执行的。 具体分析如下:
中断屏蔽分为中断控制器屏蔽中断和cpu屏蔽中断,两者区别如下:
1. 中断控制器屏蔽中断,中断将丢失
x86架构Linux系统:对于同种类型的中断,是不可以嵌套执行的,通过中断控制器屏蔽中断。
代码如下:
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
    mask_ack_irq(desc, irq);
     action_ret = handle_IRQ_event(irq, action);
    desc->chip->unmask(irq);
}
2.cpu屏蔽中断,中断将被记录,并存放在请求寄存器irr中,cpu开启中断后,中断将响应。
对于x86架构,当发生中断时,进入中断门后,处理器会自动清除IF 标志,禁掉(屏蔽)中断, 不响应任何中断。
对于Linux系统来说, 对于标志不为IRQF_DISABLED中断, 系统进入中断处理函数之前,设置IF 标志,响应中断。可见关闭全部中断是否一直执行下去,取决中断设备类型,影响中断响应程度,从而影响系统交互性。
当中断处理程序完成后,再次禁掉(屏蔽)中断,准备中断返回
具体代码如下:
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
   if (!(action->flags & IRQF_DISABLED))
                local_irq_enable_in_hardirq();
  do {
                trace_irq_handler_entry(irq, action);
                ret = action->handler(irq, action->dev_id);
                trace_irq_handler_exit(irq, action, ret);
        retval |= ret;
                action = action->next;
        } while (action);
      local_irq_disable();
        return retval;
}

3).中断上下文下半部分
对于系统不支持中断上下睡眠,主要原因影响系统的实时性,降低系统吞吐量。Linux系统把中断划分为上下半部分,对于下半部分代码要退后执行的,下半部分关键地方在于允许响应所有中断,目的也是为了提供系统响应及时性。此外自行触发的中断,本次不会执行,本次返回前不会执行,等待下次中断返回时候执行。
下半部分退后执行时机之一,代码如下:
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();
        preempt_enable_no_resched();
}

asmlinkage void __do_softirq(void)
{
        local_irq_enable(); //响应所有中断
        h = softirq_vec;
        do {
                if (pending & 1) {
                        h->action(h);
                        rcu_bh_qs(cpu);
                }
                h++;
                pending >>= 1;
        } while (pending);
       local_irq_disable();//屏蔽所有中断响应
            _local_bh_enable();
}
 
4).中断返回
中断返回其实一句代码iret, 但是中断处理返回前,在关闭中断上下情况下,已经发生需要事情,在返回之前需要对其做些处理, 究竟要做那些事情呢?需要根据发生中断时上下文环境,在内核态还是用户态来进行返回处理。
如果内核态并且不启用内核抢占,恢复保存寄存器,返回继续执行中断内核代码。
如果内核态并且启用抢占,判断是否允许抢占(计数是否为0),如果允许抢占并且有需要调度进程,执行内核抢占下进程调度,处理完成后,返回继续执行中断内核代码。
如果是用户态发生中断,没有要出来事情,恢复保存寄存器,返回继续执行中断用户态代码。
如果是用户态发生中断,并且在中断处理过程中有需要处理信号,有需要调度进程, 那么先去处理处理这些事情,处理完成后,恢复保存寄存器,返回继续执行中断用户态代码。

代码如下:
ret_from_intr:
        GET_THREAD_INFO(%ebp)
check_userspace:
        movl PT_EFLAGS(%esp), %eax      # mix EFLAGS and CS
        movb PT_CS(%esp), %al
        andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
        cmpl $USER_RPL, %eax
        jb resume_kernel                # not returning to v8086 or userspace
ENTRY(resume_userspace)
        LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
        andl $_TIF_WORK_MASK, %ecx      # is there any work to be done on
                                        # int/exception return?
        jne work_pending
        jmp restore_all
work_pending:
        testb $_TIF_NEED_RESCHED, %cl
        jz work_notifysig
work_resched:
        call schedule
        LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
        andl $_TIF_WORK_MASK, %ecx      # is there any work to be done other
                                        # than syscall tracing?
        jz restore_all
        testb $_TIF_NEED_RESCHED, %cl
        jnz work_resched
work_notifysig:                         # deal with pending signals and
                                        # notify-resume requests
#ifdef CONFIG_VM86
        testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
        movl %esp, %eax
        jne work_notifysig_v86          # returning to kernel-space or
                                        # vm86-space
        xorl %edx, %edx
        call do_notify_resume
        jmp resume_userspace_sig

        ALIGN
work_notifysig_v86:
        pushl %ecx                      # save ti_flags for do_notify_resume
        CFI_ADJUST_CFA_OFFSET 4
        call save_v86_state             # %eax contains pt_regs pointer
        popl %ecx
        CFI_ADJUST_CFA_OFFSET -4
        movl %eax, %esp
#else
        movl %esp, %eax
#endif
        xorl %edx, %edx
        call do_notify_resume
        jmp resume_userspace_sig
END(work_pending)

#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
        DISABLE_INTERRUPTS(CLBR_ANY)
        cmpl $0,TI_preempt_count(%ebp)  # non-zero preempt_count ?
        jnz restore_all
need_resched:
        movl TI_flags(%ebp), %ecx       # need_resched set ?
        testb $_TIF_NEED_RESCHED, %cl
        jz restore_all
        testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?
        jz restore_all
        call preempt_schedule_irq
        jmp need_resched
END(resume_kernel)
#endif

restore_all:
        TRACE_IRQS_IRET
restore_all_notrace:
        movl PT_EFLAGS(%esp), %eax      # mix EFLAGS, SS and CS
        # Warning: PT_OLDSS(%esp) contains the wrong/random values if we
        # are returning to the kernel.
        # See comments in process.c:copy_thread() for details.
        movb PT_OLDSS(%esp), %ah
        movb PT_CS(%esp), %al
        andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
        cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
        CFI_REMEMBER_STATE
        je ldt_ss                       # returning to user-space with LDT SS
restore_nocheck:
        RESTORE_REGS 4                  # skip orig_eax/error_code
        CFI_ADJUST_CFA_OFFSET -4
irq_return:
        INTERRUPT_RETURN
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值