内核工作队列workqueue

LDD3: 工作队列是, 表面上看, 类似于 taskets; 它们允许内核代码来请求在将来某个时间调用一个函数. 但是, 有几个显著的不同在这 2 个之间, 包括:

(1) tasklet 在软件中断上下文中运行的结果是所有的 tasklet 代码必须是原子的. 相反, 工作队列函数在一个特殊内核进程上下文运行; 结果, 它们有更多的灵活性. 特别地, 工作队列函数能够睡眠.

(2) tasklet 常常在它们最初被提交的处理器上运行. 工作队列以相同地方式工作, 缺省地.

(3) 内核代码可以请求工作队列函数被延后一个明确的时间间隔.

两者之间关键的不同是 tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能有高周期但是不需要是原子的. 每个机制有它适合的情形.

使用

工作队列有一个 struct workqueue_struct 类型, 在 <linux/workqueue.h> 中定义.

创建:
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);

如果你使用create_workqueue, 你得到一个工作队列它有一个专用的线程在系统的每个处理器上. 在很多情况下, 所有这些线程是简单的过度行为; 如果一个单个工作者线程就足够, 使用 create_singlethread_workqueue。

声明:

DECLARE_WORK(name, void (*function)(void *), void *data);

运行时初始化:

static struct workqueue_struct * workqueue;

INIT_WORK(struct work_struct *work, void (*function)(void *), void *data); 
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data); 

还有一个INIT_DELAYED_WORK:

#define INIT_DELAYED_WORK(_work, _func)                      \

do{                                           \

INIT_WORK(&(_work)->work,(_func));         \

init_timer(&(_work)->timer);                     \

}while (0)

加入队列:

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);

如果使用queue_delay_work, 但是, 实际的工作没有进行直到至少delay jiffies 已过去. 从这些函数的返回值是 0 如果工作被成功加入到队列; 一个非零结果意味着这个 work_struct 结构已经在队列中等待, 并且第 2 次没有加入.

如果你需要取消一个挂起的工作队列入口, 你可以调用:

int cancel_delayed_work(struct work_struct*work);

返回值是非零如果这个入口在它开始执行前被取消. 内核保证给定入口的执行不会在调用 cancel_delay_work 后被初始化. 如果cancel_delay_work 返回 0, 但是, 这个入口可能已经运行在一个不同的处理器, 并且可能仍然在调用 cancel_delayed_work 后在运行. 要绝对确保工作函数没有在 cancel_delayed_work 返回 0 后在任何地方运行, 你必须跟随这个调用来调用:

void flush_workqueue(structworkqueue_struct *queue);

在flush_workqueue 返回后, 没有在这个调用前提交的函数在系统中任何地方运行.

当你用完一个工作队列, 你可以去掉它, 使用:

void destroy_workqueue(structworkqueue_struct *queue);

示例:

//全局定义
static struct workqueue_struct *rohm_workqueue;

初始化函数中:
    rohm_workqueue = create_singlethread_workqueue("rohm_workqueue");  //这时队列还没有工作
    if (!rohm_workqueue) {
        return (-ENOMEM);
}

销毁函数中:
destroy_workqueue(rohm_workqueue);

