精华内容
下载资源
问答
  • tasklet

    2019-05-04 14:51:44
    文章目录一、tasklet介绍1、tasklet简介2、tasklet特点二、tasklet实现1、tasklet数据结构1)struct tasklet_struct三、tasklet用法1、API函数2、编程步骤3、举一反三附录1、参考资料 一、tasklet介绍 1、tasklet...

    一、tasklet介绍


    1、tasklet简介

    tasklet属于Linux中断底半部的实现方法之一,它能够将工作延后执行。tasklet和软中断都是基于软中断的方式实现的。软中断的延后处理函数可以同时运行在多个CPU核上,效率很高,而tasklet延后处理函数同一时刻只能运行在一个CPU核上。

    2、tasklet特点

    1. tasklet有一个对应的延后处理函数,用于执行不紧急、耗时较长的内容。
    2. tasklet本身基于软中断实现,所以其延后处理函数不能进行休眠操作
    3. tasklet的优先级高于进程(如:工作队列),低于硬件中断。

    二、tasklet实现


    1、tasklet数据结构

    1)struct tasklet_struct

    include/linux/interrupt.h

    struct tasklet_struct
    {
    		struct tasklet_struct *next;
    		unsigned long state;
    		/* @count: 0 - enable, 1 - disable */
    		atomic_t count;
    		/* @func: tasklet延后处理函数,用于处理不紧急且耗时较长的内容,不能进行休眠操作 */
    		void (*func)(unsigned long);
    		/* @data: 传递给 @func的参数 */
    		unsigned long data;
    };
    

    三、tasklet用法


    1、API函数

    函数功能
    tasklet_init初始化tasklet。
    DECLARE_TASKLET定义并初始化tasklet。
    DECLARE_TASKLET_DISABLED定义并初始化tasklet,tasklet处于disable状态,即禁止该tasklet被tasklet schedule调度。
    tasklet_schedule向内核登记tasklet延后处理函数,将来内核在适当的时候会执行其延后处理函数。
    tasklet_kill从队列中删除某个tasklet

    2、编程步骤

    1. 定义并初始化一个tasklet对象,指定延后处理函数,是否需要传递参数可自行决定。
    2. 编写延后处理函数,执行不紧急、耗时较长的内容,不能进行休眠操作。
    3. 在某个地方登记tasklet延后处理函数,如:在顶半部中断处理函数中进行登记,也可以单独登记。

    3、举一反三

    参见: /modules/c_tasklet

    附录


    1、参考资料

    1. 以上源码内容参考自:Linux-4.15.18
    展开全文
  • Tasklet

    2016-09-08 17:59:04
    Tasklet 有的时候在驱动程序中需要延迟某些操作的进行,最典型的操作就是在驱动程序的中断处理函数延迟操作。比如在DMA驱动中,当数据传输完毕之后会触发中断的,通常这时候会启动一个tasklet来完成耗时的操作,也...

    Tasklet

    有的时候在驱动程序中需要延迟某些操作的进行,最典型的操作就是在驱动程序的中断处理函数延迟操作。比如在DMA驱动中,当数据传输完毕之后会触发中断的,通常这时候会启动一个tasklet来完成耗时的操作,也就是中断的下半部,让中断尽早的返回。
    在Softirq中说过了,Tasklet的实现是基于Softirq的。也就是说Tasklet是Softirq中的一种。根据优先级不同,Linux将Tasklet分为两类如下:
    enum
    {
    	HI_SOFTIRQ=0,
    	TIMER_SOFTIRQ,
    	NET_TX_SOFTIRQ,
    	NET_RX_SOFTIRQ,
    	BLOCK_SOFTIRQ,
    	BLOCK_IOPOLL_SOFTIRQ,
    	TASKLET_SOFTIRQ,
    	SCHED_SOFTIRQ,
    	HRTIMER_SOFTIRQ,
    	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
    
    	NR_SOFTIRQS
    };
    其中HI_SOFTIRQ和TASKLET_SOFTIRQ就是提供给Tasklet使用,HI_SOFTIRQ的优先级比较高,一般驱动程序很少见到使用。

    Tasklet定义

    linux内核使用tasklet_struct结构体来表示一个Tasklet
    struct tasklet_struct
    {
    	struct tasklet_struct *next;
    	unsigned long state;
    	atomic_t count;
    	void (*func)(unsigned long);
    	unsigned long data;
    };
    next:    用于指向下一个Tasklet
    state:   用于表示当前Tasklet的状态,linux内核定义了两种状态,如下:
    enum
    {
    	TASKLET_STATE_SCHED,	/* Tasklet is scheduled for execution */
    	TASKLET_STATE_RUN	/* Tasklet is running (SMP only) */
    };
    TASKLET_STATE_SCHED用于表示当前tasklet已经被提交,但是还没有执行,可能还在Tasklet列表中。
    TASKLET_STATE_RUN用于表示当前tasklet正在执行,此状态只在SMP系统下有效。
    count:  用于表示当前tasklet是否disable/enable。如果count=0表示tasklet是enabled,可以被调度执行,否则不可以被调度。
    func:   该tasklet上的延迟回调函数,而date则是该回调函数的参数。

    申明Tasklet

    如果在驱动程序中需要使用tasklet,就需要先申明一个tasklet,驱动程序可以使用如下的宏同时初始化一个tasklet
    #define DECLARE_TASKLET(name, func, data) \
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
    同样内核也提供了一个相似的宏,用来申明一个disable状态的tasklet
    #define DECLARE_TASKLET_DISABLED(name, func, data) \
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
    除了静态申明以及初始化一个tasklet,内核也提供一个动态初始化tasklet的函数
    void tasklet_init(struct tasklet_struct *t,
    		  void (*func)(unsigned long), unsigned long data)
    {
    	t->next = NULL;
    	t->state = 0;
    	atomic_set(&t->count, 0);
    	t->func = func;
    	t->data = data;
    }

    管理tasklet

    驱动程序在申明了一个tasklet之后,就需要将其加入到系统的tasklet管理链表中来,内核定义了如下的结构用来管理tasklet
    struct tasklet_head {
    	struct tasklet_struct *head;
    	struct tasklet_struct **tail;
    };
    
    static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
    static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
    head:  用来执行tasklet对象链接的第一个节点。
    tail:     tail是个双指针,用来执行tasklet_struct指针的指针,而每个tasklet_struct中有一个next指针,所以可以理解tail总是指向最后一个节点的next。
    系统为HI_SOFTIRQ和TASKLET_SOFTIRQ类型定义了各自的tasklet_head管理链表,而且tasklet_vec和tasklet_hi_vec都是per-cpu变量。
    也就是说系统使用tasklet_vec用来管理系统中所有softirq类型为TASKLET_SOFTIRQ的tasklet, tasklet_hi_vec同理。

    提交tasklet

    当在驱动程序中初始化一个tasklet之后,在需要延迟的时候就需要将该tasklet加入到系统的tasklet_vec链表中,提交到系统中。linux系统使用tasklet_schedule函数用来提交一个tasklet
    static inline void tasklet_schedule(struct tasklet_struct *t)
    {
    	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
    		__tasklet_schedule(t);
    }
    函数首先要提交的tasklet的state上的TASKLET_STATE_SCHED位是否已经置1,如果置1说明该tasklet已经被提交过,如果没有置1,则返回的是0,同时将state的TASKLET_STATE_SCHED位置1,置1之后表示这个tasklet已经被提交了。test_and_set_bit函数的作用就是返回旧的值,设置新的值。
    void __tasklet_schedule(struct tasklet_struct *t)
    {
    	unsigned long flags;
    
    	local_irq_save(flags);                                       ---------------A
    	t->next = NULL;                                              ---------------B
    	*__this_cpu_read(tasklet_vec.tail) = t;
    	__this_cpu_write(tasklet_vec.tail, &(t->next));
    	raise_softirq_irqoff(TASKLET_SOFTIRQ);                       ----------------C
    	local_irq_restore(flags);                                    ----------------D
    }
    A: 关闭本地中断,虽然tasklet_vec是per-cpu变量,但是如果不关闭中断仍然会存在并发的情况,需要关闭本地中断。

    B:  此处的操作就是将t加入到tasklet_vec链表的末尾,同时需要更改tail和next指针的指向。

    C: 这时候就需要触发softirq,在softirq小节有讲到。

    D: 恢复中断。

    Tasklet机制的初始化

    在开机启动的时候,内核使用softirq_init函数来初始化softirq, 用来安装了tasklet的执行函数。
    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);
    }
    可以看到为每个cpu都初始化了tasklet_vec和tasklet_hi_vec, 同时为TASKLET_SOFTIRQ和HI_SOFTIRQ安装了各自对于的处理函数。

    执行Tasklet

    当一个tasklet提交到系统之后,系统会在合适的时机处理该tasklet,最终调用到tasklet_action函数中。
    static void tasklet_action(struct softirq_action *a)
    {
    	struct tasklet_struct *list;
    
    	local_irq_disable();                                                      ------------------------A
    	list = __this_cpu_read(tasklet_vec.head);                                 ------------------------B
    	__this_cpu_write(tasklet_vec.head, NULL);                                  -----------------------C
    	__this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));       ------------------------D
    	local_irq_enable();
    
    	while (list) {
    		struct tasklet_struct *t = list;
    
    		list = list->next;
    
    		if (tasklet_trylock(t)) {                                           ------------------------E
    			if (!atomic_read(&t->count)) {                              -----------------------F
    				if (!test_and_clear_bit(TASKLET_STATE_SCHED,        ------------------------H
    							&t->state))
    					BUG();
    				t->func(t->data);                                    -----------------------P
    				tasklet_unlock(t);                                   -----------------------G
    				continue;
    			}
    			tasklet_unlock(t);
    		}
    
    		local_irq_disable();                                              -----------------------Q
    		t->next = NULL;                                                   -----------------------X
    		*__this_cpu_read(tasklet_vec.tail) = t;    
    		__this_cpu_write(tasklet_vec.tail, &(t->next));
    		__raise_softirq_irqoff(TASKLET_SOFTIRQ);                          ---------------------Y
    		local_irq_enable();
    	}
    }
    
    A:    因为接下来需要操作到tasklet_vec,需要关闭本地中断,防止并发操作影响到tasklet_vec的数据。
    B:    将整个tasklet_vec链表的数据保存到临时变量list中。
    C:    将整个tasklet_vec的head指向NULL。
    D:    同时初始化tail执行为NULL
    E:    这时候就从list链表取出一个tasklet,首先通过tasklet_trylock函数判断
    static inline int tasklet_trylock(struct tasklet_struct *t)
    {
    	return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
    }
    该函数就是判断state的TASKLET_START_RUN是否设置为1, 如果已经设置为1,则返回0,否则返回1。同时会设置TASKLET_START_RUN的位为1。
    那这里为什么需要判断这个TASKLET_START_RUN标志呢?  根据前面的知识此标志只有在SMP系统有效。所以在SMP系统下有如下的情况:
    假如有来两个处理的系统A处理器和B处理器。当前有个tasklet任务T已经提交到处理器A,并且已经调度执行了,此时TASKLET_STATE_SCHED状态已经清0。此时假设外部发生了一次中断,系统将此次中断处理权交给了B处理器,而在B的中断处理函数中调用了tasklet_schedule把tasklet T提交到B处理器,因为此taksklet T的状态TASKLET_STATE_SCHED的状态已经清0,从而可以提交成功。这样一下同一个Tasklet就有可能出现在两个处理器上执行,所以引入了TASKLET_START_RUN标志。还是前面的情况,因为在A处理器已经设置了TASKLET_STATE_RUN标志,所以在B处理器执行的时候就会失败,从而防止了统一个tasklet执行在多个处理器上。当然了这种情况只出现在SMP系统中。

    F:    读取count的值,判断是否是enable的tasklet。disable的tasklet是不允许执行的。
    H:    将state的TASKLET_STATE_SCHED的位清0。
    P:    执行该tasklet的回调处理函数
    G:    将state的TASKLET_START_RUN的位清0
    Q:    因为接下来需要操作到tasklet_vec变量,为了防止中断引入并发问题,关闭中断
    X:    将上述不符合条件的tasklet重新加入到tasklet_vec链表中进行管理。因为在上述的执行的过程中也有新的tasklet加入到tasklet_vec中
    Y:    再次触发softirq。

    其他Tasklet操作

    enable一个tasklet
    static inline void tasklet_enable(struct tasklet_struct *t)
    {
    	smp_mb__before_atomic();
    	atomic_dec(&t->count);
    }
    将指定的tasklet对象t上的coun减去1。

    disable一个tasklet
    static inline void tasklet_unlock_wait(struct tasklet_struct *t)
    {
    	while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
    }
    
    static inline void tasklet_disable_nosync(struct tasklet_struct *t)
    {
    	atomic_inc(&t->count);
    	smp_mb__after_atomic();
    }
    
    static inline void tasklet_disable(struct tasklet_struct *t)
    {
    	tasklet_disable_nosync(t);
    	tasklet_unlock_wait(t);
    	smp_mb();
    }
    内核提供了两个版本的disable函数,相比tasklet_disable_nosync函数tasklet_disable函数在disable的时候会调用tasklet_unlock_wait等待,主要是等待TASKLET_START_RUN的状态被清除,也就是等待tasklet被处理完毕后在disable。

    kill掉一个tasklet
    void tasklet_kill(struct tasklet_struct *t)
    {
    	if (in_interrupt())
    		pr_notice("Attempt to kill tasklet from interrupt\n");
    
    	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
    		do {
    			yield();
    		} while (test_bit(TASKLET_STATE_SCHED, &t->state));
    	}
    	tasklet_unlock_wait(t);
    	clear_bit(TASKLET_STATE_SCHED, &t->state);
    该函数最终会通过清除TAKLET_STATE_SCHED状态位,使器不再被调用。而如果当前tasklet已经提交但是没有执行,tasklet_kill将会睡眠直到该tasklet从tasklet_vec链表中删除。如果当前tasklet已经提交并且正在运行,这时候就需要等待tasklet运行完毕。该函数一般会在驱动的卸载函数中被调用到。


    展开全文
  • Tasklet 实现

    2021-01-27 16:26:42
    记住 tasklet 是一个特殊的函数, 可能被调度来运行, 在软中断上下文, 在一个系统决定的安全时间中. 它们可能被调度运行多次, 但是 tasklet 调度不累积; ; tasklet 只运行一次, 即便它在被投放前被重复请求. 没有 ...

    记住 tasklet 是一个特殊的函数, 可能被调度来运行, 在软中断上下文, 在一个系统决定的安全时间中. 它们可能被调度运行多次, 但是 tasklet 调度不累积; ; tasklet 只运行一次, 即便它在被投放前被重复请求. 没有 tasklet 会和它自己并行运行, 因为它只运行一次, 但是 tasklet 可以与 SMP 系统上的其他 tasklet 并行运行. 因此, 如果你的驱动有多个 tasklet, 它们必须采取某类加锁来避免彼此冲突.

    tasklet 也保证作为函数运行在第一个调度它们的同一个 CPU 上. 因此, 一个中断处理可以确保一个 tasklet 在处理者结束前不会开始执行. 但是, 另一个中断当然可能在tasklet 在运行时被递交, 因此, tasklet 和中断处理之间加锁可能仍然需要.

    tasklet 必须使用 DECLARE_TASKLET 宏来声明: DECLARE_TASKLET(name, function, data);
    name 是给 tasklet 的名子, function 是调用来执行 tasklet (它带一个 unsigned long 参数并且返回 void )的函数, 以及 data 是一个 unsigned long 值来传递给tasklet 函数.

    short 驱动声明它的 tasklet 如下:

    void short_do_tasklet(unsigned long); DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0);

    函数 tasklet_schedule 用来调度一个 tasklet 运行. 如果 short 使用 tasklet=1 来加载, 它安装一个不同的中断处理来保存数据并且调度 tasklet 如下:

    irqreturn_t short_tl_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
    do_gettimeofday((struct timeval ) tv_head); / c

    展开全文
  • #NAME tasklet - 用于协作任务调度的 Java 库 #描述 ##Tasklet Tasklet 是与 tasklet 调度程序合作来决定如何(同步或异步)以及何时执行它们的小型工作单元。 Tasklet 实现了一个task()方法,调度程序调用该方法...
  • Tasklet机制

    2018-05-14 16:57:52
    一、tasklet使用Tasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联例子:Void my_tasklet_func(unsigned long)DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)代码DECLARE_TASKLET实现了定义...

    一、tasklet使用

    Tasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联

    例子:

    Void my_tasklet_func(unsigned long)

    DECLARE_TASKLET(my_tasklet,my_tasklet_func,data) //原型在 二中有描述

    代码DECLARE_TASKLET实现了定义名称name为my_tasklet的tasklet并将其与my_tasklet_func这个函数绑定,而传入这个函数的参数为data。

    需要调度tasklet的时候引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度,如下所示:

    Tasklet_schedule(&my_tasklet)

    下面给出驱动模板

     

    [cpp]  view plain  copy
    1. void xxx_do_tasklet(unsigned long);  
    2.   
    3. DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);  
    4.   
    5. void xxx_do_tasklet(unsigned long)  
    6.   
    7. {  
    8.   
    9. ……  
    10.   
    11. }  
    12.   
    13. irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)  
    14.   
    15. {  
    16.   
    17.       ……  
    18.   
    19.       tasklet_schedule(&xxx_tasklet);  
    20.   
    21.       ……  
    22.   
    23. }  
    24.   
    25. int _init xxx_init(void)  
    26.   
    27. {  
    28.   
    29.       ……  
    30.   
    31.       result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,”xxx”,NULL)  
    32.   
    33.       ……  
    34.   
    35. }  
    36.   
    37. void _exit xxx_exit(void)  
    38.   
    39. {  
    40.   
    41.       ……  
    42.   
    43.       free_irq(xxx_irq,xxx_irq_interrupt);  
    44.   
    45.       ……  
    46.   
    47. }  


    二、tasklet函数详解

    它对于中断处理特别有用:硬件中断必须尽快处理, 但大部分的数据管理可以延后到以后安全的时间执行。

    tasklet 以一个数据结构形式存在,使用前必须被初始化。初始化能够通过调用一个特定函数或者通过使用某些宏定义声明结构:



     

    [cpp]  view plain  copy
    1. #include <linux/interrupt.h>   
    2. struct tasklet_struct  
    3. {  
    4.     struct tasklet_struct *next;  
    5.     unsigned long state;  
    6.     atomic_t count;  
    7.     void (*func)(unsigned long);  
    8.     unsigned long data;  
    9. };  
    10. void tasklet_init(struct tasklet_struct *t,  
    11.  void (*func)(unsigned long), unsigned long data);  
    12.   
    13. #define DECLARE_TASKLET(name, func, data) \  
    14. struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }  
    15. #define DECLARE_TASKLET_DISABLED(name, func, data) \  
    16. struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }  
    17.   
    18. void tasklet_disable(struct tasklet_struct *t);   
    19. /*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;若这个 tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出*/  
    20. void tasklet_disable_nosync(struct tasklet_struct *t);   
    21. /*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU */  
    22. void tasklet_enable(struct tasklet_struct *t);   
    23. /*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。 tasklet_enable 和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"*/   
    24. void tasklet_schedule(struct tasklet_struct *t);   
    25. /*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己*/  
    26. void tasklet_hi_schedule(struct tasklet_struct *t);   
    27. /*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet 在其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期*/  
    28. void tasklet_kill(struct tasklet_struct *t);   
    29. /*确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行, 这个函数等待直到它执行完毕。若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill 前它重新调度它自己,如同使用 del_timer_sync*/  
    展开全文
  • tasklet使用

    2017-11-20 17:48:11
    taskletTasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联例子:Void my_tasklet_func(unsigned long)DECLARE_TASKLET(my_tasklet.my_tasklet_func,data)代码DECLARE_TASKLET实现了定义名称为my_...
  • tasklet.pdf

    2013-01-24 09:25:13
    tasklet.pdf
  • tasklet继续

    2017-01-28 17:30:59
    1.28学习记录 调度自己的taasklet 可以使用task_disable()来禁止某个指定的tasklet,若正在执行,那么就会等待其执行完毕再结束。...使用tasklet_kill()函数从挂起列队中去掉一个tasklet
  • tasklet使用方法

    2019-09-05 22:55:39
    tasklet的概念我这里就不多讲了,因为网上的文章很多,我这里只介绍怎么在我们的驱动程序中使用tasklet提供的接口函数。一般,从定义到最后注销,一共有4步,下面我们分别介绍这4个接口函数。 静态定义tasklet ...
  • tasklet机制

    2013-05-08 17:05:11
    一、tasklet使用 Tasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联 例子: Void my_tasklet_func(unsigned long) DECLARE_TASKLET(my_tasklet.my_tasklet_func,data) 代码DECLARE_TASKLET...
  • tasklet原理

    千次阅读 2016-04-28 16:24:09
    tasklet是Linux内核中“可延迟执行”机制、或“中断下半部”的一种。基于软中断实现,但比软中断灵活,tasklet有的地方翻译作“任务蕾”,大部分书籍没找到合适的词汇去翻译它。本篇博客主要介绍tasklet的设计原理、...
  • Linux Tasklet

    2017-01-28 02:48:16
    (网络和SCSI)内核定时器和tasklet也是建立在软中断上的编译期间在定义一个枚举类型来静态的申明一个软中断注册你的处理程序 open_softirq();//注册中断处理函数例如: open_softirq(NET_TX_SOFTIRQ,n

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,461
精华内容 3,784
关键字:

Tasklet