期刊库

教育   经济   科技   财会   管理   
医学   法学   文史   工业   建筑   
农学   水利   计算机   更多>>
 首 页    论文大全   论文精品    学术答疑    论文检测    出书咨询    服务流程    诚信通道    关于我们 

Linux内核新旧工作队列机制的剖析和比较

人气指数: 发布时间:2014-04-21 21:27  来源:http://www.zgqkk.com  作者: 马俊强
分享到:

 

  摘要:在中断驱动的程序设计中,工作队列是一种强有力的工具。但是在Linux2.6.35及其以前的内核版本中,每创建一个工作队列就创建与CPU数目相同的内核线程,耗费大量的内核资源;工作只能严格串行的处理,效率低。为了适应大规模多处理器硬件平台,提高处理效率,Linux2.6.36内核开发了受控并发工作队列机制。这种新机制由内核根据需要创建或销毁线程,工作可以并发的处理,可望替代之前长期使用的专用线程工具。文章详细介绍和剖析新工作队列机制,并通过实验,对比新旧工作队列机制的资源消耗和工作效率。结果表明,新工作队列机制大大减少内核资源的耗费,提高了处理效率。

  关键词: Linux内核; 中断驱动; 工作队列; 受控并发工作队列

  当一个中断发生时,并不是相关的各个操作都具有相同的急迫性,把所有的操作都放进中断处理程序本身也不合适。Linux内核将整个中断处理流程简单分为两个部分,第一部分是中断处理程序,称为上半部(Top Half),在运行时禁止可屏蔽中断,用于完成关键性的、紧急的处理;其余部分称为下半部(Bottom Half),在运行时开中断,主要完成与中断处理密切相关的工作,即延后执行工作(Deferring Work)。Linux内核提供三种不同形式的下半部实现机制:软中断(Softirq)、Tasklet和工作队列(Work Queue)。其中软中断和Tasklet运行在中断上下文中,不可阻塞。而工作队列由特殊的内核线程—工作者线程(Worker Thread)来执行,运行在进程上下文,可以阻塞。但是由于旧工作队列机制的缺陷,其应用并不普遍,取而代之的是使用专用线程池,如flush-x:y,bdi-default等。新工作队列机制为内核提供一种通用的线程池机制,以替代这些专用线程池[1]。实际上,只要涉及中断驱动的程序设计,工作队列都是一种强有力的工具,比如在实时控制系统中,可以创建工作队列方便高效地响应和执行由外部事件驱动的任务。文章将重点介绍和分析新工作队列机制。对新旧工作队列机制的介绍分析,选择的内核版本分别是2.6.36和2.6.35。

  工作队列的设计思想可以类比于现实中的生产流水线 [2]:流水线相当于工作队列中的worklist链表,加工部件相当于中断发生时所产生的工作序列,工人就是工作者线程。当中断发生时,内核将本次中断延后执行的工作序列,放到worklist工作链表中,唤醒工作者线程执行工作。工作者线程在执行时可能阻塞;当worklist中的工作处理完毕后,工作者线程进入空闲状态。从Linux 2.5.41内核引入工作队列直到2.6.35内核, 其运行机制没有大的改动,主要缺点是,在有N个CPU的计算机中,每当创建一个工作队列时,就创建N条流水线,为每条流水线创建一个工作者线程,内核可以向这N条流水线提交该种类的工作(由响应中断的CPU决定)。因此,如果创建X个工作队列,则需要创建N * X个工作者线程,但是只有当流水线上有工作时,线程才运行,其余流水线上的线程都处于空闲状态。大量的线程需要消耗线程的ID资源和大量内存,同时也会增加调度器的负担。这种情况在拥有大量CPU的超级计算机上显得尤为浪费。另一方面,一条流水线上只有一个工作者线程,因此同一流水线上工作的处理是严格串行的,严重制约处理的效率。

  为适应大规模多处理器硬件平台,提高工作队列的处理效率,从2.6.36内核开始对工作队列进行彻底的改造。在新的工作队列机制中,内核始终维持N + 1条“工作流水线”,即全局每CPU工作队列gcwq(Global Percpu Workqueue,详见1.1节)。新机制的流水线是通用的,所有来自同一个CPU的中断所产生的工作序列,都放在这条流水线上。每条流水线上的工作者线程“按需分配”,即当一个工作者线程阻塞时,可以让另一个工作者线程来处理该流水线上剩余的工作。当流水线上需要新的工作者线程时,就创建新线程;而当流水线上线程过多时,就销毁线程。同一条流水线上可以有多个工作同时被指派给多个工作者线程,当然任何时刻一条流水线上只有一个工作者线程在运行。这种新的工作队列机制称为受控并发工作队列(Concurrency Managed Workqueue)[3-5]。

  1 受控并发工作队列

  1.1 全局每CPU工作队列(gcwq)

  如果计算机有N个CPU,则内核创建N + 1个gcwq,其结构如下所示:

  struct global_cwq {

  spinlock_t lock;

  unsigned int cpu;

  struct list_head worklist;

  int nr_workers;

  int nr_idle;

  struct list_head idle_list;

  struct hlist_head busy_hash[BUSY_WORKER_HASH_SIZE];

  struct timer_list idle_timer;

  struct timer_list mayday_timer;

  ...

  } ____cacheline_aligned_in_smp;

  N个gcwq分别与N个CPU一一绑定,管理相关CPU上的工作者线程和中断产生的工作;第N + 1个gcwq称为unbound_global_cwq,其中的工作者线程未与特定的CPU绑定,详见1.4节WQ_UNBOUND标志说明。虽然gcwq也称作“工作队列”,但是与用户创建的工作队列不同,它是内部管理结构,对用户不可见。gcwq中的cpu字段表示与其关联的CPU编号,worklist双向链表存储由中断提交到该CPU上的工作,lock字段为保护gcwq结构体的自旋锁。每个gcwq都维护管理一个工作者线程池,其中的工作者线程有idle(空闲)和busy(工作)两种状态;idle_list双向链表中管理处于idle状态的工作者线程,nr_idle记录其数量;为了快速检索,使用busy_hash哈希链表管理处于busy状态的工作者线程。这些线程负责处理worklist链表中工作。nr_workers记录工作者线程池中线程的数量。

  1.2 新工作队列机制的运行

  当中断发生时,内核调用queue_work函数将工作序列提交到gcwq。若相关的线程池中没有线程,则内核创建工作者线程;否则唤醒一个工作者线程(异常情况处理见1.4节WQ_RESCUER属性)。工作者线程调用worker_thread函数,该函数在执行中使用gcwq中的自旋锁进行保护,并完成以下动作:

  1)线程从idle状态变为busy状态。

  2)对所属的gcwq的线程池进行检查管理。设线程A是当前正在执行的线程,若worklist上有多个待处理的工作,则A检查线程池中是否还有处于idle状态的线程,如果存在,设选中的为线程B;否则A将创建并唤醒一个新线程B,B在创建后进入idle状态。因此在处理工作时,线程池中将保持至少一个处于idle状态的线程待命,以便迅速响应和处理worklist上的后继工作。

  3)线程A从gcwq的worklist链表中依次取出未处理的工作进行处理。当处理完worklist中所有的工作后,将再一次对线程池进行检查管理,然后进入idle状态并休眠。

  4)一旦线程A阻塞且worklist上还有待处理的工作,则线程B开始运行,它调用worker_thread函数重复以上过程。

  如3)所述,当工作者线程处理完全部工作后将对线程池进行一次检查管理。此时如果gcwq中空闲的工作者线程过多,其判断条件是nr_idle > 2且(nr_idle - 2) * 4 >= nr_idle,则gcwq将销毁idle状态持续时间超过5分钟的工作者线程。每个gcwq的线程池最终将维护两个处于idle状态的工作者线程。


期刊库(http://www.zgqkk.com),是一个专门从事期刊推广、投稿辅导的网站。
  本站提供如何投稿辅导,寻求投稿辅导合作,快速投稿辅导,投稿辅导格式指导等解决方案:省级投稿辅导/国家级投稿辅导/核心期刊投稿辅导//职称投稿辅导。


  【免责声明】本文仅代表作者本人观点,与投稿辅导_期刊发表_中国期刊库专业期刊网站无关。投稿辅导_期刊发表_中国期刊库专业期刊网站站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。请读者仅作参考,并请自行承担全部责任。

 
QQ在线咨询
投稿辅导热线:
180-1501-6272
微信号咨询:
fabiaoba-com
咨询电话:18015016272 投稿邮箱:zgqkk365#126.com(#换成@)
本站郑重声明:文章只代表作者观点, 并不意味着本站认同。所载文章、数据仅供参考,使用前请核实,风险自负。
部分作品系转载,版权归原作者或相应的机构   若某篇作品侵犯您的权利,请来信告知.版权:周口博闻教育咨询有限公司 
Copyright © 2005-2023 . 期刊库 版权所有