当前位置: 首页 > news >正文

Linux中断初始化init_IRQ的实现

中断初始化init_IRQ

void __init init_IRQ(void)
{int i;/* all the set up before the call gates are initialised */pre_intr_init_hook();/** Cover the whole vector space, no vector can escape* us. (some of these will be overridden and become* 'special' SMP interrupts)*/for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {int vector = FIRST_EXTERNAL_VECTOR + i;if (i >= NR_IRQS)break;if (vector != SYSCALL_VECTOR)set_intr_gate(vector, interrupt[i]);}/* setup after call gates are initialised (usually add in* the architecture specific gates)*/intr_init_hook();/** Set the clock to HZ Hz, we already have a valid* vector now:*/setup_pit_timer();/** External FPU? Set up irq13 if so, for* original braindamaged IBM FERR coupling.*/if (boot_cpu_data.hard_math && !cpu_has_fpu)setup_irq(FPU_IRQ, &fpu_irq);irq_ctx_init(smp_processor_id());
}

1. 函数功能

初始化x86系统的中断处理机制,设置中断描述符表,配置硬件中断控制器,为系统中断处理建立基础设施

2. 代码详细解释

2.1. 函数定义和变量声明

void __init init_IRQ(void)
{int i;
  • void __init:没有返回值,__init表示该函数只在系统初始化阶段使用
  • init_IRQ:函数名,初始化中断请求
  • int i:循环计数器,用于遍历中断向量

2.2. 前置中断初始化钩子

        /* all the set up before the call gates are initialised */pre_intr_init_hook();
  • 在调用门初始化之前的所有设置
  • pre_intr_init_hook():架构特定的前置中断初始化钩子函数
  • 通常用于在设置中断门之前进行必要的架构特定准备工作

2.3. 设置中断向量门

        /** Cover the whole vector space, no vector can escape* us. (some of these will be overridden and become* 'special' SMP interrupts)*/for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
  • 覆盖整个向量空间

循环控制:

  • i = 0:从第一个外部向量开始
  • i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR):遍历所有外部向量
  • NR_VECTORS:总向量数量(通常256)
  • FIRST_EXTERNAL_VECTOR:第一个外部向量号(通常32)

2.4. 计算向量号和边界检查

                int vector = FIRST_EXTERNAL_VECTOR + i;if (i >= NR_IRQS)break;
  • vector = FIRST_EXTERNAL_VECTOR + i:计算实际的中断向量号
  • if (i >= NR_IRQS):如果超过支持的IRQ数量则退出循环
  • NR_IRQS:系统支持的IRQ总数

2.5. 设置中断门(排除系统调用向量)

                if (vector != SYSCALL_VECTOR)set_intr_gate(vector, interrupt[i]);}
  • if (vector != SYSCALL_VECTOR):排除系统调用向量
  • SYSCALL_VECTOR:系统调用中断向量(通常0x80)
  • set_intr_gate(vector, interrupt[i]):设置中断门
    • vector:中断向量号
    • interrupt[i]:对应的中断处理函数地址

2.6. 后置中断初始化钩子

        /* setup after call gates are initialised (usually add in* the architecture specific gates)*/intr_init_hook();
  • 在调用门初始化之后的设置
  • intr_init_hook():架构特定的后置中断初始化钩子函数
  • 用于设置架构特定的中断门或进行后续初始化

2.7. 配置PIT定时器

        /** Set the clock to HZ Hz, we already have a valid* vector now:*/setup_pit_timer();
  • 将时钟设置为HZ,现在已经有了有效的向量
  • setup_pit_timer():设置可编程间隔定时器(PIT)
  • 配置定时器中断频率(通常100Hz或1000Hz)
  • 为系统提供时间滴答和调度时间片

2.8. 设置FPU中断

        /** External FPU? Set up irq13 if so, for* original braindamaged IBM FERR coupling.*/if (boot_cpu_data.hard_math && !cpu_has_fpu)setup_irq(FPU_IRQ, &fpu_irq);
  • boot_cpu_data.hard_math:CPU支持数学协处理器
  • !cpu_has_fpu:但没有内置FPU(使用外部协处理器)
  • setup_irq(FPU_IRQ, &fpu_irq):设置FPU中断
    • FPU_IRQ:FPU中断号(通常13)
    • &fpu_irq:FPU中断处理结构

2.9. 初始化中断上下文

        irq_ctx_init(smp_processor_id());
}
  • irq_ctx_init(smp_processor_id()):初始化当前CPU的中断上下文
  • smp_processor_id():获取当前CPU的ID
  • 为中断处理准备每CPU的堆栈和上下文信息

