EDN首页   博客首页

3

关于投票
Linux中Workqueue机制分析

    走入Linux的殿堂已经有一年有余了,在这里我想将Linux的各种实现机制分析一遍,一方面对自己来说也是温故而知新,另一方面,促进大家的交流,最好能够给大家一些抛砖引玉的启迪。我是硬件出身,搞硬件已经好多年了,从是专门软件开发也接近两年了,在这一段时间内我越发认为软硬件协同设计是未来发展的主流,软硬件的界限越来越模糊,软硬件的设计思想是相通的,实现方法是各异的,实现的结果上当然也存在较大差别,因此,很有必要做好软硬件的协同设计。本着这样的想法,我想将我所认识的Linux分析一遍,特别是一些我认为精华和重要的机制,另外在讨论过程中,我会插入一些其他的OS实现机制,进行对比分析,我把这一类blog文章划归为“Linux机制分析”,希望大家支持。

 

什么是workqueue

Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。

workqueue是内核中实现简单而有效的机制,他显然简化了内核daemon的创建,方便了用户的编程,

 

Workqueue机制的实现

Workqueue机制中定义了两个重要的数据结构,分析如下:

1、          cpu_workqueue_struct结构。该结构将CPU和内核线程进行了绑定。在创建workqueue的过程中,Linux根据当前系统CPU的个数创建cpu_workqueue_struct。在该结构主要维护了一个任务队列,以及内核线程需要睡眠的等待队列,另外还维护了一个任务上下文,即task_struct

2、          work_struct结构是对任务的抽象。在该结构中需要维护具体的任务方法,需要处理的数据,以及任务处理的时间。该结构定义如下:

struct work_struct {

              unsigned long pending;

               struct list_head entry;                  /* 将任务挂载到queue的挂载点 */

               void (*func)(void *);                   /* 任务方法 */

               void *data;                                  /* 任务处理的数据*/

               void *wq_data;                           /* work的属主 */

               strut timer_list timer;                   /* 任务延时处理定时器 */

};

      

       当用户调用workqueue的初始化接口create_workqueue或者create_singlethread_workqueueworkqueue队列进行初始化时,内核就开始为用户分配一个workqueue对象,并且将其链到一个全局的workqueue队列中。然后Linux根据当前CPU的情况,为workqueue对象分配与CPU个数相同的cpu_workqueue_struct对象,每个cpu_workqueue_struct对象都会存在一条任务队列。紧接着,Linux为每个cpu_workqueue_struct对象分配一个内核thread,即内核daemon去处理每个队列中的任务。至此,用户调用初始化接口将workqueue初始化完毕,返回workqueue的指针。

 

       在初始化workqueue过程中,内核需要初始化内核线程,注册的内核线程工作比较简单,就是不断的扫描对应cpu_workqueue_struct中的任务队列,从中获取一个有效任务,然后执行该任务。所以如果任务队列为空,那么内核daemon就在cpu_workqueue_struct中的等待队列上睡眠,直到有人唤醒daemon去处理任务队列。

 

       Workqueue初始化完毕之后,将任务运行的上下文环境构建起来了,但是具体还没有可执行的任务,所以,需要定义具体的work_struct对象。然后将work_struct加入到任务队列中,Linux会唤醒daemon去处理任务。

 

       上述描述的workqueue内核实现原理可以描述如下:
点击看大图

    在Workqueue机制中,提供了一个系统默认的workqueue队列——keventd_wq,这个队列是Linux系统在初始化的时候就创建的。用户可以直接初始化一个work_struct对象,然后在该队列中进行调度,使用更加方便。

 

Workqueue编程接口

序号

接口函数

说明

1

create_workqueue

用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数:

@nameworkqueue的名称

2

create_singlethread_workqueue

用于创建workqueue,只创建一个内核线程。输入参数:

@nameworkqueue名称

3

destroy_workqueue

释放workqueue队列。输入参数:

@ workqueue_struct:需要释放的workqueue队列指针

4

schedule_work

调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue——keventd_wq输入参数:

@ work_struct:具体任务对象指针

5

schedule_delayed_work

延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,输入参数:

@work_struct:具体任务对象指针

@delay:延迟时间

6

queue_work

调度执行一个指定workqueue中的任务。输入参数:

@ workqueue_struct:指定的workqueue指针

@work_struct:具体任务对象指针

7

queue_delayed_work

延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay

 


系统分类: 嵌入式
用户分类: Linux内核设计
标签: Linux机制分析,嵌入式系统
来源: 原创
发表评论 阅读全文(343) | 回复(2)

2

关于投票
嵌入式操作系统开源项目列表

<!--[if !supportLists]-->目前,网上有很多开源的嵌入式OS项目,对嵌入式RTOS感兴趣的朋友不妨关注一下:)


1.         t-kernel。这是目前嵌入式领域应用最为广泛的开源实时内核,为日本开发,开源站点为:http://www.t-engine.org/

