Linux嵌入式驱动开发-linux中断

文章目录

  • linux中断原理
    • 中断控制器GIC
      • 中断源分类
    • 中断ID、中断线(中断号/中断源)
    • 中断ID
    • 中断线(中断号/中断源)
      • MX6U中断源
  • 上半部与下半部(顶半部和底半部)
  • 下半部实现机制
    • linux软中断
      • 注册软中断
      • 触发软中断
      • 初始化软中断
    • tasklet
      • asklet_init:初始化tasklet
      • tasklet_schedule:调度tasklet
      • Example
    • 工作队列
      • 创建工作
      • schedule_work:调度工作
      • Example

linux中断原理

中断控制器GIC

GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的NVIC。

当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况:VFIQ、VIRQ、FIQ 和 IRQ,他们之间的关系如下:

在这里插入图片描述

GIC 接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给 ARM 内核,这四个信号的含义如下:

信号含义
VFIQ虚拟快速 FIQ。
VIRQ虚拟外部 IRQ。
FIQ快速中断 IRQ。
IRQ外部中断 IRQ。

中断源分类

GIC 将众多的中断源分为分为三类:

  1. SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有 Core 共享的中断,这个是最常见的,那些外部中断都属于 SPI 中断(注意!不是 SPI 总线那个中断) 。
    比如按键中断、串口中断等等,这些中断所有的 Core 都可以处理,不限定特定 Core。

  2. PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。
    这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。

  3. SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,

通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。

中断ID、中断线(中断号/中断源)

中断ID

每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019,这 1020 个 ID 包含了 PPI、SPI 和 SGI。

ID0~ID15:这 16 个 ID 分配给 SGI。

ID16~ID31:这 16 个 ID 分配给 PPI。

ID32~ID1019:这 988 个 ID 分配给 SPI,具体某个 ID 对应哪个中断由半导体厂商根据实际情况定义。

中断线(中断号/中断源)

I.MX6U 的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID,I.MX6U 的中断源共有 128+32=160个;

这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 Cortex A7 interrupts”小节。

所以MX6ULL的中断线IRQ0–128对应的中断ID为ID32–ID160

MX6U中断源

在这里插入图片描述

上半部与下半部(顶半部和底半部)

在使用request_irq 申请中断的时候注册的中断服务函数属于中断处理的上半部

上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。

下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。

Linux 内核将中断分为上半部和下半部的主要目的就是实现中断处理函数的快进快出;

那些对时间敏感、执行速度快的操作可以放到中断处理函数中,也就是上半部;剩下的所有工作都可以放到下半部去执行;

比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。

上半部与下半部的的划分:

  1. 如果要处理的内容不希望被其他中断打断,那么可以放到上半部。

  2. 如果要处理的任务对时间敏感,可以放到上半部。

  3. 如果要处理的任务与硬件有关,可以放到上半部。

  4. 除了上述三点以外的其他任务,优先考虑放到下半部。

下半部实现机制

一开始 Linux 内核提供了“bottom half”机制来实现下半部,简称“BH”。

从 2.5 版本的 Linux内核开始 BH 已经被抛弃了,引入了软中断和tasklet来替代“BH”机制。

linux软中断

Linux 内核使用结构体 softirq_action 表示软中断, softirq_action结构体定义在文件 include/linux/interrupt.h 中。

struct softirq_action
{
    void (*action)(struct softirq_action *);
};

在 kernel/softirq.c 文件中一共定义了 10 个软中断。

软中断枚举类型

static struct softirq_action softirq_vec[NR_SOFTIRQS];
/* NR_SOFTIRQS 是枚举类型,定义在文件 include/linux/interrupt.h 中,定义如下 */
enum
{
    HI_SOFTIRQ = 0, /* 高优先级软中断*/
    TIMER_SOFTIRQ,  /* 定时器软中断*/
    NET_TX_SOFTIRQ, /* 网络数据发送软中断*/
    NET_RX_SOFTIRQ, /* 网络数据接收软中断*/
    BLOCK_SOFTIRQ,
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ, /* tasklet 软中断*/
    SCHED_SOFTIRQ,   /* 调度软中断*/
    HRTIMER_SOFTIRQ, /* 高精度定时器软中断*/
    RCU_SOFTIRQ,       /* RCU 软中断*/
    NR_SOFTIRQS
};

