Linux Kernel Interrupt 分析

在之前接触单片机时就接触到了中断这一个名词,简单理解,在CPU正常运行程序A时,突然插入了程序B,程序B就可以理解为一个中断。在linux kernel中也提供中断机制,用于处理上述突发事件。
一、中断入口
在linux kernel亦有异常向量表,以linux-2.6.32.50中的ARM架构为例,在arch/arm/kernel/entry-armv.S中存在如下的异常向量表。

 __vectors_start:
  ARM(   swi SYS_ERROR0  )
  THUMB( svc #0      )
  THUMB( nop         )
     W(b)    vector_und + stubs_offset
     W(ldr)  pc, .LCvswi + stubs_offset
     W(b)    vector_pabt + stubs_offset
     W(b)    vector_dabt + stubs_offset
     W(b)    vector_addrexcptn + stubs_offset
     W(b)    vector_irq + stubs_offset
     W(b)    vector_fiq + stubs_offset

     .globl  __vectors_end
 __vectors_end:

当中断产生,将进入vector_irq 中进行中断处理,对应的代码如下,其中,vector_stub为宏定义。

/*
  * Interrupt dispatcher
  */
     vector_stub irq, IRQ_MODE, 4

     .long   __irq_usr           @  0  (USR_26 / USR_32)
     .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
     .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
     .long   __irq_svc           @  3  (SVC_26 / SVC_32)
     .long   __irq_invalid           @  4
     .long   __irq_invalid           @  5
     .long   __irq_invalid           @  6
     .long   __irq_invalid           @  7
     .long   __irq_invalid           @  8
     .long   __irq_invalid           @  9
     .long   __irq_invalid           @  a
     .long   __irq_invalid           @  b
     .long   __irq_invalid           @  c
     .long   __irq_invalid           @  d
     .long   __irq_invalid           @  e
     .long   __irq_invalid           @  f

对于管理模式与用户模式中发生的中断,其对应的处理函数不同,分别为__irq_svc与__irq_usr。

二、中断的调用过程
以__irq_usr为例。
__irq_usr => irq_handler => asm_do_IRQ => generic_handle_irq(irq) => generic_handle_irq_desc() => __do_IRQ(irq) => handle_IRQ_event()

370 irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
371 {
372     irqreturn_t ret, retval = IRQ_NONE;
373     unsigned int status = 0;
374
375     if (!(action->flags & IRQF_DISABLED))
376         local_irq_enable_in_hardirq();
377
378     do {
379         trace_irq_handler_entry(irq, action);
380         ret = action->handler(irq, action->dev_id);
381         trace_irq_handler_exit(irq, action, ret);
382
383         switch (ret) {
384         case IRQ_WAKE_THREAD:
389             ret = IRQ_HANDLED;
395             if (unlikely(!action->thread_fn)) {
396                 warn_no_thread(irq, action);
397                 break;
398             }
399
408             if (likely(!test_bit(IRQTF_DIED,
409                          &action->thread_flags))) {
410                 set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
411                 wake_up_process(action->thread);
412             }
413
414             /* Fall through to add to randomness */
415         case IRQ_HANDLED:
416             status |= action->flags;
417             break;
418
419         default:
420             break;
421         }
422
423         retval |= ret;
424         action = action->next;
425     } while (action);
426
427     if (status & IRQF_SAMPLE_RANDOM)
428         add_interrupt_randomness(irq);
429     local_irq_disable();
430
431     return retval;
432 }

代码中380行执行中断服务程序,该中断服务程序是由我们编写并且注册系统中。通过这样的一个过程,完成了对外部硬件中断的响应。

三、中断服务程序注册
linux kernel中提供了一组函数用于完成对中断服务程序的注册与释放,request_irq()与free_irq()。

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
		const char *name, void *dev)

该函数有五个参数,其说明如下:
irq—要分配的设备号
handler—指向实际的中断服务程序的函数指针

typedef irqreturn_t (*irq_handler_t)(int, void *);

flags—中断处理程序的标志。定义在include/linux/interrupt.h
name—中断名
dev—用于共享中断线,是handler的参数。如果不用共享中断线,该参数可设为NULL
中断服务程序的注册过程如下:
request_irq() => request_threaded_irq()

1028 int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1029              irq_handler_t thread_fn, unsigned long irqflags,
1030              const char *devname, void *dev_id)
1031 {
1032     struct irqaction *action;
1033     struct irq_desc *desc;
1034     int retval;
1035
......
1081     action->handler = handler;
1082     action->thread_fn = thread_fn;
1083     action->flags = irqflags;
1084     action->name = devname;
1085     action->dev_id = dev_id;
1086
1087     chip_bus_lock(irq, desc);
1088     retval = __setup_irq(irq, desc, action);
1089     chip_bus_sync_unlock(irq, desc);
1090
......
1093
1113     return retval;
1114 }

在代码1081行中,中断服务程序handler的地址被保存在action的handler成员中。结合前面跟踪的中断调用流程,在将来中断产生,将执行我们通过request_irq注册的中断服务程序。

