tasklet和work_struct机制

一、tasklet使用

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

例子:

Void my_tasklet_func(unsigned long)

DECLARE_TASKLET(my_tasklet.my_tasklet_func,data)

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

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

Tasklet_schedule(&my_tasklet)

下面给出驱动模板

    
    
void xxx_do_tasklet(unsigned long ); DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0 ); void xxx_do_tasklet(unsigned long ) { } irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs * regs) { tasklet_schedule(&xxx_tasklet);
} int _init xxx_init(void ) { result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,”xxx”,NULL)
} void _exit xxx_exit(void ) { free_irq(xxx_irq,xxx_irq_interrupt); }

二、tasklet函数详解

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

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

#include<linux/interrupt.h>
struct tasklet_struct
{
struct tasklet_struct*
next;
unsigned
long
state;
atomic_t count;
void (*func)(unsignedlong
);
unsigned
long
data;
};
void tasklet_init(struct tasklet_struct*
t,void (*func)(unsignedlong), unsignedlong 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 }

void tasklet_disable(struct tasklet_struct*
t);
/*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;
若这个 tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出*/
void tasklet_disable_nosync(struct tasklet_struct* t);
/*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU*/

void tasklet_enable(struct tasklet_struct* t);
/*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。 tasklet_enable
和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"*/
void tasklet_schedule(struct tasklet_struct* t);
/*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理
当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己*/
void tasklet_hi_schedule(struct tasklet_struct* t);
/*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet 在
其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期*/
void tasklet_kill(struct tasklet_struct* t);
/*确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet
正在运行, 这个函数等待直到它执行完毕。若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill
前它重新调度它自己,如同使用 del_timer_sync*/
 

一、       工作队列的使用

定义一个工作队列

Struct work_struct my_wq;

Void my_wq_func(unsigned long);

通过INIT_WORK可以初始化这个工作队列并将工作队列与处理函数绑定

INIT_WORK(&my_wq,(void(*)(void*))my_wq_func,NULL)

调度工作队列

Schedule_work(&my_wq)

使用模板

struct work_struct xxx_wq;

void xxx_do_work(unsigned long);

void xxx_do_work(unsigned long)

{

}

irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)

{
schedule_work(&xxx_wq);

}

int init(void)

{

      result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,“xxx”,NULL)

      INIT_WORK(&xxx_wq,(void(*)(void*))xxx_do_work,NULL);

}

void xxx_exit(void)

{

      free_irq(xxx_irq,xxx_interrupt);

}

 

二、       工作队列函数详解

工作队列有 struct workqueue_struct 类型,在 <linux/workqueue.h> 中定义。一个工作队列必须明确的在使用前创建,宏为:

struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);

每个工作队列有一个或多个专用进程("内核线程"), 这些进程运行提交给这个队列的函数。 若使用 create_workqueue, 就得到一个工作队列它在系统的每个处理器上有一个专用的线程。在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用 create_singlethread_workqueue 来创建工作队列。

提交一个任务给一个工作队列,在这里《LDD3》介绍的内核2.6.10和我用的新内核2.6.22.2已经有不同了,老接口已经不能用了,编译会出错。这里我只讲2.6.22.2的新接口,至于老的接口我想今后内核不会再有了。从这一点我们可以看出内核发展。

复制代码
/*需要填充work_struct或delayed_work结构,可以在编译时完成, 宏如下: */

struct work_struct {
    atomic_long_t data;
#define WORK_STRUCT_PENDING 0        /* T if work item pending execution */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
    struct list_head entry;
    work_func_t func;
};

struct delayed_work {
    struct work_struct work;
    struct timer_list timer;
};

三、与tasklet的联系
工作队列类似 tasklets,允许内核代码请求在将来某个时间调用一个函数,不同在于:
(1)tasklet 在软件中断上下文中运行,所以 tasklet 代码必须是原子的。而工作队列函数在一个特殊内核进程上下文运行,有更多的灵活性,且能够休眠。
(2)tasklet 只能在最初被提交的处理器上运行,这只是工作队列默认工作方式。
(3)内核代码可以请求工作队列函数被延后一个给定的时间间隔。
(4)tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能是长周期且不需要是原子的,两个机制有它适合的情形。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值