日期:2014-05-16  浏览次数:20794 次

linux内核中断、异常、系统调用的分析以及实践

linux内核中断、异常、系统调用的分析以及实践
2010年12月03日
  报告内容
  中断是由间隔定时器和和I/O设备产生的。
  异常则是由程序的错误产生,或者由内核必须处理的异常条件产生。第一种情况下,内核通过发送一个信号来处理异常;第二种情况下,内核执行恢复异常需要的所有步骤,或对内核服务的一个请求。
  中断和异常改变处理器执行的指令顺序,通常与CPU芯片内部或外部硬件电路产生的电信号相对应。它们提供了一种特殊的方式,使处理器转而去执行正常控制流之外的代码。
  中断是异步的,由硬件随机产生,在程序执行的任何时候可能出现。异常是同步的,在(特殊的或出错的)指令执行时由CPU控制单元产生。
  每个中断和异常由0~255之间的一个数(8位)来标识,Intel称其为中断向量(vector)。非屏蔽中断的向量和异常的向量是固定的,可屏蔽中断的向量可以通过对中断控制器的编程来改变。
  Linux对中断描述符进行了如下分类:
  1.中断门
  用户态的进程不能访问的一个中断门(特权级为0),所有的中断都通过中断门激活,并全部在内核态。由set_intr_gate()函数负责在IDT表项中插入一个中断门。
  2.系统门
  用户态的进程可以访问的一个陷阱门(特权级为3),通过系统门来激活4个linux异常处理程序,它们的向量是3,4,5和128。因此,在用户态下可以发布int3,into,bound和int $0x80四条汇编指令。由set_system_gate ()函数负责在IDT表项中插入一个系统门。
  3.陷阱门
  用户态的进程不能访问的一个陷阱门(特权级为0),大部分linux异常处理程序通过陷阱门激活。由set_trap_gate ()函数负责在IDT表项中插入一个陷阱门。
  三个门均调用了_set_gate宏,代码如下:
  #define _set_gate(gate_addr,type,dpl,addr) \
  do { \
  int __d0, __d1; \
  __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
  "movw %4,%%dx\n\t" \          //将转化后的dpl值存进dx寄存器
  "movl %%eax,%0\n\t" \          //将eax寄存器的值存进gate_addr,即idt_table+n处
  "movl %%edx,%1" \
  :"=m" (*((long *) (gate_addr))), \
  "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
  :"i" ((short) (0x8000+(dpl中的每一项,这个动作是在初始化系统时,由arch/i386/kernel/head.S中的Startup_32()函数完成。
  setup_idt:
  lea ignore_int,%edx
  movl $(__KERNEL_CS 中的start_kernel()函数中调用trap_init()和init_IRQ(),来分别的用有意义的陷阱和中断处理程序替换这个空的处理程序。
  void __init init_IRQ(void)
  {
  ……
  /*
  * NR_IRQS=224
  * FIRST_EXTERNAL_VECTOR定义为0x20,十进制32
  * SYSCALL_VECTOR定义为0x80,中断号为128的中断向量
  */
  for (i = 0; i lock);       //自旋锁,在单处理系统中没有作用
  /*
  *应答PIC的中断,并禁用这条IRQ线。(为串行处理同类型中断)
  */
  desc->handler->ack(irq);      //应答
  status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);  //设置IRQ线状态
  status |= IRQ_PENDING; /* we _want_ to handle it */
  action = NULL;           //在真正开始工作之前,检查相关标志位
  if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
  action = desc->action;
  status &= ~IRQ_PENDING; /* we commit to handling */
  status |= IRQ_INPROGRESS; /* we are handling it */
  }
  desc->status = status;
  if (!action)          //action为null则跳出
  goto out;
  for (;;) {
  spin_unlock(&desc->lock);             //释放中断自旋锁
  handle_IRQ_event(irq, &regs, action);    //在循环中执行中断服务例程
  spin_lock(&desc->lock);               //执行完一次则上锁
  if (!(desc->status & IRQ_PENDING))
  break;
  //若PENDING标志位清0,则循环结束,中断不进一步传递给另一个CPU
  desc->status &= ~IRQ_PENDING;
  }
  desc->status &= ~IRQ_INPROGRESS;      //清除IRQ_INPROGERSS标志位
  out:
  desc->handler->end(irq); 
  /*
  *调用主IRQ描述符的end方法,单处理系统上相应的
  *end_8259A_irq()函数重新激活IRQ线,允许处理同类型中断
  */
  spin_unlock(&desc->lock);               //为do_IRQ释放自旋锁
  if (softirq_pending(cpu))              //检查下半部分是否执行
  do_softirq();
  return 1;
  } 
  handle_IRQ_evnet()函数依次调用这些设备例程:
  int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
  {
  ……
  irq_enter(cpu, i