3. 中断向量空间布局

x86中断向量分配:

0-31:   处理器异常(除零、页错误等)
32:     可编程中断控制器(PIC)IRQ0 - 定时器
33:     PIC IRQ1 - 键盘
34-47:  PIC IRQ2-15 - 其他设备
48-255: 其他用途(APIC、MSI、系统调用等)

4. 关键配置详解

中断门设置:

// set_intr_gate 设置的中断门特性:
- 特权级0:只有内核可以调用
- 中断禁用:进入处理程序时自动禁用中断
- 使用内核代码段

PIT定时器配置:

// setup_pit_timer() 配置:
- 中断频率:HZ(通常1001000Hz)
- 通道0:系统定时器
- 模式3:方波发生器
- 为系统调度提供时间基准

配置PIT定时器setup_pit_timer

void setup_pit_timer(void)
{extern spinlock_t i8253_lock;unsigned long flags;spin_lock_irqsave(&i8253_lock, flags);outb_p(0x34,PIT_MODE);          /* binary, mode 2, LSB/MSB, ch 0 */udelay(10);outb_p(LATCH & 0xff , PIT_CH0); /* LSB */udelay(10);outb(LATCH >> 8 , PIT_CH0);     /* MSB */spin_unlock_irqrestore(&i8253_lock, flags);
}

1. 函数功能

配置PIT定时器,设置定时器的工作模式和频率,为系统提供时间滴答中断

2. 代码详细解释

2.1. 函数定义

void setup_pit_timer(void)
  • void:没有返回值
  • setup_pit_timer:函数名,设置PIT定时器

2.2. 外部变量声明

        extern spinlock_t i8253_lock;
  • extern spinlock_t i8253_lock:声明外部定义的自旋锁变量
  • i8253_lock是8253/8254 PIT芯片的访问锁,防止并发访问
  • 8253是8254的前身,软件兼容

2.3. 局部变量声明

        unsigned long flags;
  • unsigned long flags:用于保存中断状态的变量
  • 在锁操作中用于保存和恢复中断使能状态

2.4. 获取锁并保存中断状态

        spin_lock_irqsave(&i8253_lock, flags);
  • spin_lock_irqsave(&i8253_lock, flags):获取自旋锁并保存当前中断状态
  • &i8253_lock:指向PIT锁的指针
  • flags:用于保存中断状态的变量
  • 作用:防止其他CPU或中断上下文同时访问PIT寄存器

2.5. 设置定时器模式

        outb_p(0x34,PIT_MODE);          /* binary, mode 2, LSB/MSB, ch 0 */

命令字 0x34 的二进制分解:

0x34 = 00110100 (二进制)
位7-6: 00 = 选择通道0
位5-4: 11 = 读写先低字节后高字节
位3-1: 010 = 模式2 (速率发生器)
位0:   0 = 二进制计数

参数:

  • outb_p:带暂停的端口输出(确保I/O操作完成)
  • 0x34:模式控制字
  • PIT_MODE:控制字寄存器端口(通常0x43)

2.6. 短暂延迟

        udelay(10);
  • udelay(10):延迟10微秒
  • 确保PIC有足够时间处理上一个命令
  • 避免背靠背的I/O操作导致问题

2.7. 写入定时器值的低字节

        outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
  • outb_p:带暂停的端口输出
  • LATCH & 0xff:定时器值的低8位
  • PIT_CH0:通道0数据端口(通常0x40)
  • LATCH:预定义的定时器计数值,决定中断频率

2.8. 再次延迟

        udelay(10);
  • 再次延迟10微秒,确保低字节写入完成

2.9. 写入定时器值的高字节

        outb(LATCH >> 8 , PIT_CH0);     /* MSB */
  • outb:普通端口输出(不需要暂停,因为后面就是解锁)
  • LATCH >> 8:定时器值的高8位
  • PIT_CH0:通道0数据端口(通常0x40)

2.10. 释放锁并恢复中断状态

        spin_unlock_irqrestore(&i8253_lock, flags);
}
  • spin_unlock_irqrestore(&i8253_lock, flags):释放锁并恢复中断状态
  • &i8253_lock:指向PIT锁的指针
  • flags:之前保存的中断状态
  • 作用:恢复系统到锁获取之前的状态

3. 定时器模式详解

模式2(速率发生器):

// 工作方式:
1. 从初始值开始递减计数
2. 达到1时输出一个时钟宽度的低脉冲
3. 自动重新加载初始值
4. 产生周期性的中断// 特点:
- 自动重载,无需软件干预
- 产生稳定的周期性中断
- 适合系统定时器用途

4. 系统影响

