【linux内核分析与应用-陈莉君】中断处理机制

本文详细介绍了Linux内核中断处理机制,包括中断描述符表的初始化、陷阱门和系统门的设置、中断处理过程等关键环节,并探讨了中断返回时内核的调度策略。

目录

 

1.中断描述符表及初始化

2.初始化陷阱门和系统门

3.中断门的设置

4.中断处理过程

4.1中断和异常的硬件处理

4.2中断请求队列的建立

4.3中断处理程序的执行

4.4中断返回

5.一些问题

6.参考书与链接


1.中断描述符表及初始化

IDT(中断描述符表)放在内核的数据段中,它的起始地址放在中断描述符表寄存器IDTR中.
中断描述符相关源码:
我仅仅在
D:\005-代码\001-开源项目源码\004-内核源码\linux-4.10.1\linux-4.10.1\arch\x86\kernel\head_32.S这个版本中找到与老师图中差不多的代码

这里有两个汇编语句:
idt_descr:
	.word IDT_ENTRIES*8-1		# idt contains 256 entries--表示中断描述符表中包含256个中断描述符
	.long idt_table             # 表示中断描述符的入口地址


linux内核中,系统初始化阶段,首先要初始化可编程控制器,将中断描述符表的起始地址
装入到中断描述符表寄存器(IDTR)中.并且开始初始化表中的每一项.

当计算机运行在实模式的时候,中断描述符表初始化并且由??使用.
当真正进入linux内核之后,中断描述符表就被移动到内核的另外一个区域,
并且进入保护模式运行域初始化.

2.初始化陷阱门和系统门

对陷阱门的初始化有专门的的函数trap_init(),
它是用于设置中断描述符表最开始的19个陷阱门及一个系统门.
这些中断向量都是CPU保留用于异常处理的.
这里给出0,1,19号异常以及系统门的初始化.

3.中断门的设置

中断门的初始化使用ex_rq??函数进行初始化设置.
中断门设置必须跳过0x80这个系统调用向量,
中断处理程序的入口实际上是一个数组,叫做interrupter.
数组中的每一个元素都是指向中断处理例程的指针.
每一个中断处理例程属于内核中的代码段.
既然是内核中的代码段,也就是说它的段地址是存放于全局描述符表GDT中的.

4.中断处理过程

4.1中断和异常的硬件处理

当CPU执行了当前指令之后,CS和EIP这对寄存器中所包含的内容就是下一条要执行指令的虚地址,
在对下一条指令执行之前,实际上CPU首先要判断在执行当前指令的过程中是否发生了中断或者
异常,如果发生中断或者异常,那么CPU就要做以下的事情:
(1)确定异常或中断的向量i;
(2)通过IDTR寄存器找到IDT表,读取IDT表的第i项或者称作第i个门;
(3)从GDTR寄存器中获得GDT表的地址,结合中断描述符表中的段描述符在全局描述符获得中断
   处理程序对应的段描述符,从该段描述符中获得中断或者异常处理程序所在的段基地址,然后
   与偏移量相加得到中断处理程序的入口地址.
在此过程中需要进行"段"级和"门"级两步有效性检查,检查是否发生了特权级变化.

 

如果发生特权级变化,中断的处理过程中堆栈有什么样的变化呢?
当中断发生在用户态的时候,特权级发生了变化,用户态的特权级为3,
中断运行在内核态(特权级为0),此时就发生了特权级变化,因此就发生了
堆栈的变化,也就是说要从用户堆栈切换到内核堆栈;
当中断发生在内核态的时候,也就是说CPU在内核中运行的时候,不会更换堆栈.

从左边的图中可以看出,当从用户态堆栈切换到内核态堆栈的时候,首先将用户态堆栈的值
压入中断程序的内核态堆栈中,同时将EFLAGS寄存器自动压栈,然后把被中断进程的
返回地址压入堆栈,如果异常则将产生的错误码也保存在堆栈中.

如果特权级没有发生变化,则压入栈的内容如图右边所示,前面已经得到中断
处理程序的入口地址,于是CPU就调到中断或异常处理程序.

4.2中断请求队列的建立

由于硬件条件的限制,很多硬件设备是共享一条中断线的,为了方便处理,linux为
每条中断线设置了一个中断请求队列,因此我们介绍四个方面的内容,
(1)中断处理程序与中断服务例程;
(2)中断线共享的数据结构;
(3)注册中断服务例程;
(4)注销中断服务例程.

