《Linux 内核设计与实现》07. 中断和中断处理

news2025/1/16 8:07:28

文章目录

    • 注册中断处理程序
    • 释放中断处理程序
    • 编写中断处理程序
      • 共享的中断处理程序
      • 中断例程实例
    • 中断上下文
    • /proc/interrupts
    • 中断控制
      • 禁止和激活中断
      • 禁用指定中断线
      • 中断系统的状态

注册中断处理程序

// 分配一条给定的 irq 中断线
request_irq(unsigned int irq, 
            irq_handler_t handler, 
            unsigned long flags,
	    	const char *name, 
            void *dev);

// 成功返回 0 ,否则非 0.
  • irq:要分配的中断号。该值对于某些设备来说是预先固定的,例如时钟、键盘。而对于大部分设备,可以通过探测获取或编程来确定。

  • handler:是个函数指针,指向处理这个中断的中断处理程序。

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

​ irqreturn_t 类型的返回值。

  • flag
  • name:设备名称。被用于 /proc/irq 和 /proc/interrupts。
  • dev:用于共享中断线。在共享中断线中,当一个中断处理程序需要释放时,内核会过该标识符进行删除。

request_irq() 函数可能会休眠,因此不能在中断上下文或其它不允许阻塞的代码中调用该函数。

在注册的过程中,内核需要在 /proc/irq 下创建一个与中断对应的项。由 proc_mkdir() 来负责创建这个 procfs 项。

初始化硬件和注册中断处理程序的顺序必须正确,以防中断处理程序在设备初始化之前就开始执行了。(先初始化,后注册)

释放中断处理程序

卸载驱动程序时,需要注销相应的中断处理程序,并释放中断线。

arch/sparc/kernel/irq_32.c:

void free_irq(unsigned int irq, void *dev_id) { ... }

若指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。若中断线是共享的,则仅仅只是删除 dev 所对应的中断处理程序,而这条中断线只有当最后一个中断处理程序被删除时才会跟着被禁用。

编写中断处理程序

drivers/net/fealnx.c:

/* 中断处理程序完成所有 Rx 线程工作并在 Tx 线程之后进行清理。 */
static irqreturn_t intr_handler(int irq, void *dev_instance) {
    ...
	return IRQ_RETVAL(handled);
}
  • irq:中断号。
  • dev_instance:struct net_device 类型的指针,位于:include/linux/netdevice.h。

返回值:

/**
 * enum irqreturn
 * @IRQ_NONE		中断不是来自这个设备
 * @IRQ_HANDLED		中断由该设备处理
 * @IRQ_WAKE_THREAD	处理程序请求唤醒处理程序线程
 */
enum irqreturn {
	IRQ_NONE,
	IRQ_HANDLED,
	IRQ_WAKE_THREAD,
};

typedef enum irqreturn irqreturn_t;
#define IRQ_RETVAL(x)	((x) != IRQ_NONE)

重入和中断处理程序:
同一个中断处理程序绝对不允许被同时调用,造成嵌套中断。即同一条中断线上只允许接收一个中断,而该中断线上的其它中断我们此时不接收,但其它中断线上的不受影响。

共享的中断处理程序

所有共享中断线的驱动程序都必须满足的条件:

  • request_irq() 的 flags 参数必须设置为 IRQF_SHARED。
  • 对于每个注册的中断处理程序,dev 参数必须唯一。
  • 中断处理程序必须能够区分它的设备是否真的产生了中断。(需要硬件支持,也需要软件逻辑判断)

中断例程实例

位置:drivers/char/rtc.c

当 RTC 驱动程序转载时,rtc_init() 函数会被调用,对这个驱动程序进行初始化。它的职责之一便是注册中断处理程序。

/*
	 * XXX Interrupt pin #7 in Espresso is shared between RTC and
	 * PCI Slot 2 INTA# (and some INTx# in Slot 1).
	 */
if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc",
                (void *)&rtc_port)) {
    rtc_has_irq = 0;
    printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
    return -EIO;
}

具体的中断处理程序 rtc_interrupt:

/*
 * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is
 * protected by the spin lock rtc_lock. However, ioctl can still disable the
 * timer in rtc_status and then with del_timer after the interrupt has read
 * rtc_status but before mod_timer is called, which would then reenable the
 * timer (but you would need to have an awful timing before you'd trip on it)
 */