配置后的效果:

// 定时器开始工作后:
1.1/HZ秒产生一次中断(IRQ0)
2. 内核定时器中断处理程序被调用
3. 更新系统时间(jiffies)
4. 执行定时器回调
5. 进行进程调度

中断上下文栈初始化irq_ctx_init

void irq_ctx_init(int cpu)
{union irq_ctx *irqctx;if (hardirq_ctx[cpu])return;irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE];irqctx->tinfo.task              = NULL;irqctx->tinfo.exec_domain       = NULL;irqctx->tinfo.cpu               = cpu;irqctx->tinfo.preempt_count     = HARDIRQ_OFFSET;irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);hardirq_ctx[cpu] = irqctx;irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE];irqctx->tinfo.task              = NULL;irqctx->tinfo.exec_domain       = NULL;irqctx->tinfo.cpu               = cpu;irqctx->tinfo.preempt_count     = SOFTIRQ_OFFSET;irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);softirq_ctx[cpu] = irqctx;printk("CPU %u irqstacks, hard=%p soft=%p\n",cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
}

1. 函数功能

为指定CPU初始化硬中断和软中断的专用堆栈,建立中断处理的基础设施

2. 代码详细解释

2.1. 函数定义

void irq_ctx_init(int cpu)
  • void:没有返回值
  • irq_ctx_init:函数名,中断上下文初始化
  • int cpu:要初始化中断堆栈的CPU编号

2.2. 局部变量声明

        union irq_ctx *irqctx;
  • union irq_ctx *irqctx:中断上下文联合体指针
  • 这个联合体包含线程信息和其他上下文数据

2.3. 检查是否已初始化

        if (hardirq_ctx[cpu])return;
  • if (hardirq_ctx[cpu]):检查该CPU的硬中断上下文是否已存在
  • 如果已经初始化过,直接返回,避免重复初始化
  • hardirq_ctx[]:全局数组,存储每个CPU的硬中断上下文指针

2.4. 获取硬中断堆栈地址

        irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE];
  • hardirq_stack:硬中断堆栈数组
  • cpu*THREAD_SIZE:计算该CPU在堆栈数组中的偏移
  • THREAD_SIZE:每个线程堆栈的大小
  • (union irq_ctx*):将堆栈地址转换为中断上下文指针

2.5. 初始化硬中断上下文

        irqctx->tinfo.task              = NULL;irqctx->tinfo.exec_domain       = NULL;irqctx->tinfo.cpu               = cpu;irqctx->tinfo.preempt_count     = HARDIRQ_OFFSET;irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);

task = NULL

  • 中断上下文不属于任何具体的任务
  • 表示这是中断处理堆栈,不是进程堆栈

exec_domain = NULL

  • 执行域为空,中断处理使用内核默认执行域

cpu = cpu

  • 记录这个中断上下文属于哪个CPU

preempt_count = HARDIRQ_OFFSET

  • 设置抢占计数为硬中断偏移量
  • 表示当前处于硬中断上下文,禁止抢占

addr_limit = MAKE_MM_SEG(0)

  • 设置地址限制为内核空间(0)
  • 中断处理程序只能访问内核地址空间

2.6. 保存硬中断上下文指针

        hardirq_ctx[cpu] = irqctx;
  • 将初始化好的硬中断上下文指针保存到全局数组中
  • 后续中断处理时可以通过这个指针快速访问

2.7. 获取软中断堆栈地址

        irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE];
  • softirq_stack:软中断堆栈数组
  • 同样计算该CPU在软中断堆栈数组中的位置
  • 准备初始化软中断上下文

2.8. 初始化软中断上下文

        irqctx->tinfo.task              = NULL;irqctx->tinfo.exec_domain       = NULL;irqctx->tinfo.cpu               = cpu;irqctx->tinfo.preempt_count     = SOFTIRQ_OFFSET;irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
  • preempt_count = SOFTIRQ_OFFSET:软中断偏移量
  • 表示处于软中断上下文,有不同于硬中断的抢占规则

2.9. 保存软中断上下文指针

        softirq_ctx[cpu] = irqctx;
  • 保存软中断上下文指针到全局数组
  • 软中断处理时使用这个堆栈

2.10. 打印调试信息

        printk("CPU %u irqstacks, hard=%p soft=%p\n",cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
}
  • 输出CPU的中断堆栈信息
  • CPU %u:CPU编号
  • hard=%p:硬中断堆栈地址
  • soft=%p:软中断堆栈地址
  • 用于调试和验证初始化结果

3. 为什么需要独立的中断堆栈?

堆栈隔离