一共有 10 个软中断,因此 NR_SOFTIRQS 为 10,因此数组 softirq_vec 有 10 个元素。

softirq_action 结构体中的 action 成员变量就是软中断的服务函数,数组 softirq_vec 是个全局数组,因此所有的 CPU(对于SMP系统而言)都可以访问到。

SMP:对称多处理(Symmetric multiprocessing)

每个 CPU 都有自己的触发和控制机制,并且只执行自己所触发的软中断。但是各个 CPU 所执行的软中断服务函数是相同的,都是数组softirq_vec中定义的action函数。

注册软中断

要使用软中断,必须先使用 open_softirq 函数注册对应的软中断处理函数,软中断必须在编译的时候静态注册!

void open_softirq(int nr, void (*action)(struct softirq_action *))

nr:要开启的软中断,在上面的(软中断枚举类型)中选一个。

action:软中断对应的处理函数。

返回值:没有返回值。

触发软中断

注册好软中断以后需要通过 raise_softirq 函数触发

void raise_softirq(unsigned int nr)

nr:要触发的软中断,在上面的(软中断枚举类型)中选一个。

返回值:没有返回值。

初始化软中断

linux内核自动初始化软中断,Linux 内核使用softirq_init​函数初始化软中断,softirq_init​函数定义在kernel/softirq.c​文件里面。

void __init softirq_init(void)
{
    int cpu;
    for_each_possible_cpu(cpu)
    {
        per_cpu(tasklet_vec, cpu).tail =
            &per_cpu(tasklet_vec, cpu).head;
        per_cpu(tasklet_hi_vec, cpu).tail =
            &per_cpu(tasklet_hi_vec, cpu).head;
    }
    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

softirq_init 函数默认会打开TASKLET_SOFTIRQHI_SOFTIRQ

tasklet

tasklet 是利用软中断来实现的另外一种下半部机制,在软中断和 tasklet 之间,建议使用 tasklet。

Linux 内核使用 tasklet_struct 结构体来表示 tasklet。

struct tasklet_struct
{
  struct tasklet_struct *next;   /* 下一个 tasklet */
  unsigned long state;           /* tasklet 状态 */
  atomic_t count;                /* 计数器,记录对 tasklet 的引用数 */
  void (*func)(unsigned long);   /* tasklet 执行的函数 */
  unsigned long data;            /* 函数 func 的参数 */
};

func 函数就是 tasklet 要执行的处理函数,用户定义函数内容,相当于中断处理函数。

asklet_init:初始化tasklet

如果要使用 tasklet,必须先定义一个 tasklet,然后使用 tasklet_init 函数初始化 tasklet。

void tasklet_init(struct tasklet_struct *t,
                  void (*func)(unsigned long),
                  unsigned long data);

t:要初始化的 tasklet
func:tasklet 的处理函数。
data:要传递给 func 函数的参数
返回值:没有返回值。

可以使用宏DECLARE_TASKLET来一次性完成tasklet的定义和初始化,DECLARE_TASKLET定义在include/linux/interrupt.h文件中

DECLARE_TASKLET(name, func, data)

name 为要定义的 tasklet 名字,这个名字就是一个 tasklet_struct 类型的时候变量;
func就是 tasklet 的处理函数;
data 是传递给 func 函数的参数;

tasklet_schedule:调度tasklet

在上半部,也就是中断处理函数中调用 tasklet_schedule 函数就能使 tasklet 在合适的时间运行

void tasklet_schedule(struct tasklet_struct *t)

t:要调度的 tasklet,也就是 DECLARE_TASKLET 宏里面的 name。
返回值:没有返回值。

Example

/* 定义 taselet */
struct tasklet_struct testtasklet;

/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
  /* tasklet 具体处理内容 */
}

