中断及任务调度管理(linux网络协议栈笔记)

本文介绍了Linux中断处理机制,包括中断、软中断模型,中断系统与软中断的关联,以及设备驱动如何挂接ISR。中断处理涉及中断向量、中断请求号,Linux内核通过初始化设置中断处理函数。此外,文章还探讨了软中断,如tasklet,以及其在多CPU环境中的并发执行特性。
摘要由CSDN通过智能技术生成

中断及任务调度管理

Linux 书籍中常说的 BottomHalf 已然不见了,它们被转成 tasklets,这是支持 SMP 的。但其思想基本 一致。

中断及软中断模型

我们在此不会对中断及异常的原理和机制做深入的介绍。但必须要作出一些说明,因为这是理解 Linux 内核与其它嵌入式/实时操作系统的不同,以及理解网络协议栈收报文的基础。 Linux 支持 CPU 的外部硬件中断和内部中断。严格来说,内部中断包含系统调用陷入和异常,在一 般的嵌入式操作系统(比如 VxWorks)中是没有系统调用这个概念的,所以对于一直从事嵌入式软件开 发的人初次进入到大型操作系统(比如 Linux 和 Windows)开发环境中,会面临内核空间与用户空间概 念上的困惑。其实说到底,所谓系统调用就是软件有计划地调用 CPU 提供的特殊指令,触发 CPU 内部 产生一个中断,于是完成一次核内核外运行空间的切换,具体可以参考许多书籍。而所谓异常就是软件 无意的执行了一个非法指令(比如除 0)从而造成 CPU 内部引发一次中断。
外部中断特指外部设备发出的中断信号。但这几种中断的 CPU 处理过程基本相同,即:在执行完当 前指令后,或在执行当前指令期间,根据中断源所提供的“中断向量”,在内存中找到相应的 ISR(中断 服务例程)然后调用之。
不管是内部还是外部中断,系统都会根据接收到的中断信息,查询 idt 表。idt 表依照中断源的位置按序组成,并对应中断服务程序(以及异常处理程序)的入口地址。Linux 系统在初始化页式虚存管理的 初始化以后,便调用 trap_init 和 init_IRQ 两个函数进行中断机制的初始化。我们只介绍init_IRQ

中断系统和软中断

中断向量?中断请求号?这是个问题。 现在分析试图给大家解释一下,看看是不是这样的:IRQ 是设备相关的号码,一般生产厂商都会使 自己的设备分配到一个合适的号码。而中断向量就纯粹是操作系统中关于如何处理中断的内存组织结构, 它们之间存在某种映射关系,这种关系是由 CPU 体系结构以及操作系统决定的。那么在 IA32 体系的 Linux 中,是一种直接映射的关系,所有的 IRQ 号产生的中断全部映射到从 INT_vec[32]开始的内存中。为什么 要从第 32 个单元开始呢?

这里写图片描述

上图中彩色部分都是系统能处理的中断,intel CPU 保留使用的中断向量是 0~32,根本不可能有哪 一种设备会使用这个区域的中断向量,这一部分就是我们常说的异常处理函数,还有一个比较特殊的中 断向量号 0x80(即 128)就是系统调用号,由于不可能由外部设备引发这类中断,它们就被统称为内部 中断,这就是为什么要从第 32 个单元开始的原因。内核用调用 trap_init函数挂接与之相应的中断处理函 数。接着系统调用init_IRQ 函数来初始化外部中断向量,其中断处理函数的挂接由各驱动程序自己完成。 由上图可以看出中断向量和中断请求号是相关但却不是一个东西:前者是内核中存在的一块内存,专门 存放中断处理函数的地址(指逻辑上,具体实现比较复杂) ,而后者就是一个概念,在内核中不必存在这 么一种变量,它可能就是中断向量的下标。
在 2.6 的内核中,中断相关的宏已经变化了,2.4 内核中的中断概念请看《Linux 内核源代码情景分 析 》。 在 2.6 内核的 entry.S文件中,有一 个 interrupt 的定义, 它 放在.data 节中,然 后 ,在 include/asm-i386/hw_irq.h 中引用这个变量,最后在 arch/i386/kernel/i8259.c 中初始化这个变量。
下面两个代码片断是 2.4 内核关于这个 interrupt 变量的初始化:

 1 #define IRQ(x,y) \ 
 2 IRQ##x##y##_interrupt  
 3  
 4 #define IRQLIST_16(x) \  
 5 IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \  
 6 IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \  
 7 IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \  
 8 IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) 
 9
 10 void (*interrupt[NR_IRQS])(void) = {  
 11 IRQLIST_16(0x0),  
 12 ... 

经过编译器的预处理,interrupt 这个函数指针数组变成:

1 void (*interrupt[NR_IRQS])(void) = {  
2     IRQ0x00_interrupt,  
3     IRQ0x01_interrupt,  
4     IRQ0x02_interrupt,  
5    ……  
6    IRQ0x0f_interrupt,  
7 }

从代码中看出,这样的初始化不太灵活,扩展性比较差。下面给出 2.6 内核关于 interrupt 的使用方式。 首先在 entry.S 中汇编代码如下:
这里写图片描述

hw_irq.h 中有这样的定义:extern void (*interrupt[NR_IRQS])(void);在此,NR_IRQS224。具体 的初始化如下:

 1 void __init init_IRQ(void) 
 2 {  
 3     int i;  
 4  
 5 /* all the set up before the call gates are initialised */  
 6     pre_intr_init_hook();  
 7  
 8 /*  
 9 * 扫描整个中断向量表  
 10 */  
 11    for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {  
 12        int vector = FIRST_EXTERNAL_VECTOR + i;  
 13        if (i >= NR_IRQS)  
 14            break;  
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值