static unsigned long rtc_status;	/* bitmapped status byte.	*/
static unsigned long rtc_freq;		/* Current periodic IRQ rate	*/
static unsigned long rtc_irq_data;	/* our output to the world	*/
static unsigned long rtc_max_user_freq = 64; /* > this, need CAP_SYS_RESOURCE */

/*
 * 一个非常小的中断处理程序。 
 * 它在设置了 IRQF_DISABLED 的情况下运行,但有可能与 set_rtc_mmss() 调用发生冲突(rtc irq 和定时器 irq 可以很容易地同时在两个不同的 CPU 中运行)。 因此,我们需要使用 rtc_lock 自旋锁串行化对芯片的访问,每个体系结构都应在定时器代码中实现。
 *(有关 set_rtc_mmss() 函数,请参阅 ./arch/XXXX/kernel/time.c。)
 */
static irqreturn_t rtc_interrupt(int irq, void *dev_id)
{
	/*
	 *	Can be an alarm interrupt, update complete interrupt,
	 *	or a periodic interrupt. We store the status in the
	 *	low byte and the number of interrupts received since
	 *	the last read in the remainder of rtc_irq_data.
	 */

	spin_lock(&rtc_lock);
	rtc_irq_data += 0x100;
	rtc_irq_data &= ~0xff;
	if (is_hpet_enabled()) {
		/*
		 * In this case it is HPET RTC interrupt handler
		 * calling us, with the interrupt information
		 * passed as arg1, instead of irq.
		 */
		rtc_irq_data |= (unsigned long)irq & 0xF0;
	} else {
		rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
	}

	if (rtc_status & RTC_TIMER_ON)
		mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);

	spin_unlock(&rtc_lock);

	/* Now do the rest of the actions */
	spin_lock(&rtc_task_lock);
	if (rtc_callback) // 回调函数
		rtc_callback->func(rtc_callback->private_data);
	spin_unlock(&rtc_task_lock);
	wake_up_interruptible(&rtc_wait);

	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);

	return IRQ_HANDLED;
}

中断上下文

中断上下文具有较为严格的时间限制,因为它打断了其它代码。中断上下文中的代码应当迅速、简洁、尽量避免使用循环来处理工作。

牢记:中断处理程序打断了其它的代码(甚至可能是打断了在其它中断线上的另一个中断处理程序)

应该要把繁杂的工作从中断处理程序中分离出来,放到下半部执行。

Linux 2.6 后,中断处理程序拥有了自己的栈,每个处理器一个,在 32 位系统上,大小为一页,这个栈称为中断栈

/proc/interrupts

这是个文件,该文件存放的是系统中与中断相关的统计信息。

以多核 CPU 为例子输出如下:

            CPU0       CPU1       CPU2       CPU3        
   1:      64403          0          0          0  IR-IO-APIC    1-edge      i8042
   8:          0          0          0          0  IR-IO-APIC    8-edge      rtc0
   9:        170         81          0          0  IR-IO-APIC    9-fasteoi   acpi
...
 NMI:        129        529        524        526  Non-maskable interrupts
 LOC:   13324301   12721094   12471117   12517149  Local timer interrupts
 SPU:          0          0          0          0  Spurious interrupts
 PMI:        129        529        524        526  Performance monitoring interrupts
...

第一列是中断线。XT-PIC 对与标椎的 PC 可编程中断控制器。在具有 I/O APIC 的设备上,大多数中断会列出 IO-APIC-level 或 IO-APIC-edge,作为自己的中断控制器。最后一列是这个中断对应的设备名称。

Tips:我是 7 核!!!

这些输出依靠 show_interrupts() 函数,位于:arch/arm/kernel/irq.c

中断控制

  • arch/alpha/include/asm/system.h
  • arch/arm/kernel/irq.c

禁止和激活中断

这里的禁用和激活中断,指的是处理器上的所有中断。

  • local_irq_disable():禁用中断
  • local_irq_enable():激活中断
#define 			do { setipl(IPL_MAX); barrier(); } while(0)
#define 			do { barrier(); setipl(IPL_MIN); } while(0)

如果在 local_irq_disable() 之前就以及禁用了中断,那么再次中断可能会存在一些潜在的危险,local_irq_enable() 也同理。为了根据方便操作,Linux 有一个恢复和保存中断的方式:

unsigned long flags; // 中断状态

local_irq_save(flags); // 保存当前中断状态,并且禁用中断
// ...
local_irq_restore(flags); // 恢复先前的状态

禁用指定中断线

这里的禁用指的是处理室的某个中断线。

提供如下函数:

/**
 * disable_irq
 * 当调用该函数时,该函数会等待该中断线上的所有中断例程处理完毕才返回
 */
extern void disable_irq(unsigned int irq);
/**
 * disable_irq_nosync
 * 不用于 disable_irq,该函数不会等待当前中断线上的中断例程执行完毕,而是直接中断
 */
extern void disable_irq_nosync(unsigned int irq);
/**
 * enable_irq
 * 激活当前中断线
 */
extern void enable_irq(unsigned int irq);

这些函数的调用可以嵌套。但在一条指定的中断线上,对 disable_irq() 或 disable_irq_nosync() 的每次调用,都需要响应地调用一次 enable_irq()。只有在对 enable_irq() 完成最后一次调用后,才真正激活了中断线。例如,如果 disable_irq() 被调用了两次,那么直到第二次调用 enable_irq() 后,才能真正地激活了中断线。

禁用多个中断处理程序共享的中断线是不合适的。禁用中断线也就禁用了这条线上的所有设备的中断传递。

中断系统的状态

// 若本地处理器上的中断系统被禁用,则返回0,否则非0.
#define irqs_disabled()	(getipl() == IPL_MAX)

include/linux/hardirq.h

/*
 * Are we doing bottom half or hardware interrupt processing?
 * Are we in a softirq context? Interrupt context?
 */
#define in_irq()		(hardirq_count())
#define in_softirq()		(softirq_count())
#define in_interrupt()		(irq_count())

通常情况下,有时需要检查自己是否处于进程上下文中,因为有时代码需要做一些像休眠这样只能从进程上下文中做的事。

image-20230419114653464

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/501330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

『python爬虫』13. 视频地址防盗链实战案例(保姆级图文)