/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
  ......
  /* 调度 tasklet   */
  tasklet_schedule(&testtasklet);
  ......
}

/* 驱动入口函数 */
static int __init xxxx_init(void)
{
  ......
  /* 初始化 tasklet */
   tasklet_init(&testtasklet, testtasklet_func, data);
  /* 注册中断处理函数 */
  request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
  ......
}

工作队列

工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。

如果要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软中断或 tasklet

Linux 内核使用work_struct结构体表示一个工作。

struct work_struct {
  atomic_long_t data;
  struct list_head entry;
  work_func_t func; /* 工作队列处理函数 */
};

这些工作组织成工作队列,工作队列使用workqueue_struct结构体表示。

Linux 内核使用工作者线程(worker thread)来处理工作队列中的各个工作,Linux 内核使用worker结构体表示工作者线程

每个 worker 都有一个工作队列,工作者线程处理自己工作队列中的所有工作。

在实际的驱动开发中,我们只需要定义工作(work_struct)即可,关于工作队列和工作者线程我们基本不用去管。

创建工作

定义一个 work_struct 结构体,使用 INIT_WORK 宏来初始化工作;

#define INIT_WORK(_work, _func)

_work 表示要初始化的工作,_func 是工作对应的处理函数。

也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化;

#define DECLARE_WORK(n, f)

n 表示定义的工作(work_struct),f 表示工作对应的处理函数。

schedule_work:调度工作

工作也是需要调度才能运行。

bool schedule_work(struct work_struct *work)

work:要调度的工作。
返回值:0 成功,其他值 失败。

Example

/* 定义工作(work) */
struct work_struct testwork;

/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work 具体处理内容 */
}

/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
  ......
  /* 调度 work */
  schedule_work(&testwork);
  ......
}

/* 驱动入口函数 */
static int __init xxxx_init(void)
{
  ......
  /* 初始化 work */
  INIT_WORK(&testwork, testwork_func_t);
  /* 注册中断处理函数 */
  request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
  ......
}

dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AIGC】文本与音频生成引领行业革新