// 不使用独立堆栈的问题:
- 中断可能发生在任何进程的堆栈上
- 中断处理可能耗尽进程堆栈空间
- 导致堆栈溢出或数据损坏// 使用独立堆栈:
- 中断有专用的堆栈空间
- 不会影响进程堆栈
- 提高系统可靠性

性能优化

// 独立堆栈的好处:
- 更好的缓存局部性
- 减少堆栈切换开销
- 中断处理更高效

调试支持

// 通过堆栈区分:
- 可以清晰识别中断上下文
- 堆栈跟踪更清晰
- 便于问题诊断

4. 实际使用场景

硬中断处理:

// 当硬件中断发生时:
1. CPU自动切换到硬中断堆栈
2. preempt_count设置为HARDIRQ_OFFSET
3. 执行中断处理程序
4. 完成后恢复原堆栈

软中断处理:

// 当处理软中断时:
1. 切换到软中断堆栈
2. preempt_count设置为SOFTIRQ_OFFSET  
3. 执行软中断处理程序
4. 完成后恢复

设置中断处理程序setup_irq

int setup_irq(unsigned int irq, struct irqaction * new)
{struct irq_desc *desc = irq_desc + irq;struct irqaction *old, **p;unsigned long flags;int shared = 0;if (desc->handler == &no_irq_type)return -ENOSYS;/** Some drivers like serial.c use request_irq() heavily,* so we have to be careful not to interfere with a* running system.*/if (new->flags & SA_SAMPLE_RANDOM) {/** This function might sleep, we want to call it first,* outside of the atomic block.* Yes, this might clear the entropy pool if the wrong* driver is attempted to be loaded, without actually* installing a new handler, but is this really a problem,* only the sysadmin is able to do this.*/rand_initialize_irq(irq);}/** The following block of code has to be executed atomically*/spin_lock_irqsave(&desc->lock,flags);p = &desc->action;if ((old = *p) != NULL) {/* Can't share interrupts unless both agree to */if (!(old->flags & new->flags & SA_SHIRQ)) {spin_unlock_irqrestore(&desc->lock,flags);return -EBUSY;}/* add new interrupt at end of irq queue */do {p = &old->next;old = *p;} while (old);shared = 1;}*p = new;if (!shared) {desc->depth = 0;desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |IRQ_WAITING | IRQ_INPROGRESS);if (desc->handler->startup)desc->handler->startup(irq);elsedesc->handler->enable(irq);}spin_unlock_irqrestore(&desc->lock,flags);new->irq = irq;register_irq_proc(irq);new->dir = NULL;register_handler_proc(irq, new);return 0;
}

1. 函数功能

注册中断处理程序到指定的IRQ线,支持中断共享,管理中断处理链

2. 代码详细解释

2.1. 函数定义和变量声明

int setup_irq(unsigned int irq, struct irqaction * new)
{struct irq_desc *desc = irq_desc + irq;struct irqaction *old, **p;unsigned long flags;int shared = 0;
  • int:返回错误码,0表示成功
  • setup_irq:函数名,设置中断处理程序
  • unsigned int irq:中断号
  • struct irqaction * new:新的中断处理动作结构
  • desc = irq_desc + irq:获取该IRQ的描述符指针
  • old, **p:用于遍历现有中断处理链的指针
  • flags:保存中断状态的变量
  • shared:共享中断标志,初始为0(非共享)

2.2. 检查IRQ是否可用

        if (desc->handler == &no_irq_type)return -ENOSYS;
  • desc->handler == &no_irq_type:检查该IRQ是否有有效的中断控制器处理
  • no_irq_type:表示该IRQ不可用的虚拟处理程序
  • return -ENOSYS:如果IRQ不可用,返回"功能未实现"错误

2.3. 随机数熵池初始化

        if (new->flags & SA_SAMPLE_RANDOM) {rand_initialize_irq(irq);}
  • new->flags & SA_SAMPLE_RANDOM:检查是否使用该中断作为随机数源
  • rand_initialize_irq(irq):初始化该IRQ的随机数熵池
  • 在锁外调用:因为这个函数可能睡眠

2.4. 开始原子操作

        /** The following block of code has to be executed atomically*/spin_lock_irqsave(&desc->lock,flags);
  • spin_lock_irqsave(&desc->lock,flags):获取IRQ描述符锁并保存中断状态
  • 确保中断处理链的修改是原子的

2.5. 遍历中断处理链

        p = &desc->action;if ((old = *p) != NULL) {
  • p = &desc->action:指向IRQ处理链的头指针
  • (old = *p) != NULL:检查是否已有处理程序注册
  • 如果old不为NULL,表示该IRQ已经有处理程序

2.6. 检查中断共享兼容性

                /* Can't share interrupts unless both agree to */if (!(old->flags & new->flags & SA_SHIRQ)) {spin_unlock_irqrestore(&desc->lock,flags);return -EBUSY;}
  • !(old->flags & new->flags & SA_SHIRQ):检查新旧处理程序是否都支持共享
  • 如果任一不支持共享,返回-EBUSY(设备忙错误)
  • 先释放锁再返回,因为这是错误路径

2.7. 找到处理链末尾

                /* add new interrupt at end of irq queue */do {p = &old->next;old = *p;} while (old);shared = 1;}

循环操作:

  • p = &old->next:移动到下一个节点的指针地址
  • old = *p:获取下一个节点
  • while (old):循环直到找到链表末尾(NULL)
  • shared = 1:设置共享标志为真

2.8. 插入新的处理程序

        *p = new;
  • *p = new:在链表末尾插入新的中断处理程序
  • 此时p指向最后一个节点的next指针地址

2.9. 非共享中断的初始化

        if (!shared) {desc->depth = 0;desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |IRQ_WAITING | IRQ_INPROGRESS);if (desc->handler->startup)desc->handler->startup(irq);elsedesc->handler->enable(irq);}

