您的位置:首页 > 运维架构 > Linux

深入理解Linux内核(4)---中断和异常(x86平台)

2017-03-29 11:00 567 查看
本文是ULK中断和异常这一章的笔记,讲的是Intel 80x86的中断,与硬件紧密相关,ARM与其有很大不同。

中断通常分为同步中断(synchronous)和异步中断(asynchronous):

同步中断:是当指令执行时,由CPU控制单元产生的,只有在一条指令终止执行后CPU才会发生中断。

异步中断:是由其他硬件设备依照CPU时钟信号随机产生的。

Intel微处理器手册中,把同步和异步中断分别称为异常(exception)和中断(interrupt)。

中断是由间隔定时器和I/O设备产生的,而异常是由程序的错误产生的,或是由内核必须处理的异常条件产生的。1.中断信号的作用

中断或异常处理程序不是一个进程,而是一个内核控制路径,代表中断发生时正在运行的进程执行。中断处理是内核执行的最敏感的任务之一,必须满足以下约束:

①内核相应中断后的操作分两部分,关键的紧急的部分,内核立即执行,其余的推迟随后执行

②中断程序必须使内核控制路径能以嵌套的方式执行,当最后一个内核控制路径终止时,内核必须能恢复被中断进程的执行,或者如果中断信号已经导致了重新调度,内核能切换到另外的进程。

③尽管中断可以嵌套,但在临界区中,中断必须禁止。但内核必须尽可能限制这样的临界区,大部分时间应该以开中断的方式运行。

2.中断和异常

Intel文档把中断和异常分为以下几类

中断:可屏蔽中断(maskable interrupt)或非屏蔽中断。

异常:包括处理器探测异常(根据eip寄存器的值分为故障、陷阱、异常终止)、编程异常(由int或int3触发,也叫软中断)

每个中断和异常是由0~255之间的一个数来标识,Intel把这个8位的无符号整数叫做向量(vector),非屏蔽中断的向量和异常的向量是固定的,可屏蔽中断的向量可以通过控制器改变。

(1)IRQ和中断

每个能发出中断请求的硬件控制器都有一条IRQ输出线,所有IRQ线都与可编程中断控制器(Programmable
Interrupt Controller,PIC)的硬件电路输入引脚相连。PIC执行以下动作

①监视IRQ线,检查其信号,若有两条或两条以上的IRQ线上有信号,就选择引脚编号较小的IRQ线。

②如果一个信号在IRQ线上发生

a.把接收到的引发信号转换成相应的向量

b.把这个向量存放在PIC的一个I/O端口,从而允许CPU通过数据总线读取此向量。

c.把引发信号发送到处理器的INTR引脚,即产生一个中断。

d.等待,直到CPU通过把这个中断信号写进可编程中断控制器的一个I/O端口来确认它,当这个情况发生,清INTR线

③返回①

IRQ线从0开始顺序编号,第一条IRQ线IRQ0,与IRQn关联的Intel缺省向量是n+32.通过PIC可以修改IRQ和向量之间的映射。

可以通过修改PIC禁止IRQ,但禁止的中断是不会丢失的,它们一旦被激活,PIC就又把它们发送到CPU,这样CPU可以一次处理同一类型的IRQ,PIC禁止IRQ不同于屏蔽中断,屏蔽中断是被忽略的。

传统的PIC是由两片8259A芯片级联成的,可以支持15个IRQ线。

(2)高级可编程中断控制器

为了充分发挥SMP体系结构的并行性,能够把中断传递给系统中的每个CPU只管重要。基于此,Intel从Pentium III开始引入了一种叫I/O APIC(I/O Advanced
Programmable Interrupt Controller)的新组件,用来代替老式的8259A。

如图,一条APIC总线把“前端”I/O APIC链接到本地APIC,来自设备的IRQ线连接到I/O APIC,因此相对于本地APIC,I/O APIC起路由器作用。

除了在处理器之间分发中断外,多APIC系统还允许CPU产生处理器中断(interprocessor interrupt).

(3)异常

