快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

FreeBSD 5 内核源代码分析之中断处理(3)


    同系统调用一样,这里的调用栈struct intrframe既是atpic_handle_intr()的参数,也是中断
    返回时用以恢复现场的寄存器状态。
    代码:

    /* Interrupt stack frame */
    struct intrframe {
       int   if_vec;
       int   if_fs;
       int   if_es;
       int   if_ds;
       int   if_edi;
       int   if_esi;
       int   if_ebp;
       int   :32;
       int   if_ebx;
       int   if_edx;
       int   if_ecx;
       int   if_eax;
       int   :32;      /* for compat with trap frame - trapno */
       int   :32;      /* for compat with trap frame - err */
       /* below portion defined in 386 hardware */
       int   if_eip;
       int   if_cs;
       int   if_eflags;
       /* below only when crossing rings (e.g. user to kernel) */
       int   if_esp;
       int   if_ss;
    };

    void
    atpic_handle_intr(struct intrframe iframe)
    {
       struct intsrc *isrc;

       KASSERT((uint)iframe.if_vec < ICU_LEN,
           ("unknown int %d\n", iframe.if_vec));
       isrc = &atintrs[iframe.if_vec].at_intsrc;

       /*
        * If we don't have an ithread, see if this is a spurious
        * interrupt.
        */
       if (isrc->is_ithread == NULL &&
           (iframe.if_vec == 7 || iframe.if_vec == 15)) {
          int port, isr;

          /*
           * Read the ISR register to see if IRQ 7/15 is really
           * pending.  Reset read register back to IRR when done.
           */
          port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
          mtx_lock_spin(&icu_lock);
          outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
          isr = inb(port);
          outb(port, OCW3_SEL | OCW3_RR);
          mtx_unlock_spin(&icu_lock);
          if ((isr & IRQ7) == 0)
             return;
       }
       intr_execute_handlers(isrc, &iframe);
    }


    经过简单的有关8259A特有的检查,atpic_handle_intr()就转到intr_execute_handlers()
    继续处理。

    intr_execute_handlers()是一个重要的函数,它先得到IRQ号,然后判断是否是快速中断,
    如果是,则直接在当前线程的上下文中运行,如果不是,则调度对应的中断线程来运行。
    这个处理是被critical_enter()/critical_exit()保护起来的,以保证不会嵌套调度中断线程。
    代码:

    void
    intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
    {
       struct thread *td;
       struct ithd *it;
       struct intrhand *ih;
       int error, vector;

       td = curthread;
       td->td_intr_nesting_level++;

       /*
        * We count software interrupts when we process them.  The
        * code here follows previous practice, but there's an
        * argument for counting hardware interrupts when they're
        * processed too.
        */
       atomic_add_long(isrc->is_count, 1);
       atomic_add_int(&cnt.v_intr, 1);

       it = isrc->is_ithread;
       if (it == NULL)
          ih = NULL;
       else
          ih = TAILQ_FIRST(&it->it_handlers);

       /*
        * XXX: We assume that IRQ 0 is only used for the ISA timer
        * device (clk).
        */
       vector = isrc->is_pic->pic_vector(isrc);
       if (vector == 0)
          clkintr_pending = 1;

       critical_enter();
       if (ih != NULL && ih->ih_flags & IH_FAST) {
          /*
           * Execute fast interrupt handlers directly.
           * To support clock handlers, if a handler registers
           * with a NULL argument, then we pass it a pointer to
           * a trapframe as its argument.
           */
          TAILQ_FOREACH(ih, &it->it_handlers, ih_next) {
             MPASS(ih->ih_flags & IH_FAST);
             CTR3(KTR_INTR, "%s: executing handler %p(%p)",
                 __func__, ih->ih_handler,
                 ih->ih_argument == NULL ? iframe :
                 ih->ih_argument);
             if (ih->ih_argument == NULL)
                ih->ih_handler(iframe);
             else
                ih->ih_handler(ih->ih_argument);
          }
          isrc->is_pic->pic_eoi_source(isrc);
          error = 0;


    凡是总是有例外,fast中断不在中断线程的上下文中运行,而是直接在用户进程的上下文中运行
    代码:

       } else {
          /*
           * For stray and threaded interrupts, we mask and EOI the
           * source.
           */
          isrc->is_pic->pic_disable_source(isrc);
          isrc->is_pic->pic_eoi_source(isrc);
          if (ih == NULL)
             error = EINVAL;
          else
             error = ithread_schedule(it, !cold);
       }


    其他的非快速中断则需要调度。这里先应答中断控制器,然后调度。
    代码:

       critical_exit();
       if (error == EINVAL) {
          atomic_add_long(isrc->is_straycount, 1);
          if (*isrc->is_straycount < MAX_STRAY_LOG)
             log(LOG_ERR, "stray irq%d\n", vector);
          else if (*isrc->is_straycount == MAX_STRAY_LOG)
             log(LOG_CRIT,
                 "too many stray irq %d's: not logging anymore\n",
                 vector);
       }
       td->td_intr_nesting_level--;
    }


    中断线程调度函数ithread_schedule()处理有关中断线程调度的工作。
    代码:

    int
    ithread_schedule(struct ithd *ithread, int do_switch)
    {
       struct int_entropy entropy;
       struct thread *td;
       struct thread *ctd;
       struct proc *p;

       /*
        * If no ithread or no handlers, then we have a stray interrupt.
        */
       if ((ithread == NULL) || TAILQ_EMPTY(&ithread->it_handlers))
          return (EINVAL);

       ctd = curthread;
       /*
        * If any of the handlers for this ithread claim to be good
        * sources of entropy, then gather some.
        */
       if (harvest.interrupt && ithread->it_flags & IT_ENTROPY) {
          entropy.vector = ithread->it_vector;
          entropy.proc = ctd->td_proc;
          random_harvest(&entropy, sizeof(entropy), 2, 0,
              RANDOM_INTERRUPT);
       }


    如果该中断线程有IT_ENTROPY标志,说明可以当作随机数的来源。
    代码:

       td = ithread->it_td;
       p = td->td_proc;
       KASSERT(p != NULL, ("ithread %s has no process", ithread->it_name));
       CTR4(KTR_INTR, "%s: pid %d: (%s) need = %d",
           __func__, p->p_pid, p->p_comm, ithread->it_need);

       /*
        * Set it_need to tell the thread to keep running if it is already
        * running.  Then, grab sched_lock and see if we actually need to
        * put this thread on the runqueue.  If so and the do_switch flag is
        * true and it is safe to switch, then switch to the ithread
        * immediately.  Otherwise, set the needresched flag to guarantee
        * that this ithread will run before any userland processes.
        */
       ithread->it_need = 1;


    设置it_need,可以保证中断线程不会在还有中断的情况下,错过中断而去睡眠,见ithread_loop()。
    代码:

       mtx_lock_spin(&sched_lock);
       if (TD_AWAITING_INTR(td)) {
          CTR2(KTR_INTR, "%s: setrunqueue %d", __func__, p->p_pid);
          TD_CLR_IWAIT(td);
          setrunqueue(td);
          if (do_switch &&
              (ctd->td_critnest == 1) ) {
             KASSERT((TD_IS_RUNNING(ctd)),
                 ("ithread_schedule: Bad state for curthread."));
             ctd->td_proc->p_stats->p_ru.ru_nivcsw++;
             if (ctd->td_flags & TDF_IDLETD)
                ctd->td_state = TDS_CAN_RUN; /* XXXKSE */

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论