作者:张伟AreS
/******************************************************************************/
学习此之前先需学习《学习ldd3--proc文件系统(第七章与第四章)》
实例:代码:ldd3的jit.c (D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\misc-modules\jit.c)
Tasklet:
tasklet 以一个struct tasklet_struct数据结构形式存在,使用前必须被初始化。:
#include
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
初始化能够通过调用一个特定函数或者通过使用某些宏定义声明结构
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);
#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 }
/*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;
*若这个 tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出
*/
void tasklet_disable(struct tasklet_struct *t);
/*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU */
void tasklet_disable_nosync(struct tasklet_struct *t);
/*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。
*tasklet_enable 和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"
*/
void tasklet_enable(struct tasklet_struct *t);
/*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行;
*这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
*/
void tasklet_schedule(struct tasklet_struct *t);
/*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet 在其他软中断之前,
*只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期
*/
void tasklet_hi_schedule(struct tasklet_struct *t);
/* 确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行, 这个函数等待直到它执行完毕。
*若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill 前它重新调度它自己,如同使用 del_timer_sync
*/
void tasklet_kill(struct tasklet_struct *t);
tasklet 的特点:
(1)一个 tasklet 能够被禁止并且之后被重新使能; 它不会执行,直到它被使能与被禁止相同的的次数;
(2)如同定时器, 一个 tasklet 可以注册它自己;
(3)一个 tasklet 能被调度来执行以正常的优先级或者高优先级;
(4) 如果系统不在重负载下,taslet 可能立刻运行, 但是从不会晚于下一个时钟嘀哒;
(5)一个 tasklet 可能和其他 tasklet 并发, 但是它自己是严格地串行的 ,且tasklet 从不同时运行在不同处理器上,通常在调度它的同一个 CPU 上运行。
/* the /proc function: allocate everything to allow concurrency */
int jit_tasklet(char *buf, char **start, off_t offset,
int len, int *eof, void *arg)
{
struct jit_data *data;
char *buf2 = buf;
unsigned long j = jiffies;
long hi = (long)arg;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
init_waitqueue_head (&data->wait);
/* write the first lines in the buffer */
buf2 += sprintf(buf2, " time delta inirq pid cpu command\n");
buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n",
j, 0L, in_interrupt() ? 1 : 0,
current->pid, smp_processor_id(), current->comm);
/* fill the data for our tasklet function */
data->prevjiffies = j;
data->buf = buf2;
data->loops = JIT_ASYNC_LOOPS;
/* register the tasklet */
/*调用tasklet_init初始化data->tlet,指定tasklet函数为jit_tasklet_fn,交将data作为参数传递给jit_tasklet_fn*/
tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);
data->hi = hi;
/*如果hi为1,则使用tasklet_hi_schedule函数以高优先级调度data->tlet执行。
*当软件中断处理程序运行时,它会在处理其他软件中断(包括普通的tasklet)之前,处理高优先级的tasklet
*如果hi为0,则使用tasklet_schedule调度data->tlet执行
*/
if (hi)
tasklet_hi_schedule(&data->tlet);
else
tasklet_schedule(&data->tlet);
/* wait for the buffer to fill */
wait_event_interruptible(data->wait, !data->loops);
if (signal_pending(current))
return -ERESTARTSYS;
buf2 = data->buf;
kfree(data);
*eof = 1;
return buf2 - buf;
}
tasklet函数jit_tasklet_fn:
//该函数的功能与前面介绍的定时器函数jit_timer_fn非常像,只不过把调度定时器的操作换成了调度tasklet的函数。
void jit_tasklet_fn(unsigned long arg)
{
struct jit_data *data = (struct jit_data *)arg;
unsigned long j = jiffies;
data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
current->pid, smp_processor_id(), current->comm);
if (--data->loops) {
data->prevjiffies = j;
if (data->hi)
tasklet_hi_schedule(&data->tlet);
else
tasklet_schedule(&data->tlet);
} else {
wake_up_interruptible(&data->wait);
}
}