非共享中断的特殊处理:

  • desc->depth = 0:重置禁用深度计数器
  • desc->status &= ~(...):清除各种状态标志
    • IRQ_DISABLED:中断禁用
    • IRQ_AUTODETECT:自动检测
    • IRQ_WAITING:等待状态
    • IRQ_INPROGRESS:处理中状态
  • 调用中断控制器的启动或启用函数

2.10. 释放锁

        spin_unlock_irqrestore(&desc->lock,flags);
  • spin_unlock_irqrestore(&desc->lock,flags):释放锁并恢复中断状态
  • 原子操作区域结束

2.11. 设置IRQ编号和注册proc文件

        new->irq = irq;register_irq_proc(irq);new->dir = NULL;register_handler_proc(irq, new);return 0;
}
  • new->irq = irq:在irqaction结构中记录IRQ编号
  • register_irq_proc(irq):在/proc文件系统中注册IRQ信息
  • new->dir = NULL:初始化目录指针
  • register_handler_proc(irq, new):注册处理程序到proc文件系统
  • return 0:成功返回

3. 总结

setup_irq函数的作用是:

  1. 中断处理程序注册:将驱动提供的中断处理函数注册到指定IRQ
  2. 共享中断管理:支持多个设备共享同一IRQ线
  3. 原子性操作:确保中断处理链的修改是线程安全的
  4. 硬件协调:与中断控制器交互启用/禁用中断
  5. 系统集成:注册proc文件系统接口用于调试和管理
  6. 随机数支持:为需要熵源的IRQ初始化随机数池
http://www.dtcms.com/a/492839.html

相关文章:

  • 国外网站推广宣传住房城乡与建设厅网站首页
  • 监督微调(SFT)入门:从理论到动手实践
  • 个人做电商网站金华做网站建设公司
  • 网站全屏宽度是多少专业网站建设需要多少钱
  • 网站建设 10万元网站导航网站开发
  • 新开发网站在线设计闪字图片
  • 网站logo怎么做才清晰鹤岗商城网站建设
  • 怎样在谷歌做网站网页设计师使用的是什么的屏幕显示颜色模式
  • 九思OA漏洞检测工具
  • Spring 框架 Bean 管理
  • 昆明网站推广公司企业平台有哪些
  • 网站排名优化多少钱网络营销的特点与方法有哪些
  • 安川焊接机器人智能节气仪
  • 昆明市网络优化案例宁波企业网站排名优化
  • 医生做学分在哪个网站wordpress与帝国cms
  • 陕西金顶建设公司网站wordpress 多层目录
  • 网站是用什么语言写的wordpress如何加跳转
  • Maya建模:使模型对称
  • 学校做网站需要多少钱公司注册资金实缴和认缴有什么区别
  • 自己可以做百度网站吗艺术品拍卖网站源码php
  • 做网站做推广做网站需要什么基础
  • 网站建设公司一站通系统简单互联网宣传推广
  • 建一个商城网站需要多久怎么做网站在里面填字
  • 网站建设与优化及覆盖率方案中国亚马逊跨境电商
  • 自助建站竹子番禺人才网招聘网
  • 推图制作网站网站 内容建设存在的问题
  • 西安网站seo收费旅游电子商务网站开发制作
  • 新世纪建设集团网站外贸网站contact
  • Yolo v3
  • 基于历史故障模式的相似性匹配技术