The following mechanisms are available in the kernel to defer work to a bottom half: softirqs, tasklets, and work queues .
Softirqs are the basic bottom half mechanism and have strong locking requirements. They are used only by a few performance-sensitive subsystems such as the networking layer, SCSI layer, and kernel timers.
Tasklets are built on top of softirqs and are easier to use. It's recommended to use tasklets unless you have crucial scalability or speed requirements.
A primary difference between a softirq and a tasklet is that the former is reentrant whereas the latter isn't. Different instances of a softirq can run simultaneously on different processors, but that is not the case with tasklets.
Work queues are a third way to defer work from interrupt handlers. They execute in process context and are allowed to sleep , so they can use drowsy functions such as mutexes.
驱动程序使用tasklet机制
驱动程序在初始化时,通过函数task_init建立一个tasklet,然后调用函数tasklet_schedule将这个tasklet 放在 tasklet_vec链表的头部,并唤醒后台线程ksoftirqd。当后台线程ksoftirqd运行调用__do_softirq时,会执行在中断向量表softirq_vec里中断号TASKLET_SOFTIRQ对应的tasklet_action函数,然后tasklet_action遍历 tasklet_vec链表,调用每个tasklet的函数完成软中断操作。
下面对函数tasklet_init和tasklet_schedule分析:
函数tasklet_init初始化一个tasklet,其参数t是tasklet_struct结构描述的tasklet,参数(*func)是软中断响应函数。
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_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_schedule得到当前CPU的tasklet_vec链表,并执行TASKLET_SOFTIRQ软中断。
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
函数raise_softirq_irqoff设置软中断nr为挂起状态,并在没有中断时唤醒线程ksoftirqd。函数raise_softirq_irqoff必须在关中断情况下运行。
inline fastcall void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
}
下面是tasklet_struct和softirq_action的定义。
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
摘录于《Linux内核分析及编程>
http://hi.baidu.com/ryderlee/blog/item/ceeec316e8d1f318962b431a.html
1.Tasklet 可被hi-schedule和一般schedule,hi-schedule一定比一般shedule早运行;
2.同一个Tasklet可同时被hi-schedule和一般schedule;
3.同一个Tasklet若被同时hi-schedule多次,等同于只hi-shedule一次,因为,在tasklet未 运行时,hi-shedule同一tasklet无意义,会冲掉前一个tasklet;
4.对于一般shedule, 同上。
5.不同的tasklet不按先后shedule顺序运行,而是并行运行。
6.Tasklet的运行时间:
a.若在中断中schedule tasklet, 中断结束后立即运行;
b.若CPU忙,在不在此次中断后立即运行;
c.不在中断中shedule tasklet;
d.有软或硬中断在运行;
e.从系统调用中返回;(仅当process闲时)
f.从异常中返回;
g.调试程序调度。(ksoftirqd运行时,此时CPU闲)
7.Taskelet的hi-schedule 使用softirq 0, 一般schedule用softirq 30;
8.Tasklet的运行时间最完在下一次time tick 时。(因为最外层中断一定会运行使能的softirq, 面不在中断中便能或shedule的softirq在下一定中断后一定会被调用。)
综上: Tasklet 能保证的运行时间是(1000/HZ)ms,一般是10ms。Tasklet在CPU闲或中断后被调用
5 tasklet 的简单用法
下面是 tasklet 的一个简单应用 , 以模块的形成加载。
# include < linux / module . h >
# include < linux / init . h >
# include < linux / fs . h >
# include < linux / kdev_t . h >
# include < linux / cdev . h >
# include < linux / kernel . h >
# include < linux / interrupt . h >
static struct tasklet_struct my_tasklet ;
static void tasklet_handler ( unsigned long data )
{
printk ( KERN_ALERT "tasklet_handler is running./n" );
}
static int __init test_init ( void )
{
tasklet_init (&my_ tasklet , tasklet_handler , 0 );
tasklet_schedule (&my_ tasklet );
return 0 ;
}
static void __exit test_exit ( void )
{
tasklet_kill (&my_ tasklet );
printk ( KERN_ALERT "test_exit running./n" );
}
MODULE_LICENSE ( "GPL" );
module_init( test_init );
module_exit ( test_exit );
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/RichardYSteven/archive/2009/08/24/4479236.aspx