分类什么是工作队列驱动程序编译测试别人的经验,我们的阶梯!Linux中断处理可以总结为下图:图中描述了中断处理后半部分的机制,以及如何根据实际业务场景和约束进行选择。可以看出,这些不同的实现有的是重复的,有的是相互替代的。也正是因为如此,它们之间的使用方法几乎是一样的,至少在API接口函数的使用上,从使用的角度来说,都是非常相似的。在本文中,我们将通过实际的代码操作来演示如何使用workqueue。什么是工作队列?工作队列是Linux操作系统中处理中断后半部分的重要途径!从名字就可以猜到:工作队列就像是业务层常用的消息队列,里面有很多工作项等待处理。工作队列中有两个重要的结构体:工作队列(workqueue_struct)和工作项(work_struct):/*I:workqueuename*/.../*hotfieldsusedduringcommandissue,alignedtocacheline*/unsignedintflags____cacheline_aligned;/*WQ:WQ_*flags*/structpool_workqueue__percpu*cpu_pwqs;/*I:per-cpupwqs*/structpool_workqueue__rcu*numa_pwqs];/*q_tbl[PWR:unboundpwqsindexedbynode*/};structwork_struct{atomic_long_tdata;structlist_headentry;work_func_tfunc;//指向处理函数#ifdefCONFIG_LOCKDEPstructlockdep_maplockdep_map;#endif};在内核中,工作队列中的所有工作项通过链表串在一起,等待操作系统中的某个线程被一个一个取出来处理。这些线程可以是驱动程序通过kthread_create创建的线程,也可以是操作系统预先创建的线程。这里涉及一个权衡问题。如果我们的处理功能简单,那么就没有必要单独创建一个线程来进行处理。有两个原因:创建内核线程非常耗费资源。如果功能很简单,执行完不久就关闭线程是不值得的;如果每个驱动程序编写者都无节制地创建内核线程,那么内核中就会出现大量不必要的线程,当然这本质上是系统资源消耗和执行效率的问题;为了避免这种情况,操作系统预先为我们创建了一些工作队列和内核线程。我们只需要将需要处理的工作项直接添加到这些预先创建好的工作队列中,它们就会被相应的内核线程取出来进行处理。例如下面这些工作队列,就是内部默认创建的(include/linux/workqueue.h):/**System-wideworkqueueswhicharealwayspresent.**system_wqistheoneusedbyschedule[_delayed]_work[_on]().*Multi-CPUmulti-threaded.Thereareuserswhichexpectrelatively*shortqueueflushtime.Don'tqueueworkswhichcanrunfortoo*long.**system_highpri_wqissimilartosystem_wqbutforworkitemswhich*requireWQ_HIGHPRI.**system_long_wqissimilartosystem_wqbutmayhostlongrunning*works.Queueflushingmighttakerelativelylong.**system_unbound_wqisunboundworkqueue.Workersarenotboundto*anyspecificCPU,notconcurrencymanaged,andallqueuedworksare*executedimmediatelyaslongasmax_activelimitisnotreachedand*resourcesareavailable.**system_freezable_wqisequivalenttosystem_wqexceptthatit's*freezable.***_power_efficient_wqareinclinedtowardssavingpowerandconverted*intoWQ_UNBOUNDvariantsif'wq_power_efficient'已启用;否则,*它们是相同的非节能对应部件-例如*system_power_efficient_wqisidenticaltosystem_wqif*'wq_power_efficient'isdisabled.SeeWQ_POWER_EFFICIENTformoreinfo.*/externstructworkqueue_struct*system_wq;externstructworkqueue_struct*system_highpri_wq;externstructworkqueue_struct*system_long_wq;externstructworkqueue_struct*system_unbound_wq;externstructworkqueue_struct*system_freezable_wq;externstructworkqueue_struct*system_power_efficient_wq;externstructworkqueue_struct*system_freezable_power_efficient_wq;以上这些默认工作队列的创建代码是(kernel/workqueue.c):int__initworkqueue_init_early(void){...system_wq=alloc_workqueue("events",0,0);system_highpri_wq=alloc_workqueue("events_highpri",WQ_HIGHPRI,0);system_long_wq=alloc_workqueue(“events_long”,0,0);system_unbound_wq=alloc_workqueue(“events_unbound”,WQ_UNBOUND,WQ_UNBOUND_MAX_ACTIVE);system_freezable_wq=alloc_workqueue(“events_freezable”,WQ_FREEZABLE,0);system_power_efficient_wq=alloc_workqueue(“events_FIQENT,0POWER_efficient”);system_freezable_power_eefficient_wq=alloc_workqueue("events_freezable_power_efficient",WQ_FREEZABLE|WQ_POWER_EFFICIENT,0);...}另外,由于工作队列system_wq的使用频率很高,所以内核封装了一个简单的函数(schedule_work)供我们使用:/***schedule_work-putworktaskinglobalworkqueue*@work:jobtobedone**Returns%falseif@workwasalreadyonthekernel-globalworkqueueand*%trueotherwise.**Thisputsajobinthekernel-globalworkqueueifitwasnotalready*queuedandleavesitinthesamepositiononthekernel-global*workqueueotherwise.*/staticinlineboolschedule_work(structwork_struct*work){ returnqueue_work(system_wq,work);}当然,任何事情都有优点和缺点!由于内核默认创建的工作队列是所有驱动共享的,如果所有驱动都将等待处理的工作项委托给它们,会造成某个工作队列过度拥挤。根据先来先服务的原则,工作队列中后面加入的工作项可能无法保证及时性,因为前面工作项的处理函数执行时间太长。因此,这里存在系统平衡的问题。关于工作队列的基本知识点就介绍到这里,下面会进行实际操作验证。在驱动的前几篇文章中,在驱动中测试中断处理的操作过程都是一样的,这里不再重复操作过程。这里直接给出驱动的全图代码,然后查看dmesg的输出信息。创建驱动源文件和Makefile:$cdtmp/linux-4.15/drivers$mkdirmy_driver_interrupt_wq$touchmy_driver_interrupt_wq.c$touchMakefile示例代码整体测试场景:加载驱动模块后,如果检测到键盘上的ESC键被按下,则添加一个工作项到内核默认的工作队列system_wq,然后观察是否调用了该工作项对应的处理函数。#include
