Linux设备驱动开发-12中断处理方式之tasklet与工作队列_第1页
Linux设备驱动开发-12中断处理方式之tasklet与工作队列_第2页
Linux设备驱动开发-12中断处理方式之tasklet与工作队列_第3页
Linux设备驱动开发-12中断处理方式之tasklet与工作队列_第4页
Linux设备驱动开发-12中断处理方式之tasklet与工作队列_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Tasklets一、tasklet使用Tasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联例子:Voidmy_tasklet_func(unsignedlong)DECLARE_TASKLET(my_tasklet.my_tasklet_func,data)代码DECLARE_TASKLET实现了定义名称为my_tasklet的tasklet并将其与my_tasklet_func这个函数绑定,而传入这个函数的参数为data。需要调度tasklet的时候引用一个tasklet_schedule。函数就能使系统在适当的时候进行调度,如下所示Tasklet_schedule(&my_tasklet)下面给出驱动模板Voidxxx_do_tasklet(unsignedlong);DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);Voidxxx_do_tasklet(unsignedlong){}Irqreturn_txxx_interrupt(intirq,void*dev_id,structpt_regs*regs){Tasklet_schedule(&xxx_tasklet);}Int_initxxx_init(void){Result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT'xxx”,NULL)}Void_exitxxx_exit(void){Free_irq(xxx_irq,xxx_irq_interrupt);}二、tasklet函数详解它对于中断处理特别有用:硬件中断必须尽快处理,但大部分的数据管理可以延后到以后安全的时间执行。tasklet以一个数据结构形式存在,使用前必须被初始化。初始化能够通过调用一个特定函数或者通过使用某些宏定义声明结构:#include<linux/interrupt.h>structtasklet_struct{structtasklet_struct*next;unsignedlongstate;atomic_tcount;void(*func)(unsignedlong);unsignedlongdata;};voidtasklet_init(structtasklet_struct*t,void(*func)(unsignedlong),unsignedlongdata);#defineDECLARE_TASKLET(name,func,data)\structtasklet_structname={NULL,0,ATOMIC_INIT(0),func,data}#defineDECLARE_TASKLET_DISABLED(name,func,data)\structtasklet_structname={NULL,0,ATOMIC_INIT(1),func,data}voidtasklet_disable(structtasklet_struct*t);/*函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行,这个函数忙等待直到这个tasklet退出*/voidtasklet_disable_nosync(structtasklet_struct*t);/*和tasklet_disable类似,但是tasklet可能仍然运行在另一个CPU*/voidtasklet_enable(structtasklet_struct*t);/*使能一个之前被disable的tasklet;若这个tasklet已经被调度,它会很快运行。tasklet_enable和tasklet_disable必须匹配调用,因为内核跟踪每个tasklet的"禁止次数"*/voidtasklet_schedule(structtasklet_struct*t);/*调度tasklet执行,如果tasklet在运行中被调度,它在完成后会再次运行;这保证了在其他事件被处理当中发生的事件受到应有的注意 .这个做法也允许一个tasklet重新调度它自己*/voidtasklet_hi_schedule(structtasklet_struct*t);/*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时,它处理高优先级tasklet在其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数,可避免其他软件中断处理引入的附加周期*/voidtasklet_kill(structtasklet_struct*t);/*确保了tasklet不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果tasklet正在运行,这个函数等待直到它执行完毕。若tasklet重新调度它自己,则必须阻止在调用tasklet_kill前它重新调度它自己,如同使用del_timer_sync*/工作队列工作队列的使用定义一个工作队列Structwork_structmy_wq;Voidmy_wq_func(unsignedlong);通过INIT_WORK可以初始化这个工作队列并将工作队列与处理函数绑定INIT_WORK(&my_wq,(void(*)(void*))my_wq_func,NULL)调度工作队列Schedule_work(&my_wq)使用模板Structwork_structxxx_wq;Voidxxx_do_work(unsignedlong);Voidxxx_do_work(unsignedlong){}Irqreturn_txxx_interrupt(intirq,void*dev_id,structpt_regs*regs){Schedule_work(&xxx_wq);}Intinit(void){Result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT“xxx”,NULL)INIT_WORK(&xxx_wq,(void(*)(void*))xxx_do_work,NULL);}Voidxxx_exit(void){Free_irq(xxx_irq,xxx_interrupt);二、工作队列函数详解工作队列有structworkqueue_struct类型,在<linux/workqueue.h>中定义。一个工作队列必须明确的在使用前创建,宏为:structworkqueue_struct*create_workqueue(constchar*name);structworkqueue_struct*create_singlethread_workqueue(constchar*name);每个工作队列有一个或多个专用的进程("内核线程"),这些进程运行提交给这个队列的函数。若使用create_workqueue,就得到一个工作队列它在系统的每个处理器上有一个专用的线程。在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用create_singlethread_workqueue来创建工作队列U。提交一个任务给一个工作队列,在这里《LDD3》介绍的内核2.6.10和我用的新内核2.6.22.2已经有不同了,老接口已经不能用了,编译会出错。这里我只讲2.6.22.2的新接口,至于老的接口我想今后内核不会再有了。从这一点我们可以看出内核发展。/*需要填充work_struct或delayed_work结构,可以在编译时完成,宏如下:*/structwork_struct(atomic_long_tdata;#defineWORK_STRUCT_PENDING0 /*Tifworkitempendingexecution*/#defineWORK_STRUCT_FLAG_MASK(3UL)#define WORK_STRUCT_WQ_DATA_MASK(~WORK_STRUCT_FLAG_MASK)structlist_headentry;work_func_tfunc;};structdelayed_work{structwork_structwork;structtimer_listtimer;};DECLARE_WORK(n,f)/*n是声明的work_struct结构名称,f是要从工作队列被调用的函数*/DECLARE_DELAYED_WORK(n,f)/*n是声明的delayed_work结构名称,f是要从工作队列被调用的函数*//*若在运行时需要建立work_struct或delayed_work结构,使用下面2个宏定义:*/INIT_WORK(structwork_struct*work,void(*function)(void*));PREPARE_WORK(structwork_struct*work,void(*function)(void*));INIT_DELAYED_WORK(structdelayed_work*work,void(*function)(void*));PREPARE_DELAYED_WORK(structdelayed_work*work,void(*function)(void*));/*INIT_*做更加全面的初始化结构的工作,在第一次建立结构时使用.PREPARE」做几乎同样的工作,但是它不初始化用来连接work_struct或delayed_work结构到工作队列的指针。如果这个结构已经被提交给一个工作队列,且只需要修改该结构,则使用PREPARE_*而不是INIT_**//*有2个函数来提交工作给一个工作队列:*/intqueue_work(structworkqueue_struct*queue,structwork_struct*work);intqueue_delayed_work(structworkqueue_struct*queue,structdelayed_work*work,unsignedlongdelay);/*每个都添加work到给定的workqueue。如果使用queue_delay_work,贝U实际的工作至少要经过指定的jiffies才会被执行。这些函数若返回1则工作被成功加入到队列;若为0,则意味着这个work已经在队列中等待,不能再次加入*/在将来的某个时间,这个工作函数将被传入给定的data值来调用。这个函数将在工作线程的上下文运行,因此它可以睡眠(你应当知道这个睡眠可能影响提交给同一个工作队列的其他任务)工作函数不能访问用户空间,因为它在一个内核线程中运行,完全没有对应的用户空间来访问。取消一个挂起的工作队列入口项可以调用:intcancel_delayed_work(structdelayed_work*work);voidcancel_work_sync(structwork_struct*work)如果这个入口在它开始执行前被取消,则返回非零。内核保证给定入口的执行不会在调用cancel_delay_work后被初始化.如果cancel_delay_work返回0,但是,这个入口可能已经运行在一个不同的处理器,并且可能仍然在调用cancel_delayed_work后在运行.要绝对确保工作函数没有在cancel_delayed_work返回0后在任何地方运行,你必须跟随这个调用来调用:voidflush_workqueue(structworkqueue_struct*queue);在flush_workqueue返回后,没有在这个调用前提交的函数在系统中任何地方运行。而cancel_work_sync会取消相应的work,但是如果这个work已经在运行那么cancel_work_sync会阻塞,直到work完成并取消相应的work。当用完一个工作队列,可以去掉它,使用:voiddestroy_workqueue(structworkqueue_struc

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论