2.         uc/oshttp://www.micrium.com/ micrium公司的站点,站点上有很多uc/os相关的资料,是开发uc/os系统的官方站点。

3.         uclinux。在通用linux平台的基础上,去除了MMU内存管理单元,精简了glibc等应用程序库,适合在无需虚拟内存的嵌入式环境下应用。官方站点:http://www.uclinux.org/

4.         ecosEcos是一个开源,完全免费的实时嵌入式操作系统,官方站点:http://ecos.sourceware.org/

5.         rtlinux。在同用linux平台的基础上构建的实时Linux系统,在实时性较强的环境中应用。开源站点为:http://en.wikipedia.org/wiki/RTLinuxhttp://www.rtlinuxfree.com/

6.         lxRTOS。爱好者自己开发的开源微系统实时内核,只能用于学习,不可商用。官方站点:http://www.lx-rtos.com/

7.         Nucleus Plus。美国源代码操作系统商ATI公司推出的新一代嵌入式操作系统,属于抢先式实时多任务操作系统内核, 95%的代码使用ANSI C编写,非常便于移植于各种处理器。相关站点:http://en.wikipedia.org/wiki/Nucleus_RTOS

8.        Windows CE。这是微软开源的嵌入式操作系统,http://en.wikipedia.org/wiki/Windows_CE

9.         tinyos。应用于无线传感器网络领域的嵌入式操作系统,由美国伯克利分校完成。相关资源:http://www.tinyos.net/http://en.wikipedia.org/wiki/TinyOS

系统分类: 嵌入式
用户分类: 嵌入式系统设计
标签: 嵌入式操作系统
来源: 整理
发表评论 阅读全文(454) | 回复(3)

3

关于投票
Linux中COW快照bitmap语义分析

快照技术是计算机技术中的传统技术,常应用于数据的快速备份。在快照实现技术中,最常用的快照算法有COW写时拷贝算法和ROW写时映射算法。COW算法适合在写少读多的情况下应用;ROW算法适合在写多读少的情况下应用。这里主要分析一下COW算法实现过程中的bitmap语义。

 

        COWCopy On Write)快照有两种实现方式:一种为草型快照,另一种为链式快照。草型快照在快照设备非常多的时候需要进行多份数据拷贝,但是快照设备之间不存在紧密的依赖关系;链式快照只需要进行一次数据拷贝,但是快照设备之间存在紧密的依赖关系。两种快照的逻辑结构表述如下:

链式快照

草型快照

    在实现COW快照过程中需要用一个bitmap来表示快照设备之间的增量信息。对于链式快照而言,假设创建一个快照点s4,那么源设备会将记录的bitmap信息交给s4,然后将源设备的bitmap清空。这样在写源设备的时候,系统会检查写操作所在的chunk是否在历史的某个时刻被写过,如果已经被写过,那么将orig设备上所对应chunk拷贝到快照点s4,并且将新的数据写入orig,然后再修改orig设备的bitmap对应chunk的位信息。这个过程就是快照的写时拷贝过程。对于草型快照而言,数据将向多个快照点进行COW,效率相对于链式快照低。从这个过程可以看出,orig设备上的bitmap信息记录的是最近一个快照点之后的增量数据信息。在未来的某个时刻,如果再创建一个快照点,那么orig上的bitmap信息将转移给新创建的快照设备,这样这个bitmap信息就描述了目前最近快照点到未来快照点之间的增量数据。这些增量数据的老数据都位于最近快照设备之上,因此,在链式快照的读写算法中,需要通过读前驱设备的bitmap信息来定位数据是否位于被访问的快照设备上。草型快照具有同样的原理,其bitmap信息在创建快照的时候从orig转移给新快照,然后orig上的bitmap清空。由于草型快照设备之间具有很强的独立性,无法反映快照设备之间的增量信息,所以通过这个增量数据的bitmap信息,可以方便的获取草型快照之间的增量信息。

 

       下面补充一下链式快照的读过程:假设读快照设备s2时间点的信息,那么首先读取s3时间的bitmap信息,如果读取chunk的位信息为1,那么说明s2上的数据有效,直接从s2设备上读取信息。如果位信息无效,那么依次往前遍历,定位bitmap位有效的下一个快照设备。这就是COW快照设备的读过程。草型快照的读过程和这个不一样,具体代码可以参考Linux中的snapshot代码。

 

       从上述分析可以看出,COWbitmap语义为快照点之间的增量信息,通过这个增量信息,链式快照可以遍历得到快照点上的数据,草型快照可以得到快照点之间的增量信息。感兴趣的朋友可以阅读Linux中的snapshot相关代码,本文只是对阅读代码过程中遇到的bitmap语义进行分析说明。


系统分类: 软件开发
用户分类: 软件开发
标签: Linux驱动
来源: 原创
发表评论 阅读全文(379) | 回复(0)
总共 , 当前 /