【嵌入式Linux驱动程序-基础篇】- tasklets机制

2019-07-13 02:37发布

Tasklets机制

tasklet在某些方面类似于内核定时器,它们一直在中断时间运行,它们一直运行在调度它们的同一个CPU上,并且它们接收到一个unsigned long参数。不像内核定时器,我们无法请求在一个指定的时间执行函数,需要通过调度tasklet,当我们简单的调用调度后,内核将在一个适当的时间去执行。这个行为对于中断处理特别有用,即“上下半部(前后半部)”。实际上,一个tasklet就像一个内核定时器,在一个“软中断”的上下文中执行(以原子模式),在使能硬件中断时执行异步任务的一个内核机制。

1 tasklet结构声明

一个tasklet用tasklet_struct结构进行表示,我们在使用前需要初始化该结构,初始化能够通过调用一个特定的函数或者通过某些宏定义声明结构。 #include // 动态初始化tasklet_struct结构(前提已经通过分配或其他方式获得的tasklet_struct结构) void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); // 静态初始化tasklet_struct结构 DECLARE_TASKLET(name, func, data); DECLARE_TASKLET_DISABLED(name, func, data); tasklet提供了许多特性:
  • 一个tasklet能够被禁止并且之后被重新使能;它不会执行直到被它被使能与被禁止相同的次数。
  • 如同定时器,一个tasklet可以注册它自己。
  • 可以一个tasklet被调度执行以正常的优先级或者高优先级。后一组一直是首先执行。
  • tasklet可能立即运行,如果系统不再重载下,但是从不会晚于下一个时钟滴答。
  • 一个tasklet可能和其他tasklet并发,但是对它自己是严格地串行的,即同样的tasklet从不同时运行在超过一个处理器上。同样,一个tasklet常常在调度它的同一个CPU上运行。

2 tasklet禁止接口

void tasklet_disable(struct tasklet_struct *t); 这个函数禁止给定的tasklet。tasklet可能仍然被tasklet_schedule调度,但是它的执行被延后直到这个tasklet被再次使能。如果这个tasklet当前在运行,这个函数忙等待直到这个tasklet退出;因此,在调用disklet_diskable后,你可以确保这个tasklet在系统任何地方都不在运行(即在SMP上不存在tasklet运行)。 void tasklet_disable_nosync(struct tasklet_struct *t); 禁止这个tasklet,但是没有等待任何当前的函数退出,当它返回,这个tasklet被禁止并且不回在以后被调度知道重新使能,但是它可能仍然运行在另一个CPU当这个函数返回时。 void tasklet_enable(struct tasklet_struct *t) 使能一个之前被禁止的tasklet,如果这个tasklet已经被调度,它会很快运行,一个对tasklet_enable的调度必须匹配每个对tasklet_disable的调用,因为内核跟踪每个tasklet的“禁止次数”。

 

3 tasklet使能接口

void tasklet_schedule(struct tasklet_struct *t); 调度tasklet执行,如果一个tasklet被再次调度在它有机会运行前,它只运行一次。但是,如果它在运行中被调度,它在完成后再次运行;这个保证了其他事件被处理当中发生的事件收到应有的注意。这个做法允许一个tasklet重新调度它自己。 注:只要tasklet没运行前,多次调度,其只运行一次。然而在运行中,若被调度,则其完成后将会再执行多一次。 void tasklet_hi_schedule(struct tasklet_struct *t); 调度tasklet在更高优先级执行,当软中断处理运行时,它处理高优先级tasklet在其他软中断之前,包括“正常的”tasklet。理想地,只有具有地响应周期要求(例如填充音频缓冲)应当使用这个函数,为避免其它软件中断处理映入的附加周期。 void tasklet_kill(struct tasklet_struct *t); 这个函数确保了这个tasklet没有再次调度来运行;它常常被调用当做一个设备整备关闭或者模块卸载时,如果这个tasklet被调度来运行,这个函数等待直到它已执行。如果这个tasklet重新调度它自己,你必须阻止在调度tasklet_kill前它重新调度自己。