80x86发布了大约20中不同的异常,内核必须为每种异常提供一个专门的异常处理程序,对于某些异常,CPU在执行异常处理程序前会产生一个硬件出错码,并压入内核态堆栈。

0—“Divde error”除0故障

1—“Debug”陷阱或故障

2—未用

3—“breakpoint”陷阱,由int3引起





(4)中断描述符表(Interrupt
Descriptor Table,IDT)

IDT是一个系统表,它与每一个中断或异常向量相联系,每一个向量在表中有相应的中断或异常处理程序的入口地址。内核在允许中断发生前,必须适当地初始化IDT.

IDT表中每一项对应一个中断或异常向量,每个向量由8个字节组成,因此最多需要256×8=2048字节来存放IDT.

IDT包含三种类型的描述符

任务门(task gate):存放当中断信号发生时,必须取代当前进程的那个进程的TSS选择符

中断门(interrupt gate):包含段选择符和中断或异常处理程序段内偏移量。当控制权转移到一个适当的段时,处理器清IF标志,从而关闭将来会发生的可屏蔽中断。

陷阱门(Trap gate)与中断门相似,只是控制权传递到一个适当的段时处理器不修改IF标志。

Linux利用中断门处理中断,利用陷阱门处理异常。

(5)中断和异常的硬件处理

假定内核已被初始化,CPU在保护模式下运行,当执行一条指令后,cs和eip这对寄存器包含下一条将要执行的指令的逻辑地址。在处理这条指令之前,控制单元会检查允许前一天指令时是否已经发生了一个中断或异常,若发生,那么控制单元执行下列操作:

①确定与中断或异常关联的向量i(0handler(irq, action->dev_id, regs); action = action->next; } while (action);

Action指向irqaction数据结构链表的开始,而irqaction表示接受中断后要采取的操作。

c.用cil指令禁止本地中断

d.通过返回局部变量retval的值而终止,也就是说,如果没有与中断对应的中断服务例程返回0,否则返回1.

中断服务例程参数(eax,edx,ecx寄存器传递)

irq: IRQ号

dev_id:设备标识符

regs:指向内核(异常)栈的pt_regs结构的指针。

⑩IRQ线的动态分配

同一条IRQ线可以让几个硬件设备使用,即使想这些设备不允许IRQ共享,技巧是使这些硬件设备的活动串行化,以便一次只能有一个设备拥有这个IRQ线。

(2)处理器间中断(IPI)

处理器间中断允许一个CPU向系统中的其他CPU发送中断信号,IPI不是通过IRQ线传输的,而是作为信号直接放在连接所有CPU本地APIC的总线上。

在多处理器系统中,Linux定义了三种处理器间中断

①CALL_FUNCTION_VECTOR(向量0xfb)

发往除本身之外的所有CPU,强制这些CPU允许发送者传递过来的函数。

②RESCHEDULE_VECTOR(向量0xfc)

处理程序reschedule_interrupt()限定自己来应答中断,当从中断返回时,所有的重新调度都自动进程。

③INVALIDATE_TLB_VECTOR(向量0xfd)

发往除本身之外的所有CPU,强制他们的转换后缓冲器(TLB)变为无效。

7.软中断及tasklet

软中断:常常表示可延迟函数的所有种类,是静态的,在编译时定义。ISR须是可重入函数且必须明确地使用自旋锁保护其数据。

中断上下文:表示内核当前正在执行一个中断处理程序或一个可延迟的函数。

tasklet:是在软中断之上实现的,可以在运行时进行tasklet的分配和初始化,ISR可以是非重入函数,相同类型的tasklet总是被串行的执行,类型不同的tasklet可以在几个CPU上并发执行,但不能在两个CPU上同时运行相同类型的tasklet。

一般而言,在可延迟函数上可以执行四种操作:

①初始化(initialization):定义一个新的可延迟函数,通常在内核自身初始化或加载模块时进行

②激活(activation):标记一个可延迟函数的“挂起”(在可延迟的下一轮调度中执行),激活可以在任何时候进行,及时正在处理中断。

③屏蔽(masking):有选择地屏蔽一个可延迟函数,这样,及时它被激活,内核也不执行。