当外设发送中断请求的时候,通过中断控制器PIC的INT引脚将CPU
请求中断处理,则中断处理机制启动.其中包含两个部分,一个叫
中断处理程序,共享同一条中断线的所有中断请求有一个总的中断
处理程序;
中断服务例程又是什么呢?每一个中断请求都有自己单独的中断
服务例程.

下面我们介绍一下IRQ数据结构(中断描述符).
在内核中,对于每一个外设的IRQ都用一个struct irq_desc来描述,
我们称之为中断描述符.

内核中会有一个数据结构保存所有IRQ中断描述符信息,放在
irq_des[NR_IRQS]数组中,irq_des[NR_IRQS]是Linux内核中维护
IRQ资源的管理单元.它记录了某个IRQ对应的流控处理函数,中断控制器,
中断服务程序,IRQ自身的属性,资源等信息,是内核中中断子系统
的一个核心数组.


中断线共享的数据结构irqaction
每一个设备都会共享一个单独的irq,因此内核要维护多个irq描述符,其中每一个
描述符都涉及一个特定的硬件设备和一个特定的中断,在这个数据结构中有以下
一些图中的几个字段.


注册和注销中断服务例程
中断描述符表初始化以后必须通过request_irq函数将相应的中断服务例程挂入
中断请求队列中,也就是对它进行注册.request_irq函数原型如图.其中参数中的
中断服务例程被挂入到中断请求队列中,当我们关闭设备的时候,必须通过调用
free_irq函数释放所申请的中断请求号.

4.3中断处理程序的执行

CPU从中断控制器的一个端口取得中断向量i以后,就根据这个中断向量i从中断描述符
表中找到相应的中断门,然后从中断门获得中断处理程序的入口地址,接着判断是否要
进行堆栈的切换,接下来就会调用核心函数do_IRQ对所接受的中断进行应答,并且禁止
这条中断线上中断的发生,在这个过程中调用一个很重要的handle_IRQ_event函数来运行
对应的中断服务例程,关于这些源码的分析将会在下一讲的动手实践中继续查看分析.

4.4中断返回


中断返回(ret_from_intr)
当所有的中断处理程序在处理完之后都要返回,首先要判断进入中断前是在用户空间还是
在内核空间.
如果是在内核空间,则直接调用restore_all函数;
如果是在用户空间,则可能需要进行依次调度,如果不需要调度则可能有信号需要处理,
最后还是要调用restore_all函数.
restore_all和save_all是个相反的操作,将堆栈中的寄存器恢复.
最后调用iret(中断返回指令),将处理权交给CPU从中断返回时,CPU要调用回复中断现场的宏
彻底回复中断现场.

5.一些问题

关于中断,异常,系统调用的返回,我们看一下操作系统还做了什么,在这里抛出四个问题,如同.
1.内核调度与中断/异常/系统调用(统称中断)调用的关系如何?
2.信号处理与中断的关系如何?
3.内核抢占与中断的关系如何?
4.内核线程的调度有什么也别指出?中断返回时,内核线程会发生调度吗?

1.中断返回和异常返回的流程基本上是一致的,差别在于异常返回的时候需要先关一次中断,
因为在linux实现中,异常通过的是陷阱门,通过的时候不会自动关中断,但是中断使用的是
中断门,通过的时候会自动关闭中断.
中断/异常/系统调用返回的时候是进行调度的重要时机点,其中时钟中断返回的时是调度依赖的
最主要时机点,时钟中断处理函数中不会直接进行调度,只是根据相应的调度算法决定是否需要调度
以及需要调度的下一个任务,如果需要调度则设置调度标记,然后调度的实际执行是在终端返回的时候
去执行(检查终端标志,如果有终端标志则进行调度)

2.信号处理是当前进程从内核态返回用户态时进行的,在发生中断时,异常(包括系统调用)
都有可能从内核态返回到用户态,此时都是处理信号的时机,此时要注意,只有当前进程的信号
才能在此时得到处理,其他非正在运行的进程信号是无法处理的.

关于内核的抢占和内核线程的调度也非常有趣,这个问题留给读者自己思考了.

6.参考书与链接

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值