Linux中断(tasklet,工作队列,内核线程的使用)

文章目录

  • 前言
  • 一、tasklet
  • 二、工作队列
  • 三、内核线程
  • 总结


前言

本篇文章来讲解在Linux中tasklet,工作队列,内核线程的使用。

一、tasklet

tasklet在内核里面其实就是下面这样的一个结构体:

struct tasklet_struct
{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};

下面是对 struct tasklet_struct 结构体成员的解释:

struct tasklet_struct *next: 链表中的下一个任务队列项。任务队列项通过 next 成员在链表中连接在一起,可以形成一个队列。

unsigned long state: 任务队列项的状态标志。用于跟踪任务队列项当前的状态,例如等待执行、已禁用等。

atomic_t count: 计数器,用于跟踪任务队列项的引用计数。当某个任务队列项被多个地方引用时,可以通过增加计数器来避免释放或重用。

void (*func)(unsigned long): 函数指针,指向任务队列项需要执行的函数。当任务队列项被调度执行时,内核将调用该函数来执行任务。

unsigned long data: 存储额外的数据,供任务队列项函数使用。可以将数据与任务队列项关联起来,以在执行函数时传递给函数。

相关的三个重要函数:

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

tasklet_init() 函数用于初始化一个任务队列项。它接受三个参数:

tasklet:指向要初始化的 struct tasklet_struct 对象的指针。
func:指向任务队列项的处理函数的指针。该函数将在任务队列项被调度执行时被调用。
data:传递给任务队列项处理函数的数据。
通过调用 tasklet_init(),可以为任务队列项设置适当的处理函数和数据。初始化的任务队列项是处于禁用状态的,需要通过调用 tasklet_schedule() 来启用它。

tasklet_kill(struct tasklet_struct *tasklet)

tasklet_kill() 函数用于停止一个正在运行或已经被调度的任务队列项。它接受一个参数:

tasklet:指向要停止的任务队列项的指针。
调用 tasklet_kill() 将会禁用任务队列项,并确保它不会被再次调度执行。这在你不再需要该任务队列项时非常有用,可以确保它不会再干扰系统的正常操作。

tasklet_schedule(struct tasklet_struct *tasklet)

tasklet_schedule() 函数用于将一个任务队列项添加到任务队列中以待执行。它接受一个参数:

tasklet:指向要调度的任务队列项的指针。
调用 tasklet_schedule() 将会启用被调度的任务队列项,并安排它在适当的时机执行。在软中断上下文中调用该函数会立即执行任务队列项的处理函数,而在其他上下文中,例如进程上下文,稍后会调度执行。

任务队列项的处理函数将在任务队列中的适当时机被调用,以更好地与操作系统的内核执行过程进行协调。

使用tasklet步骤:

1.先定义tasklet,需要使用时调用tasklet_schedule,驱动卸载前调用tasklet_kill。

2.tasklet_schedule只是把tasklet放入内核队列,它的func函数会在软件中断的执行过程中被调用。

注意:

tasklet_schedule只会把tasklet放入队列一次,调用完成后需要再次放入队列中。

tasklet内部机制:

tasklet属于TASKLET_SOFTIRQ软件中断。
入口函数为tasklet_action。

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);
}

当驱动程序调用tasklet_schedule时,会设置tasklet的state为TASKLET_STATE_SCHED,并把它放入某个链表:

void __tasklet_schedule(struct tasklet_struct *t)
{unsigned long flags;local_irq_save(flags);t->next = NULL;*__this_cpu_read(tasklet_vec.tail) = t;__this_cpu_write(tasklet_vec.tail, &(t->next));raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_restore(flags);
}

当发生硬件中断时,内核处理完硬件中断后,会处理软件中断。对于TASKLET_SOFTIRQ软件中断,会调用tasklet_action函数。

注意:
1.tasklet_schedule调度tasklet时,其中的函数并不会立刻执行,而只是把tasklet放入队列。

2.调用一次tasklet_schedule,只会导致tasklnet的函数被执行一次。

