Chinaunix首页 | 论坛 | 博客
  • 博客访问: 487817
  • 博文数量: 9
  • 博客积分: 286
  • 博客等级: 二等列兵
  • 技术积分: 445
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-01 21:39
文章分类

分类: LINUX

2012-07-13 19:44:06

cpu与磁盘、网卡、键盘等外围设备(相对于cpu和内存而言)交互时,cpu下发I/O请求到这些设备后,相对cpu的处理能力而言,磁盘、网卡等设备需要较长时间完成请求处理。


那么在请求发出到处理完成这段时间,应如何设定cpu的行为,既能让这期间运行的其他程序得到执行,又能在外设处理完成后,cpu及时获取到处理完成的消息?

 

可以按以下方式设定cpu行为:

  1. cpu以轮询(polling)的方式,每隔一段时间询问请求是否处理完成
  2. cpu下发请求后执行其他进程,磁盘等外设完成处理后,主动告知cpu

对于第1种方式,轮询会带来不必要的cpu消耗。第2种是现行的方式,外设通过电信号告知cpu的机制被称为中断(interrupt)

 

IRQ lineISR

不同设备发起的中断以不同的数值标识,这些标识中断的数值被称为中断请求线(interrupt request line, IRQ line)IRQ line与中断的关系相当于系统调用号与系统调用的关系。


cpu接收到中断后会将中断递交给内核处理,内核中有相应的函数完成中断处理(interrupt handlerinterrupt service routine,ISR)

 

中断上下文

每个中断对应一个中断处理函数,当中断处理函数被调用时,内核处于中断上下文(interrupt context)。不同于进程上下文(process context),中断处理过程中不能发生阻塞、休眠,即不进行进程调度,其基于以下原因:

  1. 中断处理函数需要较快的响应速度,尽快告知外设已接收到中断的消息,以让外设继续工作
  2. 中断处理中没有独立的函数栈,如果被调度,不会保存寄存器的值,因而回不到调度前执行的代码

 

中断屏蔽

从是否可屏蔽的角度,中断可分为:

  • 可屏蔽中断(maskable interrupt): 可通过设定中断屏蔽寄存器EFLAGSIF标志位关闭的中断
  • 不可屏蔽中断(non-maskable interrupt, NMI): 无法通过设置标志位屏蔽的中断,如电源掉电、时钟中断

对于可屏蔽中断,关闭中断的方式有以下3种:

  1. 使用cli(clear interrupt)指令,在全局范围关闭所有中断,使用sti(set interrupt)指令恢复
  2. 调用local_irq_disablelocal_irq_save关闭当前cpu中断,使用local_irq_enablelocal_irq_restore恢复
  3. 调用disable_irqdisable_irq_nosync在全局范围关闭某一特定中断线,使用enable_irqsynchronize_irq恢复

因中断是异步的,屏蔽中断可用于防止中断嵌套。非正常的中断关闭会带来很多不良结果,例如cpu不响应键盘中断时,用户无法使用键盘操作;又例如cpu不响应时钟中断,则不能进行进程调度,依赖于时钟中断的任务都无法完成,机器基本变成僵尸。

 

中断处理流程

外设发起中断,cpu接收中断并传递给内核处理,下图说明了该过程:

内核代码中,中断号被传递到do_IRQ处理,do_IRQ调用__do_IRQ函数,__do_IRQ中调用handle_IRQ_event函数,其调用相应的中断处理函数进行中断处理;处理完成,返回到do_IRQ函数,之后调用irq_exit,在irq_exit函数中,调用do_softirq处理软中断,软中断相关内容将在后文讲述。

 

proc接口

proc文件系统向用户开放了各个中断的情况:

  1. linux # cat /proc/interrupts
  2.          CPU0      CPU1
  3. 0:    178037   155726    IO-APIC-edge    timer
  4. 1:      49578      7372    IO-APIC-edge    i8042
  5. 8:           34         23    IO-APIC-edge    rtc0
  6. ……
  7. NMI:         0           0    Non-maskable interrupts
  8. LOC:   85437    86438    Local timer interrupts
  9. ……

以上输出显示了中断号、各个cpu接收中断次数、中断控制器(interrupt controller)和中断名称等信息,从/proc/stat也可查到中断相关信息。

 

cpu亲和力