一般来说,我们在驱动程序中的init函数中调用request_irq(),将我们编写的中断服务程序handler注册到系统中;当中断产生后,系统经过一系列的调用,最终将会执行我们的中断服务程序handler,完成对中断的响应。
在驱动程序的exit函数中调用free_irq(),将注册到系统中的中断服务程序handler移除。

<think>好的,用户想了解Linux Kernel 6.8.8中e1000网卡驱动的中断处理机制。首先,我需要回忆e1000驱动的基本情况。e1000是Intel千兆以太网卡的驱动,属于内核网络子系统的一部分。中断处理对网络性能很关键,特别是数据包的接收和发送。 接下来,用户提到的内核版本是6.8.8,这个版本可能包含一些新的特性或优化。需要确认e1000驱动在该版本中的结构是否有变化。通常,网卡驱动的中断处理流程包括中断注册、处理函数、以及可能的NAPI机制。 根据引用[1],用户的内核支持SMP和PREEMPT_DYNAMIC,这些特性可能影响中断处理的并发性和抢占能力。中断处理函数需要考虑到多核环境下的同步问题,比如使用锁或者原子操作。 然后,中断处理的基本步骤应该是:当网卡接收到数据包时,触发硬件中断,CPU响应中断后调用注册的中断处理函数。在e1000驱动中,可能使用request_irq来注册中断处理程序。处理函数需要读取网卡的中断状态寄存器,判断中断类型(接收、发送或错误),然后进行相应的处理,比如调度NAPI轮询数据包。 另外,现代驱动常用NAPI来提升网络吞吐量,减少中断次数。在初始中断后,关闭进一步的中断,进入轮询模式处理多个数据包,这可能在e1000驱动中也有应用。需要查看驱动代码中是否使用了netif_napi_add和napi_schedule等函数。 用户提供的引用中没有直接提到e1000,但引用[2]涉及内核头文件的问题,可能影响驱动编译,不过用户的问题更多是关于运行时的中断机制。引用[3]是Kibana配置,可能与当前问题无关。 需要确保回答中提到关键点:中断注册过程、处理函数的工作(清除中断标志、处理数据包)、可能使用的NAPI机制,以及多核和抢占对中断处理的影响。最后,建议用户查阅具体的内核源码和文档,或者使用工具如ftrace来跟踪中断处理流程。</think>### Linux Kernel 6.8.8 e1000网卡驱动中断处理机制 #### 1. **中断注册与初始化** 在Linux内核中,e1000网卡驱动通过`e1000_probe`函数初始化硬件并注册中断处理程序。具体步骤包括: - **硬件初始化**:配置网卡寄存器(如`E1000_CTRL`控制寄存器)和DMA缓冲区。 - **中断请求**:调用`request_irq`注册中断服务例程(ISR),例如: ```c err = request_irq(pdev->irq, e1000_intr, IRQF_SHARED, netdev->name, netdev); ``` 其中`e1000_intr`是中断处理函数[^1]。 #### 2. **中断处理流程** 当网卡接收到数据包或发生异常时,触发硬件中断,内核调用注册的`e1000_intr`函数: - **中断类型判断**:通过读取`E1000_ICR`(中断原因寄存器)判断中断类型(如接收完成、发送完成或错误)。 - **接收处理**:若为接收中断,驱动调用`e1000_clean_rx_irq`从环形缓冲区(RX Ring)提取数据包,并通过`netif_receive_skb`提交到网络协议栈。 - **发送处理**:若为发送中断,调用`e1000_clean_tx_irq`释放已发送数据包的内存。 - **NAPI调度**:现代内核通常结合NAPI(New API)减少中断频率。例如,在初始中断后调用`napi_schedule`切换到轮询模式。 #### 3. **中断优化与多核支持** - **中断亲和性**:通过`irqbalance`或手动设置(`/proc/irq/<IRQ>/smp_affinity`)将中断绑定到特定CPU核心,减少缓存抖动。 - **MSI/MSI-X**:内核6.8.8可能启用MSI-X(多消息中断),允许网卡为不同队列分配独立中断,提升多核处理效率[^1]。 #### 4. **关键代码片段示例** ```c // 中断处理函数示例 static irqreturn_t e1000_intr(int irq, void *data) { struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); u32 icr = er32(ICR); // 读取中断原因 if (icr & E1000_ICR_RXT0) { // 接收中断处理 napi_schedule(&adapter->napi); } return IRQ_HANDLED; } // NAPI轮询函数 static int e1000_clean(struct napi_struct *napi, int budget) { // 处理接收队列数据包 e1000_clean_rx_irq(adapter, &adapter->rx_ring[0], budget); // 如果未处理完,返回实际消耗的预算值 return work_done; } ``` #### 5. **调试与监控** - **查看中断统计**:`cat /proc/interrupts | grep eth0` - **动态追踪**:使用`ftrace`或`bpftrace`跟踪中断处理函数的执行时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值