开始:
INIT_WORK(&ps_als->work, ps_als_work_func);  //初始化还没有工作
设定一个定时器:
/* timer process */
        hrtimer_init(&ps_als->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ps_als->timer.function = ps_als_timer_func;
注意这里并没有启动定时器,这条语句下面有一条代码:
INIT_DELAYED_WORK(&ps_als->input_work, ps_als_work_func); ps_als_work_func会启动定时器。

定时器处理函数:
static enum hrtimer_restart ps_als_timer_func(struct hrtimer *timer)
{
    PS_ALS_DATA *ps_als;
    int         result;

    ps_als = container_of(timer, PS_ALS_DATA, timer);
    result = queue_work(rohm_workqueue, &ps_als->work);
    if (result == 0) {
        printk(KERN_ERR "can‘t register que.\n");
        printk(KERN_ERR "result = 0x%x\n", result);
    }

    return (HRTIMER_NORESTART);
}

static void ps_als_work_func(struct work_struct *work)
{

/* the setting value from application */
        get_timer = ps_als->delay_time;
        /* 125ms(8Hz) at least */
        wait_sec  = (get_timer / SM_TIME_UNIT);
        wait_nsec = ((get_timer - (wait_sec * SM_TIME_UNIT)) * MN_TIME_UNIT);
        result    = hrtimer_start(&ps_als->timer, ktime_set(wait_sec, wait_nsec), HRTIMER_MODE_REL);
        if (result != 0) {
            printk(KERN_ERR "can‘t start timer\n");
            return;
        }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-02 23:31:42

内核工作队列workqueue的相关文章

[内核]Linux workqueue

转自:http://blog.chinaunix.net/uid-24148050-id-296982.html 一.workqueue简介 workqueue与tasklet类似,都是允许内核代码请求某个函数在将来的时间被调用(抄<ldd3>上的)每个workqueue就是一个内核进程. workqueue与tasklet的区别: tasklet是通过软中断实现的,在软中断上下文中运行,tasklet代码必须是原子的. 而workqueue是通过内核进程实现的,就没有上述限制的,而且工作队列

kobox : key_wq.c -v1 如何使用工作队列 workqueue

kobox: key_wq.c - v1 说明: TQ2440主要驱动因素,四个按键驱动的处理 key_wq.c和key.c类别似,与key.c之间的差异的主要驱动力: key.c使用计时器,在中断发生100ms调用定时器处理函数来防止按键抖动 key_wq.c使用工作队列.在内核调度共享工作队列,在工作队列中延时100ms然后推断按键状态来防止按键抖动 问题: 仅仅有内核共享工作队列,且不延时的情况下.程序运行才正常: /* 使用内核共享队列,马上调度.延时放到中断函数中 */ schedul

Linux内核工程导论——进程

进程 进程调度 概要 linux是个多进程的环境,不但用户空间可以有多个进程,而且内核内部也可以有内核进程.linux内核中线程与进程没有区别,因此叫线程和进程都是一样的.调度器调度的是CPU资源,按照特定的规则分配给特定的进程.然后占有CPU资源的资源去申请或使用硬件或资源.因此这里面涉及到的几个问题: 对于调度器来说: l  调度程序在运行时,如何确定哪一个程序将被调度来使用CPU资源? n  如何不让任何一个进程饥饿? n  如何更快的定位和响应交互式进程? l  单个CPU只有一个流水线

Linux内核线程kernel thread详解--Linux进程的管理与调度(十)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-02 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度-之-进程的描述 内核线程 为什么需要内核线程 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求). 内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的. 内核线程就是内核的分身,一个分身可以处理一件特定事情.内核线程的调度由内核负责,一个内核线程处于阻

内核中断处理

中断取代了轮询的通知方式,DMA取代了轮询的读写数据方式. 分类软件指令造成的中断(又叫异常,同步中断).    svc, und, abt硬件通过中断请求信号造成的中断(异步中断).  irq,fiq 向量中断和非向量中断采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行.不同中断号的中断有不同的入口地址.非向量中断的多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来识别具体是哪个中断.向量中断由硬件提供中断

linux内核是中断下半部

首先阐述下为什么内核要将中断分成上下半部 因为中断本身打断了正常的程序执行,中断中不能进行任务调度,所以中断需要快返回,但是某些操作必须在中断中执行. 如果内核需要执行一个硬件相关.时间敏感.不能被中断的操作,那么这些操作就应该放到上半部中,其他能够推迟的操作应该放到下半部中去,这样完成了中断中必须完成的操作,又能很好的进行调度. 看看内核对于下半部的支持 首先说以下如何添加自己的软中断程序 首先添加自己的软中断类型,值越低优先级越高 用open_softirq增加相对应的中断处理函数 用rai

【转】工作队列学习

首先要注意本文的两个概念:(1)使用内核提供的工作队列, (2)自己创建工作队列 http://blog.csdn.net/fontlose/article/details/8286445 工作队列是一种将工作推后执行的形式,交由一个内核线程去执行在进程上下文执行,其不能访问用户空间.最重要特点的就是工作队列允许重新调度甚至是睡眠. 工作队列子系统提供了一个默认的工作者线程来处理这些工作.默认的工作者线程叫做events/n,这里n是处理器的编号,每个处理器对应一个线程,也可以自己创建工作者线程

Linux内核模块编程可以使用的内核组件

2.2.2 在阅读<深入Linux内核架构与底层原理> 作者:刘京洋 韩方,发现一些错误,有些自己的理解,特以此记录 1.工作队列(workqueue) 队列是一种可以先进先出的数据结构,常常用来将一些工作任务缓冲的情况中.在linux下的workqueue可以用来处理内核中的任务链. linux内核有workqueue,用户可以实现自己的workqueue,如果需要workqueue时,都临时创建,会导致系统开销大,为了减少开销,内核使用了workqueue的线程池的技术,将创建好的work

VxWorks 6.9 内核编程指导之读书笔记 -- ISRs和Watchdog Timer

中断服务程序 ISR 硬件中断处理是实时系统的关键,因为它是外部时间通知系统的方式. ISR亦称为中断处理函数,是对中断的正确响应.可以使用任何ISR连接到任何没有被VxWorks使用的中断上.当关联的中断发生时,VxWorks运行ISR:ISR的处理不会延迟,除非你配置系统去延迟. VxWorks的ISR配置 默认支持ISR.但是,中断栈和额外特征可以被配置.此外,ISR的延迟支持和显示函数支持可以被增加到系统. 配置中断栈 所有中断使用相同的中断栈.栈在系统启动时更加配置参数来分配和初始化.