多核cpu给中断处理方式带来了新的问题:若某个中断到来,应该由哪个核处理?


我们可以通过proc的接口,设定特定的cpu处理某个中断,这被称为cpu对中断的亲和力(affinity),利用/proc/irq/{interrupt num}/smp_affinity可以查看或设定cpu亲和力:

  1. linux# cat /proc/irq/[0,1]/smp_affinity
  2. 3
  3. 1

以上查询结果显示,0号、1cpu均可处理0号中断,而1号中断更倾向于交给1cpu处理。若要设定1cpu处理1号中断,可执行以下命令:

  1. linux # echo 2 > /proc/irq/1/smp_affinity


中断负载平衡

irqbalance为用户态下的一个守护进程,其可平均地在多核cpu间分发中断:

  1. linux # ps -elf | grep irqbalance | grep -v grep
  2. 1 S root 2408 1 0 80 0 - 2242 - Jul12 ? 00:00:01 /usr/sbin/irqbalance

 

下半部

内核接收到cpu传递过来的中断后,为做到快速响应外设,中断的处理被分成两部分:

  • 上半部(top halves):不得不做的工作放在上半部,也即中断处理程序中,例如告知外设接收到中断、将数据从外设中拷贝到内存
  • 下半部(bottom halves):不紧急的工作延后完成,如处理上半部中从外设拷贝来的数据

 

例如网卡接收数据过程中,首先网卡发起中断告诉cpu取数据,然后内核从网卡读取数据存入缓存,再之后内核解析数据并将数据送到应用层。如果以上工作都让中断处理程序来处理,过程太长,会导致丢失新来的中断。更优的方式是将以上工作分成两部分,从网卡读取数据到缓存由上半部完成,解析数据等较不紧急的工作由下半部完成。

 

下面我们看softirqtasklet两种下半部实现方式。

 

softirq

softirq2.3版本内核被引入,相关代码在中定义,内核中直接使用softirq的场景较少。

 

可用的softirq有以下几种,在中定义:

  1. enum
  2. {
  3. HI_SOFTIRQ=0,
  4. TIMER_SOFTIRQ,
  5. NET_TX_SOFTIRQ,
  6. NET_RX_SOFTIRQ,
  7. BLOCK_SOFTIRQ,
  8. BLOCK_IOPOLL_SOFTIRQ,
  9. TASKLET_SOFTIRQ,
  10. SCHED_SOFTIRQ,
  11. HRTIMER_SOFTIRQ,
  12. RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
  13. NR_SOFTIRQS
  14. };

提交一个softirq需要调用raise_softirq函数,raise_softirq调用raise_softirq_irqoff,该函数将相应软中断标识为pending、完成软中断提交。

 

标识为pending的软中断可在以下时机得到处理:

1. 硬中断处理完成、do_IRQ函数即将返回时处理

do_IRQ调用irq_exit函数,irq_exit调用invoke_softirq,即do_softirq函数,do_softirq调用__do_softirq。在__do_softirq函数中,最多处理10个软中断,若软中断的个数超过10,则调用wakeup_softirq唤醒ksoftirqd内核线程进行软中断处理

 

2. ksoftirqd内核线程被唤醒后处理

以上说明了ksoftirqd线程被唤醒的一种情况,wakeup_softirqd还会在raise_softirq_irqoff函数中被调用,也即软中断被提交之后,可唤醒ksoftirqd完成软中断处理

 

3. 显式地调用do_softirq进行软中断处理

在内核网络代码中,netif_rx_ni函数会主动调用do_softirq进行软中断处理

 

tasklet

tasklet基于softirq实现,其本质也是softirq,对应softirq枚举类型中的HI_SOFTIRQTASKLET_SOFTIRQHI_SOFTIRQ优先级较高。

 

相比softirq,即使相同类型的softirq也可同时在不同的cpu上处理,而相同类型的tasklet不可同时在不同cpu上处理,不同类型的tasklet可以。

 

可通过tasklet_scheduletasklet_hi_schedule提交tasklet,这两个函数最终调用raise_softirq_irqoff提交软中断。

 

tasklet提交的同样是软中断,所以还是由do_softirq函数完成tasklet的处理。



Reference: Chapter 7 and chapter 8, Linux kernel development.3rd.Edition




阅读(10569) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~