目录 1. 寻找视频真实url地址(视频地址被加密了)2. 防盗链中的来源判断完整代码总结 欢迎关注 『python爬虫』 专栏,持续更新中 欢迎关注 『python爬虫』 专栏,持续更新中 1. 寻找视频真实url地址(视频地址被加密了&am…

【刷题笔记】不要二+把字符串转换为整数

一、不要二 题目: 牛客网链接:不要二_牛客题霸_牛客网 描述 二货小易有一个W*H的网格盒子,网格的行编号为0~W-1,网格的列编号为0~H-1。每个格子至多可以放一块蛋糕,任意两块蛋糕的欧几里得距离不能等于2。 对…

camunda表达式如何使用

在Camunda中,表达式是一种灵活的方式,可以用于在流程定义和表单中计算和处理数据。表达式可以在Camunda的各个环节中使用,例如服务任务、网关、表单、条件等。 以下是Camunda表达式的一些常见用途: 1、计算值:表达式可…

腾讯云轻量16核32G28M带宽服务器CPU流量性能测评

腾讯云轻量16核32G28M服务器3468元15个月,折合每月231元,28M公网带宽下载速度峰值可达3584KB/s,折合3.5M/秒,系统盘为380GB SSD盘,6000GB月流量,折合每天200GB流量。腾讯云百科来详细说下腾讯云轻量应用服务…

SeaweedFS学习笔记:架构和快速入门

目录 1. 介绍1.1 Components1.2 Master 服务1.3 Volume 服务1.4 Filer服务1.5 S3服务1.6 Volume的概念1.7 Collection的概念 2. 快速入门2.1 安装 SeaweedFS2.2 启动 Master 服务2.3 启动 Volume 服务2.4 快速启动一个Master服务和一个Volume服务2.5 测试 3. 参考 1. 介绍 1.1 …

asp.net汽车保养美容店维修管理系统

本系统汽车预约美容系统分为前台和后台两部分,具体功能如下 前台部分功能 1.注册登录,用户通过注册登录之后可以进行保养和美容项目的预约 2.新闻资讯,查看和汽车保养,美容相关的新闻资讯信息 3.美容查看,查看汽车美容…

6年测开经验,从功能测试到测试开发,每一步都深思熟虑...

蓦然回首,软件测试风风雨雨的这几年,起初每天的工作只是鼠标点点点,我还真不知道怎么办,闲的时候真的怀疑自己的存在价值,每天拿着7000的工资,飘荡在繁华的深圳,吃不饱也饿不死,未来…

【PWN · ret2libc】ret2libc1

ret2libc的第一题 目录 前言 一、动态链接 二、ret2libc原理 三、exp编写 干货 干货一:python下的ELF 干货二:strings看看有没有待选字符串 编写exp 总结 前言 本来是和学习ret2text\ret2shellcode\ret2syscall一样在网上找文字资源&#x…

28从零开始学Java之面向对象和面向过程到底有哪些区别?

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 壹哥相信,经过你对前面文章中技术点的学习,现在的你应该已经对Java具备了初步…

【UE】直升机沿样条线移动

效果 步骤 1. 将虚幻商城中的免费资产导入工程 下载完毕后可以看到如下文件 2. 新建一个Actor蓝图类,命名为“Track”,这个蓝图就是用来画样条线的 打开“Track”,添加样条组件 3. 打开“BP_West_Heli_AH64D” 在事件图表中先新建一个时间轴…

Unity冷知识:读取用户输入应该写在Update还是FixedUpdate里?

Unity冷知识:读取用户输入应该写在Update还是FixedUpdate里? 版权声明: 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明文章内容,不得删减、修改、演绎相关学习资源见文末 一些人…

1.SpringCloud技术

SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff…

初识C++之线程库

目录 一、C中的线程使用 二、C的线程安全问题 1. 加锁 2. 变为原子操作 3. 递归里面的锁 4. 定时锁 5. RAII的锁 三、条件变量 1. 为什么需要条件变量 2. 条件变量的使用 2.1 条件变量的相关函数 2.2 wait函数 一、C中的线程使用 线程的概念在linux中的线程栏已经…

9个加密货币交易所被查封,交易所安全审计后仍不安全

美国联邦调查局和乌克兰警方查封了九个加密货币交易网站,这些网站为包括勒索软件参与者在内的诈骗者和网络犯罪分子洗钱提供了便利。 联邦调查局 FBI 在其公告中表示,该行动是在虚拟货币响应小组、乌克兰国家警察和该国法律检察官的帮助下进行的。 此次…

ai皮带跑偏撕裂监测算法 yolov7

ai皮带跑偏撕裂监测系统算法基于yolov7网络模型人工智能视觉技术,ai皮带跑偏撕裂监测算法模型自动识别现场画面中传送皮带撕裂、跑偏、偏移等情况,立即告警抓拍存档同步回传后台。YOLO 的核心思想就是把目标检测转变成一个回归问题,利用整张图…

Git入门学习

Git是什么? 是一种免费开源的分布式版本控制系统,区别于集中式挂历系统(SVN,CVS)的是分布式每人都有一个“档案馆”,而集中式是只有一个“档案馆”。 这样的话,如果你使用git进行开发,感觉自己…

多层PCB层叠结构

在设计多层PCB电路板之前,设计者需要首先根据电路的规模、电路板的尺寸和电磁兼容(EMC)的要求来确定所采用的电路板结构,也就是决定采用4层,6层,还是更多层数的电路板。确定层数之后,再确定内电…

全景丨0基础学习VR全景制作,平台篇第18章:热点功能-音频

大家好,欢迎观看蛙色VR官方——后台使用系列课程! 功能说明 应用场景 热点,指在全景作品中添加各种类型图标的按钮,引导用户通过按钮产生更多的交互,增加用户的多元化体验。 音频热点,即点击热点后会直接播…

低代码开发重要工具:jvs表单动态字段配置方式

在表单中常常会有动态字段的场景,也就是如下图所示,根据一个字段的内容选项去控制另外字段的内容展示 配置的思路:根据第一个单选框选择的内容,通过关联筛选的方式去选择第二个单选框的内容,那么第二个单选框的物理存储…

K8s基础7——DaemonSet控制器、Job批处理调度、Cronjob定时调度

文章目录 一、DaemonSet二、Job三、Cronjob 一、DaemonSet 控制器介绍: DaemonSet 可以确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时, 也会给这新节点新增一个 Pod 。当有节点从集群移除时,此节点…