Linux中断底半部机制总结

news2024/11/19 5:57:38

linux实现底半部的机制主要有tasklet、workqueue、softirq。

1.tasklet

tasklet的使用较为简单,它的执行上下文是软中断,所以在tasklet中不能睡眠,它的执行时机通常是中断顶半部返回的时候。我们只需要定义tasklet及其处理函数,并将两者关联起来即可,例如:

1 void my_tasklet_func(unsigned long); /* 定义一个处理函数 */
2 DECLARE_TASKLET(my_tasklet, my_tasklet_func, data); /* 定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联 */

代码 DECLEARE_TASKLET(my_tasklet, my_tasklet_func, data)实现了定义名称为my_tasklet的tasklet,并将其与my_tasklet_func()这个函数绑定,而传入这个函数的参数为data。在需要调度tasklet的时候引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度运行:

tasklet_schedule(&my_tasklet);

 使用tasklet作为底半部处理中断的设备驱动程序模板如下:

复制代码

struct xxx_struct {
  int xxx_irq;
  ...
  ... 
};

static unsigned long data;

/* 定义 tasklet 和底半部函数并将它们关联 */
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, (unsigned long)&data); 
/*  中断处理底半部 */
void xxx_do_tasklet(unsigned long data)
{
   struct xxx_struct *pdata = (void *)*(unsigned long *)data;
   ...  
}
 
/* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
  ...
  data = (unsigned long)dev_id;
  tasklet_schedule(&xxx_tasklet);
  ...  
}
 
/* 设备驱动模块探测函数 */
static int xxx_probe(struct platform_device *pdev)
{
  ...
  struct xxx_struct *pdata = devm_kzalloc(&pdev->dev, sizeof(struct xxx_struct), GFP_KERNEL);
  if (!pdata) {
    dev_err(&pdev->dev, "Out of memory\n");
    return -ENOMEM; 
  } 
  
  platform_set_drvdata(pdev, pdata);
  /* 申请中断 */
  result = request_irq(pdata->xxx_irq, xxx_interrupt, 0, "xxx", pdata);
  ...
  return IRQ_HANDLED;  
}
 
/* 设备驱动模块remove函数 */
static int xxx_remove(struct platforn_device *pdev)
{
  struct xxx_struct *pdata = platform_get_drvdata(pdev);
  ...
  /* 释放中断 */
  free_irq(pdata->xxx_irq, NULL);
  ... 
}

复制代码

上述程序在模块加载函数中申请中断,并在模块卸载函数中释放它。对应于xxx_irq的中断处理程序被设置为xxx_interrupt()函数,在这个函数中,tasklet_schedule(&xxx_tasklet)调度被定义的tasklet函数xxx_do_tasklet()在适当的时候执行。

2.工作队列

工作队列的使用方法和tasklet非常相似,但是工作队列的执行上下文是内核线程,因此可以调度和睡眠。下面的代码用于定义一个工作队列和一个底半部执行函数:

struct work_struct my_wq;    /* 定义一个工作队列 */
void my_wq_func(struct work_struct *work); /* 定义一个处理函数 */

通过INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:

INIT_WORK(&my_wq, my_wq_func); /* 初始化工作队列并将其与处理函数绑定 */

与tasklet_schedule()对应的用于调度工作队列执行的函数为 schedule_work(),如:

schedule_work(&my_wq); /* 调度工作队列执行 */

使用工作队列处理中断底半部的设备驱动程序模板代码如下:

复制代码

struct xxx_struct {
  int xxx_irq;
  struct work_struct xxx_wq; /* 定义工作队列 */
  ...
  ...
};

/* 中断处理底半部 */
void xxx_do_work(struct work_struct *work)
{
  struct xxx_struct *pdata = container_of(work, struct xxx_struct, xxx_wq);
  ...
}

/* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
  struct xxx_struct *pdata = (struct xxx_struct *)dev_id;
  ...
  schedule_work(&pdata->xxx_wq);
  ...
  return IRQ_HANDLED;
}

/* 设备驱动模块探测函数 */
int xxx_probe(struct platform_device *pdev)
{
  ...
  struct xxx_struct *pdata = devm_kzalloc(&pdev->dev, sizeof(struct xxx_struct ), GFP_KERNEL);

  /*申请中断*/
  result = request_irq(pdata->xxx_irq, xxx_interrupt, 0, "xxx", pdata);
  ...
  /* 初始化工作队列 */
  INIT_WORK(&pdata->xxx_wq, xxx_do_work);
  platform_set_drvdata(pdev, pdata);
  ...
}

/* 设备驱动模块remove函数 */
int xxx_remove(struct platform_device *pdev)
{
  struct xxx_struct *pdata = platform_get_drvdata(pdev);
  ...
  cancel_work_sync(&pdata->xxx_wq);
  /* 释放中断 */
  free_irq(pdata->xxx_irq, NULL);
  ...
}

复制代码

工作队列早期的实现是在每个CPU核上创建一个worker内核线程,所有在这个核上调度的工作都在该worker线程中执行,其并发性显然差强人意。在linux 2.6.36以后,转而实现了 “Concurrency-managed workqueues”,简称"cmwq",cmwq会自动维护工作队列的线程池以提高并发性,同时保持了API的向后兼容。

延时工作队列

  1. 数据结构delayed_work用于处理延迟执行

复制代码

struct delayed_work {
    struct work_struct work;
    struct timer_list timer;

    /* target workqueue and CPU ->timer uses to queue ->work */
    struct workqueue_struct *wq;
    int cpu;
};

复制代码

  2.在工作队列中被调用的函数原形如下

typedef void (*work_func_t)(struct work_struct *work);

  3. 初始化数据结构

INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func)

  4. 提交延时任务到工作队列

int schedule_delayed_work(struct delayed_work *work, unsigned long delay);

  5.删除提交到工作队列的任务

int cancel_delayed_work(strcut delayed_work *work);

  6. 刷新默认工作队列(常跟cancle_delayed_work一起使用)

void flush_schedlue_work(void);
或者
int cancel_delayed_work_sync(strcut delayed_work *work);

使用延时工作队列处理中断底半部的设备驱动程序模板代码如下:

复制代码

struct xxx_struct {  
  int xxx_irq;
  struct delayed_work xxx_dwork; /* 定义延时工作队列 */
  ...
  ...
};

/* 中断处理底半部 */
void xxx_do_work(struct work_struct *work)
{
  struct xxx_struct *pdata = container_of(work, struct xxx_struct, xxx_dwork.work);
  ...
}

/* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
  struct xxx_struct *pdata = (struct xxx_struct *)dev_id;
  ...
  
  /*
   * delay 2000ms
   */
  schedule_delayed_work(&pdata->xxx_dwork, msesc_to_jiffies(2000)); 
  ...

  return IRQ_HANDLED;
}

/* 设备驱动模块探测函数 */
int xxx_probe(struct platform_device *pdev)
{
  ...
  struct xxx_struct *pdata = devm_kzalloc(&pdev->dev, sizeof(struct xxx_struct ), GFP_KERNEL);

  /*申请中断*/
  result = request_irq(pdata->xxx_irq, xxx_interrupt, 0, "xxx", pdata);
  ...
  /* 初始化工作队列 */
  INIT_DELAYED_WORK(&pdata->xxx_dwork, xxx_do_work);
  platform_set_drvdata(pdev, pdata);
  ...
}

/* 设备驱动模块remove函数 */
int xxx_remove(struct platform_device *pdev)
{
  struct xxx_struct *pdata = platform_get_drvdata(pdev);
  ...
  cancel_delayed_work(&pdata->xxx_dwork);
  cancel_delayed_work_sync(&pdata->xxx_dwork);

  /* 释放中断 */
  free_irq(pdata->xxx_irq, NULL);
  ...
}

复制代码

3.软中断

