200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > (十三)linux中断底半部分处理机制

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

时间:2022-08-22 22:12:26

相关推荐

(十三)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使用流程
定义结构体并初始化

struct tasklet_struct task;tasklet_init(&task,自定义函数功能名,函数形参);

在合适的地方(一般在中断里)对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青年

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。