④执行(execution):执行一个挂起的可延迟函数,执行是在特定的时间进行的。

激活和执行总是捆绑在一起,由给定CPU激活的一个可延迟函数必须在同一个CPU上执行。

(1)软中断

①Linux 2.6使用了有限个软中断,目前只定义了六种

Table 4-9. Softirqs used in Linux 2.6
Softirq
Index (priority)
Description
HI_SOFTIRQ
0
Handles high priority tasklets
TIMER_SOFTIRQ
1
Tasklets related to timer interrupts
NET_TX_SOFTIRQ
2
Transmits packets to network cards
NET_RX_SOFTIRQ
3
Receives packets from network cards
SCSI_SOFTIRQ
4
Post-interrupt processing of SCSI commands
TASKLET_SOFTIRQ
5
Handles regular tasklets
软中断的下标决定了它的优先级:第下标以为着高优先级,软中断函数从0开始执行。

②软中断使用的数据结构

Softirq_vec数组,该数组包含类型为softirq_action的32个元素。softirq_action包括两个字段,指向软中断函数的一个action指针和指向软中断函数需要的通用数据结构data指针。另一个关键字段是32位的preempt_count字段,用它来跟踪内核抢占和内核控制路径的嵌套,该字段存放在每个进程描述符的thread_info字段中,preempt_count字段的编码表示三个不同的计数器和一个标志。

Bits
Description
07
Preemption counter (max value = 255)
815
Softirq counter (max value = 255).
1627
Hardirq counter (max value = 4096)
28
PREEMPT_ACTIVE flag
当内核代码明确不允许发生抢占(抢占计数器不等于0)或内核正在中断上下文运行时,必须禁用内核的抢占功能。

宏in_interrupt()检查current_thread_info()->preempt_cout字段的硬中断计数器和软中断计数器。

CPU的32位掩码,存放在irq_cpustat_t数据结构的__softirq_pending字段,内核使用宏local_softirq_pending(),选择本地CPU的中断掩码。

③处理软中断

Open_softirq()函数处理软中断的初始化,使用三个参数:软中断下标、指向要执行的软中断函数的指针及指向软中断函数使用的数据结构的指针。

Raise_softirq()函数用来激活软中断。

内核应该周期性地(但又不能太频繁地)检查活动(挂起)的软中断,在内核代码几个点上检查。

a.当内核调用local_bh_enable()函数记过本地CPU的软中断时

b.当do_IRQ()完成I/O中断处理或调用irq_exit()宏时

c.如果系统使用I/O APIC,则当smp_apic_timer_interrupt()函数处理完本地定时器中断时。

d.在多处理器系统中,当CPU处理完成CALL_FUNCTION_VECTOR处理器间中断所触发函数时。

e.当一个特殊的ksoftirqd/n内核线程被唤醒时。

如果在一个检查点(local_softirq_pending()不为0)检测到挂起的软中断,内核就调用do_softirq()来处理它们。

④ksoftirqd内核线程

每个CPU都有自己的ksoftirqd/n内核线程(n为CPU的逻辑号),每个ksoftirqd/n内核线程都运行ksoftirqd()函数。 点击(此处)折叠或打开
for(;;) {

set_current_state(TASK_INTERRUPTIBLE );

schedule( ); /* now in TASK_RUNNING state */

while (local_softirq_pending( )) {

preempt_disable();

do_softirq( );

preempt_enable();

cond_resched( );

}

}
当内核线程被唤醒时,就检查local_softirq_pending()中的软中断掩码并在必要时调用do_softirq(),如果没有挂起的软中断,吧当前进程状态设置为TASK_INTERRUPTIBLE,随后如果当前进程需要就调用cond_resched()函数来实现进程切换。

软中断可以重新激活自己,实际上网络软中断和tasklet软中断都可以这么做。

(2)tasklet

Tasklet是I/O驱动程序中实现可延迟函数的首选,其建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。 点击(此处)折叠或打开
struct tasklet_struct

