linux内核中断

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核中断相关的知识,希望对你有一定的参考价值。

目录

硬中断特点

中断API

线程中断

系统标准的优先级顺序

中断信息查看

中断上半部与下半部

软中断与并发


硬中断特点

  • 优先级最高
  • 中断函数在中断上下文中,不能阻塞

        不要间接或直接调用shedule()

        在申请内存空间时,使用GFP_ATOMIC 标志(非阻塞,尽可能快);

        不能在用户与内核空间传递数据

  • 中断屏蔽:

        默认情况下,在中断函数处理时,所有的本地CPU的中断是被屏蔽的

  • 尽可能的快

        几十微妙内,如果超过则使用中断下半部

下面这块程序存在的问题

my_interrupt()

	struct mys *sp;
	ack_intr();
	x = read_regX();
	sp = kzalloc(SIZE_HWBUF, GFP_KERNEL);
	if (!sp)
		return -ENOMEM;
	sp = fetch_data_from_hw();
	copy_to_user(ubuf, sp, count);
	kfree(sp);
  1. kzalloc() 使用GFP_KERNEL标志,可能引起调用schedule(),内核会输出oops错误;
  2. copy_to_user()调用可能会引起页错误,引起上下文切换,触发schedule()
  3. 更一般的错误,a函数最终调用了schedule(),将会引起oops错误。a()--b()--c()--[] --g() --schedule()--[]

中断API

request_irq()
devm_request_irq()


*free_irq(unsigned int, void *);

详细分析request_irq函数的参数 

#include <linux/interrupt.h>
int __must_check
request_irq(unsigned int irq, irq_handler_t (*handler_func)(int, void *),
unsigned long flags, const char *name, void *dev);
  • 头文件 #include <linux/interrupt.h>
  • irq:中断线
  • irq_handler_t:中断处理函数
  • flag:中断标志bitmask,中断标志在下节介绍
  • name:驱动名
  • *dev:给中断函数传递参数

中断标志

  • IRQF_SHARED:在几个设备中间允许共享IRQ线
  • IRQF_ONESHOT:经常用在线程中断中,以确保IRQ在线程处理程序完成之前保持禁用状态;
  • __IRQF_TIMER:定时器中断 ,定时器中断标志组合如下

#define IRQF_TIMER(__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)

触发方式

  • Level-triggered:中断在没有得到响应之前,一直触发。
  • Edge-triggered:中断仅仅触发一次

中断掩码

        芯片的中断控制器(PIC/GIC)有掩码寄存器,操作系统能够通过编程屏蔽或阻塞硬件中断。

  1. 尽可能的保持中断使能,这对于系统至关重要。如果中断被阻塞,外设不能响应,系统性能滞后或许受此影响。使用自旋锁锁定将导致中断和抢占被禁用。
  2. Linux系统的默认行为,发生硬件中断且该中断未被屏蔽时,内核确保在其中断(hardirq)处理程序执行时,本地CPU内核上执行处理程序的所有中断都被禁用。
  3. 多核系统上的中断:当IRQn在CPU内核1上执行时,除内核1外,其他中断在所有CPU内核上保持启用。因此,在多核系统硬件上,中断可以在不同的CPU内核上并行运行。就全局数据而言,只要他们互不干涉,这就没问题!如果有竞争,就必须使用锁定。 

线程中断

//线程中断
request_threaded_irq()
devm_request_threaded_irq() (recommended!)

内核线程与用户模式线程非常相似,它在进程上下文中独立运行,并有自己的任务结构,能够被调度。

  • 实现正在的实时
  • 消除软中断的瓶颈:

消除的瓶颈如何理解?

  • 线程中断运行在进程上下文中,,它被认为不像hardirq处理程序那样重要;因此,中断处理可能需要更长的时间。
  • 当硬中断执行时,中断IRQ line 在所有核上都禁止。如果执行到完成需要一段时间,那么系统的响应可能会显著下降;而线程处理程序执行时,默认启用硬件IRQ line。这有助于提高性能和响应能力。

线程处理程序的限制

  • IRQF_ONESHOT 必须要存在
  • 在处理NIC,多媒体等时处理速度会很慢,建议使用top/bottom机制

系统标准的优先级顺序

  • 硬件中断 (抢占了一切资源)
  • 实时线程(SCHED_FIFO.  SCHED_RR)
  • 处理器异常(系统调用,缺页异常等)
  • 用户模式线程(SCHED_OTHER)

如何判定选择硬中断还是线程中断?

内核提供了request_any_context_irq,根据特定情况,它将把中断处理程序设置为hardirq处理程序或线程处理程序。

int __must_check
request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id);

返回值

  •         如果运行在硬中断上下文,返回IRQC_IS_HARDIRQ;
  •         如果运行在进程或线程环境中,返回IRQC_IS_NESTED;
  •         返回负数,代表执行错误 

中断信息查看

查看硬中断

# cat /proc/interrupts 
            CPU0       CPU1       CPU2       CPU3       
   0:         24          0          0          0   IO-APIC    2-edge      timer
   1:      15354       5040      12673       1046   IO-APIC    1-edge      i8042
   8:          0          1          0          0   IO-APIC    8-edge      rtc0
   9:          0          0          0          0   IO-APIC    9-fasteoi   acpi
  12:          0      58896      51200      29989   IO-APIC   12-edge      i8042
  14:          0          0          0          0   IO-APIC   14-edge      ata_piix
  15:          0          0          0          0   IO-APIC   15-edge      ata_piix
  16:          0     122949          0        289   IO-APIC   16-fasteoi   vmwgfx, snd_ens1371
  17:      37934          0          0          0   IO-APIC   17-fasteoi   ehci_hcd:usb1, ioc0
  18:          0         41          0          0   IO-APIC   18-fasteoi   uhci_hcd:usb2
  19:          0          0         67      31218   IO-APIC   19-fasteoi   ens33

