Workqueue机制的实现

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

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

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_workqueue对workqueue队列进行初始化时,内核就开始为用户分配一个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都创建一个内核线程。输入参数:

@name:workqueue的名称

2

create_singlethread_workqueue

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

@name:workqueue名称

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。

work queue 的使用实例:

 

struct my_work_t {

char *name;

struct work_struct work;  //作为自己数据结构的成员,然后用container_of获得my_work_t的指针

};
void my_func(struct work_struct *work)

{

struct my_work_t *my_work = container_of (work, struct my_work_t, work);

printk(KERN_INFO “Hello world, my name is %s!/n”, my_work->name);

}
struct workqueue_struct *my_wq = create_workqueue(“my wq”);      //创建自己的work queue

struct my_work_t my_work;

my_work.name = “Jack”;

INIT_WORK(&(my_work.work), my_func);          //初始化自己的work queue

queue_work(my_wq, &(my_work.work));

destroy_workqueue(my_wq);                     //销毁自己的work queue

时间: 2024-08-02 22:28:31

Workqueue机制的实现的相关文章

workqueue机制分析之wb_workfn函数

上面一篇文章说到: process_one_work中最重要的一件事情就是worker->current_func(work); 这里就具体到一项很具体的任务了,由于我要研究文件系统嘛,所以很自然就到具体的任务里: void wb_workfn(struct work_struct *work) 首先,work变量只是个助推器,真正的主子在哪呢? struct bdi_writeback *wb = container_of(to_delayed_work(work), struct bdi_w

Linux workqueue疑问【转】

转自:http://blog.csdn.net/angle_birds/article/details/9387365 各位大神,你们好.我在使用workqueue的过程中遇到一个问题. 项目采用uClinux系统,VoIP相关的. 现有两个驱动,一个是负责数据传输的,还有一个是负责打电话的.这两个驱动里分别使用了一个workqueue.在数据传输量很大时,负责数据传输的workqueue非常耗费资源,CPU占用能达到60-70%.这时候,我打电话,也就是让负责打电话的workqueue工作,但

Linux workqueue工作原理 【转】

转自:http://blog.chinaunix.net/uid-21977330-id-3754719.html 转自:http://bgutech.blog.163.com/blog/static/18261124320116181119889/1. 什么是workqueue       Linux中的Workqueue机制就是为了简化内核线程的创建.通过调用workqueue的接口就能创建内核线程.并且可以根据当前系统CPU的个 数创建线程的数量,使得线程处理的事务能够并行化.workqu

workqueue --最清晰的讲解

带你入门: 1.INIT_WORK(struct work_struct *work, void (*function)(void *), void *data) 上面一句只是定义了work和work对应的操作.  要是在实际使用的时候还是需要你去在适当的条件下激活这个work.只有激活了这个work,  这个work才有运行的机会.这个激活操作接口是shudule_work或是queue_work.  这两个接口之后只是说这个work有了运行的机会,但是具体到什么时候运行,那要看你用哪个接口激

Concurrency Managed Workqueue(三)创建workqueue代码分析

一.前言 本文主要以__alloc_workqueue_key函数为主线,描述CMWQ中的创建一个workqueue实例的代码过程. 二.WQ_POWER_EFFICIENT的处理 __alloc_workqueue_key函数的一开始有如下的代码: if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient) flags |= WQ_UNBOUND; 在kernel中,有两种线程池,一种是线程池是per cpu的,也就是说,系统中

Concurrency Managed Workqueue(二)CMWQ概述

一.前言 一种新的机制出现的原因往往是为了解决实际的问题,虽然linux kernel中已经提供了workqueue的机制,那么为何还要引入cmwq呢?也就是说:旧的workqueue机制存在什么样的问题?在新的cmwq又是如何解决这些问题的呢?它接口是如何呈现的呢(驱动工程师最关心这个了)?如何兼容旧的驱动呢?本文希望可以解开这些谜题. 本文的代码来自linux kernel 4.0. 二.为何需要CMWQ? 内核中很多场景需要异步执行环境(在驱动中尤其常见),这时候,我们需要定义一个work

Linux中断管理 (3)workqueue工作队列

目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管理 (3)workqueue工作队列> 关键词: 工作队列的原理是把work(需要推迟执行的函数)交由一个内核线程来执行,它总是在进程上下文中执行. 工作队列的优点是利用进程上下文来执行中断下半部操作,因此工作队列允许重新调度和睡眠,是异步执行的进程上下文,它还能解决软中断和tasklet执行时间过长导

Linux 中断处理浅析

最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理一下~ 第一阶段--获取中断号 每个CPU都有响应中断的能力, 每个CPU响应中断时都走相同的流程. 这个流程就是内核提供的中断服务程序. 在进入中断服务程序时, CPU已经自动禁止了本CPU上的中断响应, 因为CPU不能假定中断服务程序是可重入的. 中断处理程序的第一步要做两件事情: 1. 将中断号压入栈中; (不同中断号的中断对应不同的中

kobox -- key_tasklet.c -v1

将key.c中的timer机制.key_wq.c中的workqueue机制改成tasklet机制,完成中断的下半部 需要特别注意:tasklet中不可休眠,其上下文是中断,而workqueue是可以休眠的,wq的上下文是内核线程 所以这里并没有去除抖动,如果需要延时去抖动,timer或者workqueue更合适 如果需要休眠,就不能选择tasklet #include "key.h" #define S3C_ADDR_BASE 0xF6000000 #define S3C_ADDR(x