{

struct tasklet_struct *next;//指向链表中下一个描述符的指针

unsigned long state;//tasklet的状态

atomic_t count;//锁计数器

void (*func)(unsigned long);//指向tasklet函数的指针

unsigned long data;//可以由tasklet函数使用

};

用tasklet_init()初始化tasklet_struct,用tasklet_schedule()或tasklet_hi_schedule()激活。

除非tasklet函数重新激活自己,否则tasklet的每次激活之多触发tasklet函数的一次执行。

8.工作队列工作队列的数据机构workqueue_struct。一个工作队列必须明确的在使用前创建,宏为: 点击(此处)折叠或打开
struct workqueue_struct *create_workqueue(const char *name);

struct workqueue_struct *create_singlethread_workqueue(const char *name);
Tasklet和工作队列的详细分析见http://blog.chinaunix.net/uid-24708340-id-3035286.html

9.从中断和异常返回

终止阶段主要目的是,恢复某个程序的执行,在这样做之前,需要考虑:

a.内核控制路径并发执行的数量:

如果只有一个,那么CPU必须切换到用户态。

b.挂起进程的切换请求:

如有任何请求,内核必须执行进程调度,否则,吧控制权还给当前进程。

c.挂起的信号:

如果一个信号发送到当前进程,就必须处理它。

d.单步执行模式:

如果掉是程序正在跟踪当前进程的执行,就必须在进程切换回到用户态之前恢复但不执行。

e.Virtual-8086模式

如果CPU处于virtual-8086模式,当前进程正在执行原来的实模式程序,因为必须以特殊的方式处理这种情况。


需要使用一些标志来记录挂起进程切换的请求、挂起信号和单步执行,这些标志存放在thread_info描述符的flags字段中。

Table 4-15. The flags field of the thread_info
descriptor (continues)

Flag name
Description
TIF_SYSCALL_TRACE
System calls are being traced
TIF_NOTIFY_RESUME
Not used in the 80 x 86 platform
TIF_SIGPENDING
The process has pending signals
TIF_NEED_RESCHED
Scheduling must be performed
TIF_SINGLESTEP
Restore single step execution on return to User Mode
TIF_IRET
Force return from system call via iret rather than sy***it
TIF_SYSCALL_AUDIT
System calls are being audited
TIF_POLLING_NRFLAG
The idle process is polling the TIF_NEED_RESCHED flag
TIF_MEMDIE
The process is being destroyed to reclaim memory (see the section "The
Out of Memory Killer" in Chapter
17)
从技术上说,完成所有事情的汇编代码并不是一个函数,只是一个代码片段,有两个不同的入口点,分别叫做ret_from_intr()中断处理时和ret_from_exception()异常处理结束时.

②恢复内核控制路径

③检查内核抢占

如需要进行进程切换,就调用preempt_schedule_irq()函数,schedule()

④恢复用户态程序

⑤检查重调度标志

⑥处理挂起信号,虚拟8086模式和单步执行。


<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>

阅读(5754) | 评论(3) | 转发(11) |

0
上一篇:深入理解Linux内核(3)---进程

下一篇:转来的,我每次看都有收获

相关热门文章

linux 常见服务端口

xmanager 2.0 for linux配置

【ROOTFS搭建】busybox的httpd...

openwrt中luci学习笔记

什么是shell

linux dhcp peizhi roc

关于Unix文件的软链接

求教这个命令什么意思,我是新...

sed -e "/grep/d" 是什么意思...

谁能够帮我解决LINUX 2.6 10...

给主人留下些什么吧!~~



lmnos2012-10-08 09:46:17
leon_yu: 这个是笔误,已经改正了,谢谢.....知道是笔误,PC里好像没有8159这个器件
回复 | 举报



leon_yu2012-10-08 09:21:08
lmnos: 是8259吧。不是8159。我的LMOS内核可以在初始化时自动检测是8259还是io-apic

并自动配置好其中一个中断控制器.....这个是笔误,已经改正了,谢谢
回复 | 举报



lmnos2012-10-07 17:00:53
是8259吧。不是8159。我的LMOS内核可以在初始化时自动检测是8259还是io-apic

并自动配置好其中一个中断控制器
回复 | 举报

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: