(十三)linux中断底半部分处理机制

这篇文章介绍一下linux中断的底半部分的tasklet和workquene两种处理机制,其中tasklet中不能有延时函数,workquene的处理函数可以加入延时操作

目录

      • (一)tasklet小任务处理机制
          • (1)tasklet相关函数接口
          • (2)tasklet使用流程
          • (3)tasklet实例代码
      • (二)workquene工作队列处理机制
          • (1)workqueue相关函数接口
          • (2)共享工作队列使用流程
          • (3)自定义工作队列使用流程
          • (4)共享workqueue实例代码

在Linux中为了提高系统的响应速度及并发能力,将Linux的中断划分为顶半部和底半部两部分。
顶半部(top half):做中断的登记操作,当然也可以做不耗时的中断处理(内核中会创建一个中断登记表)。
顶半部完成的一般是紧急的硬件操作,一般包括读取寄存的中断状态,清除中断标志,将底半部处理程序挂到底半部的执行队列中去,此过程不可被打断
底半部(bottom half):处理耗时操作,把耗时的操作放入底半部执行,这个过程可以被打断,耗时操作推后执行

(一)tasklet小任务处理机制

内核中关于tasklet的介绍:

/* Tasklets --- multithreaded analogue of BHs.Main feature differing them of generic softirqs: taskletis running only on one CPU simultaneously.Main feature differing them of BHs: different taskletsmay be run simultaneously on different CPUs.Properties:* If tasklet_schedule() is called, then tasklet is guaranteedto be executed on some cpu at least once after this.* If the tasklet is already scheduled, but its execution is still notstarted, it will be executed only once.* If this tasklet is already running on another CPU (or schedule is calledfrom tasklet itself), it is rescheduled for later.* Tasklet is strictly serialized wrt itself, but notwrt another tasklets. If client needs some intertask synchronization,he makes it with spinlocks.
(1)tasklet相关函数接口

小任务机制相关的数据结构:

struct tasklet_struct
{struct tasklet_struct *next;	//用来实现多个tasklet_struct结构链表unsigned long state;			//当前这个tasklet是否已经被调度atomic_t count;					//值为0的时候用户才可以调度/*原子变量操作:指的是操作过程中不允许被打断机制typedef struct {int counter;} atomic_t;	*/void (*func)(unsigned long);	//指向tasklet绑定的函数指针unsigned long data;				//传向tasklet绑定的函数的参数
};

小任务数据结构创建:

#define DECLARE_TASKLET(name, func, data) \        //静态初始化,默认为使能
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }#define DECLARE_TASKLET_DISABLED(name, func, data) \      //静态初始化,默认为失能
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

初始化小任务:

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

小任务加锁解锁:

//尝试加锁
static inline int tasklet_trylock(struct tasklet_struct *t)
{return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}//解锁
static inline void tasklet_unlock(struct tasklet_struct *t)
{smp_mb__before_clear_bit(); clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
/*** test_and_set_bit - Set a bit and return its old value* @nr: Bit to set* @addr: Address to count from** This operation is atomic and cannot be reordered.* It may be reordered on other architectures than x86.* It also implies a memory barrier.*/
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
{unsigned long mask = BIT_MASK(nr);unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);unsigned long old;unsigned long flags;_atomic_spin_lock_irqsave(p, flags);old = *p;*p = old | mask;_atomic_spin_unlock_irqrestore(p, flags);return (old & mask) != 0;
}

小任务登记:

static inline void tasklet_schedule(struct tasklet_struct *t)
{if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))__tasklet_schedule(t);
}

小任务失能:

static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{atomic_inc(&t->count);smp_mb__after_atomic_inc();
}static inline void tasklet_disable(struct tasklet_struct *t)
{tasklet_disable_nosync(t);tasklet_unlock_wait(t);smp_mb();
}

小任务使能:

static inline void tasklet_enable(struct tasklet_struct *t)
{smp_mb__before_atomic_dec();atomic_dec(&t->count);		//单纯的将count减一操作
}

结束小任务:

extern void tasklet_kill(struct tasklet_struct *t);
extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);
(2)tasklet使用流程
  1. 定义结构体并初始化
struct tasklet_struct task;
tasklet_init(&task,自定义函数功能名,函数形参);
  1. 在合适的地方(一般在中断里)对tasklet登记
   tasklet_schedule(&task);
(3)tasklet实例代码

chrdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>struct tasklet_struct task;void tasklet_fun(unsigned long data)
{printk("this is tasklet test\n");
}static int __init  tasklet_module_init(void)
{tasklet_init(&task,tasklet_fun,(unsigned long)10);//tasklet_disable(&task);//失能后不能卸载该tasklettasklet_schedule(&task);return 0;
}static void __exit  tasklet_module_cleanup(void)
{tasklet_kill(&task);
}
module_init(tasklet_module_init);
module_exit(tasklet_module_cleanup);
MODULE_LICENSE("GPL");

(二)workquene工作队列处理机制

工作队列提供了将功能推迟到下半部分的通用方法。核心是工作队列(struct workqueue_struct),这是工作所在的结构。内核中通过work_struct结构标识要延迟的工作和要使用的延迟功能。events / X内核线程(每个CPU一个)从工作队列中提取工作,并激活下半部处理程序之一。

工作队列是更新的延迟机制,已在2.5 Linux内核版本中添加。工作队列不是通用的延迟机制,不像Tasklet那样提供一站式的延迟方案,在该机制中,工作队列的处理函数可以休眠(在Tasklet模型中是不可能的),工作队列的延迟可能比任务小,但包含更丰富的API以进行工作延迟,延迟之前是通过keventd任务队列管理,现在由名为events / X的内核工作线程管理。

在这里插入图片描述

内核中有两种工作队列,一种是共享工作队列,另一种是自定义工作队列

共享工作队列 :内核提供,用户可直接使用,秩序调用对应的接口即可,更多的时候选择共享消息队列

自定义工作队列:需要用户手动创建,并手动销毁

共享工作队列自定义工作队列
内核启动期间会创建一个工作全局的工作队列,所有的驱动都可以把自己延后执行的工作函数挂到这个共享工作队列中。当你要执行工作不希望受到其他工作的影响时,可以自己创建一个工作队列,然后把自己的工作放在自定义的工作队列调度。
优点:不需要自己创建工作队列,简单,快捷,方便。优点:不会受到其他工作的影响,工作函数执行有保障。
缺点:可能会受到其他工作的影响,前面的工作阻塞,影响到后面的工作的执缺点:造成系统巨大开销大,如果过多创建自定义工作队列,会严重影响系统实时
(1)workqueue相关函数接口

工作队列数据结构:

struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};typedef void (*work_func_t)(struct work_struct *work);

声明并初始化工作队列:

 1.静态方式:
#define DECLARE_WORK(n, f)					\struct work_struct n = __WORK_INITIALIZER(n, f)#define DECLARE_DELAYED_WORK(n, f)				\struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f)2. 动态形式初始化:
#define INIT_WORK(_work, _func)					\do {							\__INIT_WORK((_work), (_func), 0);		\} while (0)

创建自定义工作队列的时候使用:

extern int queue_work(struct workqueue_struct *wq, struct work_struct *work);

工作队列登记:

extern int schedule_work(struct work_struct *work);
/*** schedule_work - put work task in global workqueue* @work: job to be done** Returns zero if @work was already on the kernel-global workqueue and* non-zero otherwise.** This puts a job in the kernel-global workqueue if it was not already* queued and leaves it in the same position on the kernel-global* workqueue otherwise.*/
int schedule_work(struct work_struct *work)
{return queue_work(system_wq, work);
}

根据结构体成员找到结构体首地址:

/*** container_of - cast a member of a structure out to the containing structure* @ptr:	the pointer to the member.* @type:	the type of the container struct this is embedded in.* @member:	the name of the member within the struct.**/
#define container_of(ptr, type, member) ({			\const typeof( ((type *)0)->member ) *__mptr = (ptr);	\(type *)( (char *)__mptr - offsetof(type,member) );})

container_of使用示例:

struct mywork{int m;int n;struct work_struct works;
}test;container_of根据结构体内部的某一成员获取结构的首地址container_of(ptr, type, member) @ptr:	指向结构体成员的指针.  如 struct work_struct *works;* @type:	the type of the container struct this is embedded in.  结构体类型 struct mywork* @member: the name of the member within the struct.  works
(2)共享工作队列使用流程

1.定义共享工作队列结构体并初始化

struct work_struct works;
INIT_WORK(&works,workqueue_fun);

2.在合适位置(一般为中断)对工作队列登记

 schedule_work(&works);
(3)自定义工作队列使用流程

1、创建工作队列

struct workqueue_struct  my_workqueue;
struct workqueue_struct *create_workqueue(&my_workqueue);  
//struct workqueue_struct *create_singlethread_workqueue(const char *name);  
//create_workqueue函数会在系统中的每个处理器上创建一个线程(多线程),而create_singlethread_workqueue只是创建一个单一的线程,如果单个线程足够使用,那么应该使用create_singlethread_workqueue函数。

2、创建任务

struct work_struct works;
INIT_WORK(&works,workqueue_fun);

3、提交任务,要将任务提交到工作队列中,内核提供了下面两个API:

int queue_work(struct workqueue_struct *wq, struct work_struct *work);  
int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);  

这两个函数都会将任务提交到工作队列中,使用queue_delayed_work函数,则提交的任务至少延时由参数delay指定的时间才被执行。

如果要取消工作队列中的某个任务,使用cancel_delayed_work,原型如下:

int cancel_delayed_work(struct work_struct *work);  

如果任务在被执行之前取消,那么cancel_delayed_work函数返回非零值,调用该函数之后内核会确保被取消的任务不被执行。但是返回0,则表示任务已经被执行,因此调用cancel_delayed_work函数后,任务有可能仍在运行,所以为了确保任务测地被取消,需要调用flush_workqueue函数,与方法1中的不同。

void flush_workqueue(struct workqueue_struct *wq);  

4、销毁工作队列, 使用完工作队列之后,可以使用destroy_workqueue销毁工作队列:

void destroy_workqueue(struct workqueue_struct *wq); 
(4)共享workqueue实例代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>struct work_struct works;
void workqueue_fun(struct work_struct * work)
{printk("this is workqueue test\n");
}
static int __init  workqueue_module_init(void)
{INIT_WORK(&works,workqueue_fun);//初始化共享工作队列结构体schedule_work(&works);//将工作队列进行登记return 0;
}
static void __exit  workqueue_module_cleanup(void)
{printk("module is exit\n");
}
module_init(workqueue_module_init);
module_exit(workqueue_module_cleanup);
MODULE_LICENSE("GPL");

本文章仅供学习交流用禁止用作商业用途,文中内容来水枂编辑,如需转载请告知,谢谢合作

微信公众号:zhjj0729

微博:文艺to青年

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

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

相关文章

vmware中装的ubuntu上不了网

本文章针对桥接方式进行讲解&#xff0c;如果需要另外两种连接方式请参考文末给出的链接 &#xff08;一&#xff09;问题 主机和虚拟机可以相互ping通&#xff0c;但是却不能ping网址 &#xff08;二&#xff09;解决办法 vmware为我们提供了三种网络工作模式&#xff0c;…

关于gedit的编码问题

今天由于gedit的编码格式导致LCD显示屏的问题&#xff0c;开始没有想到后来才发现&#xff0c;在这记录一下 #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h>…

c语言表白程序代码

双十一要到了&#xff0c;好激动啊&#xff01;&#xff01;&#xff01; 是时候准备出手了&#xff01; 花了一天的时间写的表白代码。 表示自己弱弱的..... 看了网上好多都是js写的&#xff0c;感觉碉堡了&#xff01;js用的不熟&#xff0c;前端不好&#xff0c;java&#x…

tiny4412移植tslib库

1、将tslib-1.4.tar.gz拷贝到虚拟机某个路径进行解压 2、进入解压路径tslib 3、执行#./autogen.sh 如果提示&#xff1a;./autogen.sh: 4: ./autogen.sh: autoreconf: not found 原因&#xff1a;没有安装automake工具, 解决办法:需要安装此工具&#xff1a; apt-get instal…

移植QT到tiny4412开发板

目录&#xff08;一&#xff09; 环境准备&#xff08;二&#xff09; Qt源代码下载&#xff08;三&#xff09; 移植tslib库&#xff08;四&#xff09;操作流程1.解压qt源码包2.配置编译环境3.生成Makefile4.编译安装5.安装一些库用来支持 qt6. 添加以下内容到开发板目录下的…

eclipse导入web项目之后项目中出现小红叉解决办法

项目中有小红叉我遇到的最常见的情况&#xff1a; 1、项目代码本身有问题。&#xff08;这个就不说了&#xff0c;解决错误就OK&#xff09; 2、项目中的jar包丢失。&#xff08;有时候eclipse打开时会出现jar包丢失的情况&#xff0c;关闭eclipse重新打开或者重新引入jar包就O…

windows下实现Git在局域网使用

1.首先在主机A上创建一个文件夹用于存放你要公开的版本库。然后进入这个文件夹&#xff0c;右键->Git create repository here&#xff0c;弹出的窗口中勾选Make it Bare&#xff01;之后将这个文件夹完全共享&#xff08;共享都会吧&#xff1f;注意权限要让使用这个文件夹…

lintcode 滑动窗口的最大值(双端队列)

题目链接&#xff1a;http://www.lintcode.com/zh-cn/problem/sliding-window-maximum/# 滑动窗口的最大值 给出一个可能包含重复的整数数组&#xff0c;和一个大小为 k 的滑动窗口, 从左到右在数组中滑动这个窗口&#xff0c;找到数组中每个窗口内的最大值。 样例 给出数组 [1…

适用于Linux的Windows子系统WSL

以前使用的都是在虚拟机里安装linux&#xff0c;最近才发现在win10提供了WSL(Windows Subsystem for Linux) &#xff0c;简单来说就是可以在win10里面直接使用Linux。 &#xff08;一&#xff09;首先打开Microsoft Store , 搜索 Linux &#xff08;二&#xff09;选择自己需…

jsp通过易宝方式实现在线支付

项目下载地址: https://github.com/hjzgg/OnlinePayment 参考&#xff1a;http://blog.csdn.net/jadyer/article/details/7380259?utm_sourcetuicool&utm_mediumreferral 效果图1&#xff1a;请求界面 效果图2&#xff1a;地支付请求和易宝之间建立连接之后跳转到相应的银…

nand flash和nor flash的这几点区别你知道吗?

这篇文章讲解nand flash和nor flash的特点和区别&#xff0c;不涉及存储原理的讲解 &#xff08;一&#xff09;Flash简介 FLASH是一种存储芯片&#xff0c;全名叫Flash EEPROM Memory&#xff0c;通地过程序可以修改数据&#xff0c;即平时所说的“闪存”。Flash又分为NAND f…

windows8建立局域网的方法

win8建立局域网的方法&#xff1a;1、首先笔记本有无线网卡且支持 虚拟WIFI ;2、按winX键&#xff0c;选择"命令提示符(管理员)A"; 3、输入"netsh wlan set hostednetwork modeallow ssid网络名称 key我的密码" ; 4、接着输入"netsh wlan start hoste…

内核移植出现:Kernel panic - not syncing: No init found.

今天在升级SDK的时候&#xff0c;升级到kernel时遇到如题所述的问题&#xff0c;花了天时间调通&#xff0c;在这里记录一下。 报错提示&#xff1a;(当时没有记录&#xff0c;错误的提示大概如下) Kernel panic - not syncing: No init found. Try passing init option to k…

32位和64位机器上C语言数据类型的大小

作为嵌入式开发的人员&#xff0c;是必须了解C语言在不同位数机器上占用的字节大小的&#xff0c;下面做下对比 不同位数平台对比&#xff1a; \16位平台32位平台64位平台char1个字节8位1个字节8位1个字节short2个字节16位2个字节16位2个字节int2个字节16位4个字节32位 4个字节…

lintcode最长回文子串(Manacher算法)

题目来自lintcode, 链接&#xff1a;http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/ 最长回文子串 给出一个字符串&#xff08;假设长度最长为1000&#xff09;&#xff0c;求出它的最长回文子串&#xff0c;你可以假定只有一个满足条件的最长回文串。…

全排列总结

接触全排列已经好长时间了&#xff0c;一直没有抽空总结一下全排列的相关问题&#xff0c;下面来说一下&#xff01; 排列 一般地&#xff0c;从n个不同元素中取出m&#xff08;m≤n&#xff09;个元素&#xff0c;按照一定的顺序排成一列&#xff0c;叫做从n个元素中取出m个元…

大小端问题傻傻分不清?

先来熟悉一下概念&#xff1a; 大端&#xff1a;数据的高位数据保存在低位地址&#xff0c;数据的低位数据保存在高地址 小端&#xff1a;数据的高位数据保存在高位地址&#xff0c;数据的低位数据保存在低地址为什么会存在大小端的问题&#xff1f; 这是因为在计算机系统中&a…

mount --bind的用处

&#xff08;一&#xff09;mount --bind介绍 mount --bind的作用是将两个目录连接起来&#xff0c;例如&#xff1a;mount ---bind /dir1 /dir2 是将dir1目录挂载到dir2目录上&#xff0c;下面来实际演示一下&#xff1a; 上面的操作中首先创建了dir1 dir2两个目录&#xf…

lintcode 落单的数(位操作)

题目1 落单的数 给出2*n 1 个的数字&#xff0c;除其中一个数字之外其他每个数字均出现两次&#xff0c;找到这个数字。 链接&#xff1a;http://www.lintcode.com/zh-cn/problem/single-number/ 样例 给出 [1,2,2,1,3,4,3]&#xff0c;返回 4 挑战 一次遍历&#xff0c;常数级…

旋转图像

旋转图像 给定一个NN的二维矩阵表示图像&#xff0c;90度顺时针旋转图像。 看个例子 算法1&#xff1a; 如上图所示&#xff0c;设一个N阶二维矩阵&#xff0c;则将矩阵从外向里可以分成N/2个圈&#xff0c;例如&#xff08;1 2 3 4 8 12 16 15 14 13 9 5&#xff09;这是最外边…