3.如果tasklet的函数尚未执行,多次调用tasklet_schedule也是无效的,只会放入队列一次。

tasklet_action函数:
这个函数会将队列中的tasklet逐个取出来执行:

static __latent_entropy void tasklet_action(struct softirq_action *a)
{struct tasklet_struct *list;local_irq_disable();list = __this_cpu_read(tasklet_vec.head);__this_cpu_write(tasklet_vec.head, NULL);__this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));local_irq_enable();while (list) {struct tasklet_struct *t = list;list = list->next;if (tasklet_trylock(t)) {if (!atomic_read(&t->count)) {if (!test_and_clear_bit(TASKLET_STATE_SCHED,&t->state))BUG();t->func(t->data);tasklet_unlock(t);continue;}tasklet_unlock(t);}local_irq_disable();t->next = NULL;*__this_cpu_read(tasklet_vec.tail) = t;__this_cpu_write(tasklet_vec.tail, &(t->next));__raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_enable();}
}

二、工作队列

tasklet使用起来非常方便简单,但是tasklet依然是属于中断,在中断执行时,应用程序还是无法得到执行,这样就可能导致系统的卡顿。所以就有了工作队列。

工作队列在内核中的定义:

struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};

1.atomic_long_t data: 这是一个原子长整型变量,通常用于存储工作项相关的数据。原子型意味着对该变量的读取和写入是原子操作,可以在多线程环境下进行安全的并发访问。

2.struct list_head entry: 这是一个链表节点,用于将工作项(work_struct)添加到一个链表中。链表节点通常包含指向前一个节点和后一个节点的指针,以便在链表中进行插入、删除和遍历操作。

3.work_func_t func: 这是一个函数指针类型(work_func_t),用于指向执行工作项的函数。通过该函数指针,可以在工作队列中执行相应的操作或任务。

4.#ifdef CONFIG_LOCKDEP 和 struct lockdep_map lockdep_map: 这部分代码是一个预处理指令,用于条件编译。在特定的配置条件下(CONFIG_LOCKDEP宏定义为真),会包含struct lockdep_map类型的lockdep_map成员变量。lockdep_map用于在工作项中添加用于锁依赖分析的调试信息。

使用工作队列时,步骤如下:
① 构造一个work_struct结构体,里面有函数;
② 把这个work_struct结构体放入工作队列,内核线程就会运行work中的函数。

相关函数:

1.INIT_WORK:
INIT_WORK用于初始化一个工作项(struct work_struct)。其原型如下:

void INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));

该函数用于初始化一个工作项,并指定要执行的回调函数(func)。回调函数将在调用schedule_work函数时被调度执行。初始化后的工作项可以被添加到工作队列中,以便在合适的时机执行相关操作。

2.schedule_work:
schedule_work用于将一个工作项添加到工作队列,以便在合适的时机执行工作项的回调函数。其原型如下:

int schedule_work(struct work_struct *work);

该函数将指定的工作项添加到工作队列,工作队列会按照一定的调度机制在合适的时机执行工作项。工作项的回调函数将在调度时被执行。这样可以将一些延后执行的任务或者需要在工作队列上下文中执行的任务安排在合适的时机执行。

注意:schedule_work函数是异步的,意味着它只是将工作项添加到队列,然后立即返回,并不会等待工作项的执行完成。

三、内核线程

有了工作队列后就可以把耗时的任务放入工作队列中进行处理了,但是当有多个work时前面没有执行完的work就会影响到后面work的执行,这样的效率还是不高,那么这个时候就需要使用中断的线程化处理了。

使用这个机制需要使用到下面这个函数:

request_threaded_irq:

int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id);

该函数用于请求指定中断号(irq)的中断,并将中断处理函数(handler)注册到该中断上。当中断事件发生时,处理函数会被调用。

这个函数具有以下参数:

irq:要请求的中断号。

handler:中断处理函数,即中断发生时要调用的函数。它通常是一个标准的中断处理函数,接收中断相关的参数。

thread_fn:中断线程函数,当中断处理函数返回IRQ_WAKE_THREAD时会调用。它通常用于执行耗时操作,以便避免中断处理函数的执行时间过长。

irqflags:中断标志,用于指定中断的行为,例如共享中断、触发类型等。

devname:设备名称,用于标识请求中断的设备。

dev_id:设备ID,用于传递设备相关的数据给中断处理函数。

request_threaded_irq的工作流程如下:

1.检查中断号的有效性和是否已被占用。如果中断号无效或已被占用,函数会返回错误。

2.创建一个中断描述符(struct irq_desc)并初始化相应的字段。

3.将中断处理函数和中断线程函数注册到中断描述符的对应字段上。

4.配置中断描述符的中断行为,如中断类型、中断共享等。

5.将中断描述符添加到全局中断控制器中断描述符数组中,以便系统可以管理该中断。

6.启用中断,允许中断事件触发中断处理函数的调用。

在请求中断成功后,中断事件发生时,中断控制器会检测到对应的中断号,然后调用已注册的中断处理函数或中断线程函数(如果应用)。中断处理函数是在中断上下文中执行的,因此需要遵循中断上下文的要求,尽量做到快速和非阻塞。如果中断处理函数在处理中需要较长时间,可以调用wake_up_process函数唤醒中断线程函数,以便执行更复杂的工作。

总结而言,request_threaded_irq函数用于请求指定中断,并将中断处理函数注册到该中断上。它提供了一种在Linux内核中管理中断的机制,以响应硬件事件或触发的事件,处理与中断相关的操作

总结

本篇文章就讲解到这里,需要大家好好理解并进行实践。

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

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

相关文章

使用的华为云RDS数据库不小心把数据删了

目录 前言恢复qp文件帮助文档表级时间点恢复删除数据的时候要注意 前言 华为云查数据的时候前面是有个序号的,删除数据的时候不小心把序号看成id了,导致误删数据。 注:图片如果看不清楚可以点击放大观看! 恢复qp文件 华为云每天…

2023最新软件测试面试题(带答案)

1. 请自我介绍一下(需简单清楚的表述自已的基本情况,在这过程中要展现出自信,对工作有激情,上进,好学) 面试官您好,我叫###,今年26岁,来自江西九江,就读专业是电子商务,毕…

机器学习之主成分分析(Principal Component Analysis)

1 主成分分析介绍 1.1 什么是主成分分析 主成分分析(Principal Component Analysis)简称PCA,是一个非监督学习的机器学习算法,主要用于数据的降维,对于高维数据,通过降维,可以发现更便于人类理…

CVPR2023新作:pix2pix3D

Title: 3D-Aware Conditional Image SynthesisAffiliation: Carnegie Mellon University (卡内基梅隆大学)Authors: Kangle Deng, Gengshan Yang, Deva Ramanan, Jun-Yan ZhuKeywords: Image Synthesis, 3D-aware, Neural Radiance Fields, Interactive Editing, Conditional G…

Qt+GDAL开发笔记(一):在windows系统mingw32编译GDAL库、搭建开发环境和基础Demo

若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/131931309 红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

驱动开发 day3 (模块化驱动启动led,蜂鸣器,风扇,震动马达)

模块化驱动启动led,蜂鸣器,风扇,震动马达并加上Makefile 封装模块化驱动,可自由安装卸载驱动,便于驱动更新(附图) 1.安装模块驱动同时初始化各个设备并使能 2.该驱动会自动创建驱动节点. 3.通过c函数程序输入控制各个设备 4.卸载模块驱动 //编译驱动…

K8s卷存储详解(一)

K8s卷存储详解(一) K8s 存储K8s卷分类K8s目前支持的卷类型 临时卷类型EmptyDirCSI 临时卷通用临时卷 K8s 存储 什么是卷?为什么要用卷? 我们知道K8s是基于容器,对各个Pod进行管理的。Pod是由容器构成的,我…

开发中遇到的 cookie 问题