软中断(Softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,tasklet是基于软中断实现的,因此也运行于软中断上下文。

在Linux内核中,用softirq_action结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给该函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

软中断和tasklet运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进程上下文。因此,在软中断和tasklet处理函数中不允许睡眠,而在工作队列处理函数中允许睡眠。

local_bh_disable() 和 local_bh_enable() 是内核中用于禁止和使能软中断及tasklet底半部机制的函数。

内核中采用softirq的地方包括 HI_SOFTIRQ、TIMER_SOFTIRQ、NET_TX_SOFTIRQ、NET_RX_SOFTIRQ、SCSI_SOFTIRQ、TASKLET_SOFTIRQ等,一般来说,驱动的编写者不会也不宜直接使用softirq。

硬中断、软中断和信号的区别:

  硬中断是外部设备对CPU的中断,软中断是中断底半部的一种处理机制,而信号则是由内核(或其他进程)对某个进程的中断。在设计系统调用的场合,人们也常说通过软中断(例ARM为swi)陷入内核,此时软中断的概念是指由软件指令引发的中断,和我们这个地方所说的softirq是两个完全不同的概念,一个是software,一个是soft。

需要特别说明的是,软中断以及基于软中断的tasklet如果在某段时间内大量出现的话,内核会把后续软中断放入 ksoftirqd 内核线程中执行。总的来说,中断优先级高于软中断,软中断优先级又高于任何一个线程。软中断适度线程化,可以缓解高负载情况下系统的响应。

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

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

相关文章

【管理运筹学】第 7 章 | 图与网络分析(4,最大流问题)

系列文章目录 【管理运筹学】第 7 章 | 图与网络分析(1,图论背景以及基本概念、术语、矩阵表示) 【管理运筹学】第 7 章 | 图与网络分析(2,最小支撑树问题) 【管理运筹学】第 7 章 | 图与网络分析&#xf…

健身小程序制作流程详解

随着移动互联网的普及,越来越多的人开始关注健康和健身。为了满足这一需求,制作一款健身小程序已经成为一种趋势。本文将详细介绍如何使用第三方制作平台,如乔拓云网,制作健身小程序,让你轻松成为专家。 一、注册与登录…

CG MAGIC分享3ds Max卡顿未保存处理方法有哪些?

3ds Max进行建模、渲染这一系列过程中,大家使用中都会遇到各种原因导致软件卡顿或崩溃是很常见的情况。 可以说卡机没关系,可是卡顿发生时,如果之前的工作没有及时保存,可能会导致数据的丢失和时间的浪费。这就是最让人烦躁的了&…

Pytorch 多卡并行(1)—— 原理简介和 DDP 并行实践

近年来,深度学习模型的规模越来越大,需要处理的数据也越来越多,单卡训练的显存空间和计算效率都越来越难以满足需求。因此,多卡并行训练成为了一个必要的解决方案本文主要介绍使用 Pytorch 的 DistributedDataParallel&#xff08…

Java开发分布式抽奖系统

Lottery 基于Springboot,Dubbo 等开发的分布式抽奖系统 1. 环境 配置 规范 2. 搭建(DDD RPC)架构 DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统…

阿里云轻量应用服务器为什么便宜?CPU性能差吗?

阿里云轻量应用服务器2核2G3M带宽优惠价108元一年,轻量应用服务器为什么便宜?是因为性能差吗?并不是,轻量应用服务器不限制CPU基准性能,轻量有月流量限制,轻量不支持指定CPU处理器,阿里云轻量2核…

研发效能行业发展趋势及人才认证的重要性丨IDCF

在当今的高科技环境下,“研发效能”已成为企业竞争和发展的关键因素。近年来,随着技术的快速发展,研发效能行业也在不断演进,并且将在未来几年内持续发展。本文将探讨研发效能行业的发展趋势以及人才认证的重要性。 一、研发效能…

便捷查询中通快递,详细物流信息轻松获取

在如今快节奏的生活中,快递已成为人们生活中不可或缺的一部分。然而,快递查询却常常让人头疼,因为需要分别在不同的快递公司官网上进行查询,耗费时间和精力。为了解决这个问题,固乔科技推出了一款便捷的快递查询助手&a…

Deepface使用教程

一:github地址 GitHub - iperov/DeepFaceLab: DeepFaceLab is the leading software for creating deepfakes. GitHub - iperov/DeepFaceLive: Real-time face swap for PC streaming or video calls DeepFaceLab为视频处理。 DeepFaceLabLive为直播版本。 此处C…

vue3中css使用script中定义的变量

代码 <template><div class"box">haha</div> </template><script setup lang"ts"> const boxWidth 500px </script><style lang"scss"> .box {width: v-bind(boxWidth);height: 200px;background-c…

Allegro166版本如何在颜色管理器中实时显示层面操作指导

Allegro166版本如何在颜色管理器中实时显示层面操作指导 在用Allegro166进行PCB设计的时候,需要在颜色管理器中频繁的开关层面。但是166不像172一样在颜色管理器中可以实时的开关层面,如下图 需要打开Board Geometry/Soldermask_top层,首先需要勾选这个层面,再点击Apply即…

2023年车载超声波雷达行业研究报告

第一章 行业概况 车载超声波雷达&#xff0c;通常在英文中被称为“Automotive Ultrasonic Radar”或“Automotive Ultrasonic Sensor”&#xff0c;是一种使用超声波来检测车辆周围物体的距离的传感器。这个行业主要关注的是为汽车提供停车辅助、防撞和其他安全功能。 图 超声…

GDB调试方法汇总

gcc常用选项 选项含义描述-o filename指定输出文件名&#xff0c;在编译目标代码时&#xff0c;可不选&#xff0c;不指定filename时&#xff0c;默认文件名是a.out-c只编译不链接&#xff0c;生成目标文件.o-S只编译不汇编&#xff0c;生成汇编代码-E只进行预编译&#xff0c…

MyBatis:The error occurred while setting parameters;foreach语句不生效

根本原因就是在参数上&#xff0c;列举一下可能的原因&#xff1a; 1.sql语句中的传的参数类型和数据库中不一致 2.#{}写成${} 3.也有说是在sql语句后加了“&#xff1b;”有影响的 本人sql语句如下&#xff1a; 该条语句的参数是list&#xff0c;list中存着试卷对象&#xff…

雅思口语回答拿不准,chatgpt担任考官帮你润色文本

目录 你现在是一位雅思口语考官&#xff0c;请你现在以雅思口语的评价标准&#xff0c;来对于我的回答进行打分 问题是“Do you like wearing T-shirts?”&#xff0c;我即将在下一句话中进行回答&#xff0c;请你进行评价&#xff0c;请等待我的回答&#xff0c;请等待我的回…

【Linux】编辑器 vim

1、vim的基本概念 vi/vim【一款文本编辑器】vim【一款多模式编辑器】vi/vim 的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是 vim 是 vi 的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0…

Android高德地图截屏功能(可包含自定义控件)

一、不包含自定义控件 地图 SDK 支持对当前屏幕显示区域进行截屏&#xff0c;可以对地图、覆盖物&#xff08;包含信息窗口&#xff09;、Logo进行截取屏幕&#xff0c;这其中不包括地图控件、Toast窗口。 详细示例如下&#xff1a; // 对地图进行截屏aMap!!.getMapScreenSho…

ajax day3

3、将普通对象转为查询参数字符串形式&#xff1a; 创建URLSearchParams参数&#xff0c;再用toString方法转为字符串 4、xhr对象 请求参数&#xff1a;body参数 5、promise promise对象一旦被兑现或拒绝&#xff0c;就是已敲定了&#xff0c;状态无法再被改变。故此处先执行…

基于Python和mysql开发的智慧校园答题考试系统(源码+数据库+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python和mysql开发的智慧校园答题考试系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

第十章 数组和指针

本章介绍以下内容&#xff1a; 关键字&#xff1a;static 运算符&#xff1a;&、*&#xff08;一元&#xff09; 如何创建并初始化数组 指针&#xff08;在已学过的基础上&#xff09;、指针和数组的关系 编写处理数组的函数 二维数组 人们通常借助计算机完成统计每月的支出…