AIGC技术崛起 一、AIGC技术概述二、文本生成:结构化与创作型并进三、实例与代码解析四、音频生成:语音合成技术大放异彩五、结语 在科技的浪潮中,人工智能与大数据的结合不断推动着时代的进步。其中,AIGC(Artificial I…

安居水站:水站经营秘籍:年入30万不是梦。水站创业指南。

在这个快节奏的社会里,初创企业家们总是在寻找一条明路,以在竞争激烈的市场中立足。为了帮助他们更好地实现这一目标,我根据经验决定制定一份水站经营指导手册。这份手册将详细阐述如何从零起步,如何运营,如何进行市场…

智慧浪潮下的产业园区:解读智慧化转型如何打造高效、绿色、安全的新产业高地

随着信息技术的飞速发展,智慧化转型已经成为产业园区发展的重要趋势。在智慧浪潮的推动下,产业园区通过集成应用物联网、大数据、云计算、人工智能等先进技术手段,实现园区的智慧化、高效化、绿色化和安全化,从而打造成为新产业高…

x-cmd ai | x openai - 用于发送 openai API 请求,以及与 ChatGPT 对话

介绍 Openai 模块是 Openai 大模型 Chatgpt 3 和 ChatGPT 4 命令行实现。x-cmd 提供了多个不同平台间多种 AI 大模型的调用能力。无论是本地模型还是 Web 服务上的模型,用户都可以在不同的 AI 大模型间直接无缝切换,并能把之前的聊天记录发送给新的大模…

【PCL】教程conditional_euclidean_clustering 对输入的点云数据进行条件欧式聚类分析...

[done, 3349.09 ms : 19553780 points] Available dimensions: x y z intensity 源点云 Statues_4.pcd 不同条件函数output.pcd 【按5切换到强度通道可视化】 终端输出: Loading... >> Done: 1200.46 ms, 19553780 points Downsampling... >> Done: 411…

【JavaWeb】Day50.Mybatis的XML配置文件

XML配置文件规范 使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。 在Mybatis中使用XML映射文件方式开发,需要符合一定的规…

Nginx解决跨域访问难题:轻松实现跨域资源共享!

点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~ 跨域资源共享(CORS,Cross-Origin Resource Sharing)是一种网络浏览器的安全功能,它限制了一个源(域、协议和端口…

学习空间转换-3D转换

1.什么是空间转换? 使用的是transform属性实现元素在空间内的位移,旋转,缩放等效果。 空间:是从坐标轴角度定义的。x,y,z三条坐标轴构成的一个立体空间,Z轴位置与视线方向相同。 所以空间转换也被叫做3D转换 语法&a…

docker+awk=无敌?!

欢迎来到我的博客,代码的世界里,每一行都是一个故事 dockerawk无敌?! 前言需求分析容器间通过容器名称访问脚本实现一键部署命令解释 前言 当今软件开发的世界充满了数据,而 Docker 则是许多开发者首选的容器化解决方…

嵌入式学习55-ARM4(ADC和I²C)

1、什么是ADC,模拟量和数字量有什么特点? ADC: …

GhostNetV3:探索紧凑型模型的训练策略学习笔记

代码地址(coming soon):Efficient-AI-Backbones/ghostnetv3_pytorch at master huawei-noah/Efficient-AI-Backbones GitHub 论文地址:2404.11202v1.pdf (arxiv.org) 紧凑型神经网络是专门为边缘设备上的应用而设计的&#xff0…

linux离线安装mysql

一、下载mysql 地址:MySQL 这里选择64为还是32为要根据操作系统来 uname -m 二、上传解压配置mysql 使用root账户登录linux服务器,在opt文件下创建mysql文件夹 cd /opt sudo mkdir mysql 使用Xftp上传mysql压缩包到此文件夹下(自行决定路径) cd mysql/…

Unity Editor编辑器扩展之创建脚本

前言 既然你看到这篇文章了,你是否也有需要使用代码创建脚本的需求?使用编辑器扩展工具根据不同的表格或者新增的内容去创建你想要的脚本。如果不使用工具,那么你只能不断去修改某个脚本,这项工作既繁琐也浪费时间。这个时候作为程…

蓝桥杯第十五界软件测试线下省赛题目分析及解决

PS 需要第十五界蓝桥杯被测系统或者功能测试模板、单元测试被测代码、自动化测试被测代码请加🐧:1940787338 备注:15界蓝桥杯省赛软件测试 题目1:功能测试 题目描述 ​ 某物流公司的货运收费标准根据重量、距离和节假日三个因素来确定。如…

【介绍下LeetCode的使用方法】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…

1.为什么选择Vue框架

参考:百战程序员 为什么选择Vue框架 Vue是什么? 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架 为什么要学习Vue Vue是目前前端最火的框架之一Vue是目前企业技术栈中要求的知识点Vue可以…

Lesson4--栈和队列

【本节目标】 1.栈 2.队列 3.栈和队列面试题 1.栈 1.1栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO &…

vue快速入门(三十五)组件通信-父传子

注释很详细&#xff0c;直接上代码 上一篇 新增内容 父组件传值子组件接收父组件传来的数据 源码 App.vue <template><div id"app"><!-- :item"item"为将item的值传递给MyTest组件 --><MyTest v-for"item in roles" :key&q…

【双曲几何】圆盘上的三角形概念

目录 一、说明二、对偶三角形概念2.1 反演关系2.2 对偶关系2.3 找出三角形的对偶三角形 三、正交三角形概念3.1 通过对偶三角形&#xff0c;找到垂心3.2 正交三角形的概念3.3 中心射影点的概念 四、后记 一、说明 本文对双曲空间的三角形进行分析&#xff0c;本篇首先给出&am…

(vue)el-select选择框加全选/清空/反选

(vue)el-select选择框加全选/清空/反选 <el-form-item label"批次"><el-selectv-model"formInline.processBatch"multiplecollapse-tagsfilterableplaceholder"请选择"style"width: 250px"no-data-text"请先选择企业、日…