1. cookie 无法跨域携带问题 尽管已经登录,但是请求接口返回状态码:202,msg: 未登录,如下图所示; 1.1 XMLHttpRequest.withCredentials未设置 如果需要跨域 AJAX 请求发送 Cookie,需要withCre…

使用wxPython和pillow开发拼图小游戏(四)

上一篇介绍了使用本地图片来初始化游戏的方法,通过前边三篇,该小游戏的主要内容差不多介绍完了,最后这一篇来介绍下游戏用时的计算、重置游戏和关闭窗口事件处理 游戏用时的计算 对于游戏用时的记录,看过前几篇的小伙伴可能也发现…

MATLAB实现图像处理:图像识别、去雨、去雾、去噪、去模糊等等(附上20个完整仿真源码)

图像处理是计算机视觉领域的重要研究方向,MATLAB是一种功能强大的数学计算软件,可以用于图像处理和分析。下面是一些简单的MATLAB图像处理代码示例,包括图像增强、边缘检测、形态学处理、特征提取等。 文章目录 1. 图像增强2. 边缘检测3. 形态…

【雕爷学编程】Arduino动手做(88)---水流量传感器模块3

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

Springboot初识(二)

文章目录 前言一.Spring配置文件1.1 配置文件的作用1.2 配置文件的类型 二.properties配置⽂件说明2.1 基本语法2.2 读取配置文件 三.yml 配置⽂件说明2.1 基本语法2.2 读取配置文件2.3 注意事项 前言 上篇文章我们介绍了什么是SpringBoot和怎么去创建一个SpringBoot项目,现在…

Navicat连接服务器上Docker安装的mysql提示拒绝连接

一、报错的原因 英语翻译 --- ‘不允许主机连接到此MySQL服务器’ (意思是本地账号连接可以登录,但是远程登陆不行) 二、解决方法 ①进入docker中的mysql容器 docker exec -it mysql bash ②登陆mysql mysql -uroot -p ③执行以下步骤…

13.5.3 【Linux】PAM 模块设置语法

PAM 借由一个与程序相同文件名的配置文件来进行一连串的认证分析需求。我们同样以passwd 这个指令的调用 PAM 来说明好了。 当你执行 passwd 后,这支程序调用 PAM 的流程是: 1. 使用者开始执行 /usr/bin/passwd 这支程序,并输入密码&#xf…

Mongodb 多文档聚合操作处理方法(Map-reduce 函数)

聚合 聚合操作处理多个文档并返回计算结果。您可以使用聚合操作来: 将多个文档中的值分组在一起。 对分组数据执行操作以返回单个结果。 分析数据随时间的变化。 要执行聚合操作,您可以使用: 聚合管道 单一目的聚合方法 Map-reduce 函…

基于Java+SpringBoot+vue前后端分离在线商城系统设计实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

spring 存储对象 + 获取对象

前言 本篇在spring中如何使用五大类注释与方法注释将对象加入IOC容器中,了解如何使用注释来获取容器中的Bean对象,如有错误,请在评论区指正,让我们一起交流,共同进步! 文章目录 前言1.通过注释将类加入IoC…

【云计算小知识】云环境是什么意思?有什么优点?

随着云计算的快速发展,了解云计算相关知识也是运维人员必备的。那你知道云环境是什么意思?有什么优点?云环境安全威胁有哪些?如何保证云环境的运维安全?这里我们就来简单聊聊。 云环境是什么意思? 云环境是…

SpringBoot中配置文件的加载

springboot 启动会扫描一下位置的application.properties或者application.yml文件作为springboot的默认配置文件 file:./config/(项目根目录config文件夹下的配置文件) file:./(项目根目录下的配置文件) classpath:/config/(resources目录config文件下的配置文件) classpat…

如何在3ds max中创建可用于真人场景的巨型机器人:第 2 部分

推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建主体 步骤 1 打开 3ds Max。选择机器人头部后,二次单击鼠标并选择隐藏未选中。机器人的其他部分 除了头部之外,将被隐藏。 打开 3ds Max 步骤 2 在人脸选择模式下&#x…