查看软中断 

# cat /proc/softirqs 
                    CPU0       CPU1       CPU2       CPU3       
          HI:          0          0          0          0
       TIMER:       8700       9001       8961      10715
      NET_TX:         12          3          2          2
      NET_RX:         76          7          7          3
       BLOCK:       2642          0          0          0
    IRQ_POLL:          0          0          0          0
     TASKLET:          0          0          1          0
       SCHED:       5516       4294       3979       3572
     HRTIMER:          0          0          0          0
         RCU:       6481       7745       9292       7009

使用线程中断处理是现代的一种方式,使用下半部机制是传统的方法。

中断上半部与下半部

中断上半部处理比较快的程序,如执行纯粹小的需求,中断处于关闭状态;

下半部处理相对较慢的处理程序,也是大部分中断工作;中断使能状态;

上半部的函数有:

                request_irq() / devm_request_irq() / request_threaded_irq() /
devm_request_threaded_irq()

下半部的函数:

                tasklet,workqueue,softirq

下半部分仍然在原子或中断上下文中运行!

  • 不能传输数据(用户和内核空间);
  • 分配内存要使用GFP_ATOMIC;
  • 不能直接间接调用schedule();

tasklet

#include <linux/interrupt.h>
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long),
unsigned long data);
void tasklet_schedule(struct tasklet_struct *t);


/ * 下半部的处理 */
static void mydrv_tasklet(unsigned long data)


	process_it(); // 中断的绝大多数处理

/*上半部处理 */
static irqreturn_t my_hardirq_handler(int irq, void *data)

	tasklet_schedule(ts);//调度tasklet执行
	return IRQ_HANDLED;



static struct tasklet_struct *ts;

static int __init mydriver_init(void)

	struct device *dev;

	/* misc设备注册*/	
	ret = misc_register(&keylog_miscdev);
	dev = keylog_miscdev.this_device;
	//申请内存
	ts = devm_kzalloc(dev, sizeof(struct tasklet_struct), GFP_KERNEL);

	//tasklet初始化,并注册回调函数
	tasklet_init(ts, mydrv_tasklet, 0);

	//申请中断 中断处理函数my_hardirq_handler
	ret = devm_request_irq(dev, MYDRV_IRQ, my_hardirq_handler,
				IRQF_SHARED, OURMODNAME, THIS_MODULE);

tasklet_hi_schedule()相比tasklet_schedule()有更高的软中断优先级。

softirq

// kernel/softirq.c
void open_softirq(int nr, void (*action)(struct softirq_action *))

    softirq_vec[nr].action = action;

软中断类型以及解释

优先级序号Softirq说明函数
0HI_SOFTIRQ最高的软件优先级tasklet_hi_action()
1TIMER_SOFTIRQ定时器run_timer_softirq()
2NET_TX_SOFTIRQ网络栈发送net_tx_action()
3NET_RX_SOFTIRQ网络栈接收net_rx_action()
4BLOCK_SOFTIRQBlock进程blk_done_softirq()
5IRQ_POLL_SOFTIRQ内核的块层轮询IRQ模式irq_poll_softirq()
6TASKLET_SOFTIRQtasklet下半部tasklet_action()
7SCHED_SOFTIRQCFS调度;将任务迁移到其他队列run_rebalance_domains()
8HRTIMER_SOFTIRQ高精度定时器hrtimer_run_softrq()
9RCU_SOFTIRQ执行RCU进程

rcu_core_si()/

rcu_process_callbacks()

软中断与并发

  • tasklet 不能并发执行;softirq可以并发执行,因此需要锁;
  • 软中断能被硬中断打断;
  • 软中断不能抢占正在执行的软中断,虽然有更优的优先级,必须安装优先级使用;
  • spin_lock_bh(),当被锁住的时候,禁止软中断;防止死锁

硬中断,tasklet与线程处理函数的比较

时间使用优缺点
<=10微妙hardirq最佳方案
[10,100]微妙hardirq或hardirq与tasklet(softirq)运行压力测试/工作负载,看看是否真的需要tasklet。对于线程处理程序或工作队列,不鼓励使用
100毫秒,非关键的设备hardirq,threaded handler, workqueue避免softirq处理,这有助于减少系统延迟,但可能导致处理速度稍慢
100毫秒,关键设备(网络、块、媒体设备)hardirq,tasklet当大量中断到来时,可能会导致“livelock”问题和长时间的延迟
100毫秒,极其关键的工作或设备hardirq,hi-tasklet,softirq相当极端,不太可能情况

大量未执行的softirq可能导致livelock的情况,以下两种方式缓解了这个问题

  • 线程中断或工作队列
  • 调用ksoftirqd/n内核线程来接管softirq处理

上述的两个例子运行在进程上下文,缓解了线程不足的问题。

如何知道程序处在进程或中断上下文中? 内核源码中有函数

// include/linux/preempt.h
/*
* Are we doing bottom half or hardware interrupt processing?
*
* in_irq() - We're in (hard) IRQ context
* in_softirq() - We have BH disabled, or are processing softirqs
* in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
* in_serving_softirq() - We're in softirq context
* in_nmi() - We're in NMI context
* in_task() - We're in task context

 参考

埃梯零声-零声教育

以上是关于linux内核中断的主要内容,如果未能解决你的问题,请参考以下文章

把握linux内核设计思想:下半部机制之软中断

《Linux内核设计与实现》学习笔记——中断中断处理程序

Linux并发与同步专题

Linux中断(interrupt)子系统之一:中断系统基本原理

Linux(内核剖析):23---中断下半部之(下半部总体概述)

linux 内核中断机制