tasklet、wait_queue、completion、work_queue用法总结

对于内核中常用的中断处理机制做一些总结,方便在合适的时候采用合适的机制。

tasklet 和 work_queue 是延期执行工作的机制,实现基于软中断;completion的实现基于wait_queue。

——————————————————————————————————————————————————————————————————————————————————————————————————————————————

tasklet

小进程,主要用于执行一些小任务,对这些任务使用全功能进程比较浪费。也称为中断下半部,在处理软中断时执行。

definition:

struct tasklet_struct
{
        struct tasklet_struct *next;
        unsigned long state;
        atomic_t count;
        void (*func)(unsigned long);
        unsigned long data;
};

相关定义:

#define DECLARE_TASKLET(name, func, data) struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

#define DECLARE_TASKLET_DISABLED(name, func, data) struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

enum
{
        TASKLET_STATE_SCHED,    /* Tasklet is scheduled for execution */
        TASKLET_STATE_RUN       /* Tasklet is running (SMP only) */
};

初始化及销毁tasklet的方法:

void tasklet_init(struct tasklet_struct *t,
                  void (*func)(unsigned long), unsigned long data)
{
        t->next = NULL;
        t->state = 0;
        atomic_set(&t->count, 0);
        t->func = func;
        t->data = data;
}

void tasklet_kill(struct tasklet_struct *t)
{
        if (in_interrupt())
                printk("Attempt to kill tasklet from interrupt\n");

        while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
                do {
                        yield();
                } while (test_bit(TASKLET_STATE_SCHED, &t->state));
        }
        tasklet_unlock_wait(t);
        clear_bit(TASKLET_STATE_SCHED, &t->state);
}

调度tasklet的方法:

static inline void tasklet_schedule(struct tasklet_struct *t)
{
        if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
                __tasklet_schedule(t);
}

void __tasklet_schedule(struct tasklet_struct *t)
{
        unsigned long flags;

        local_irq_save(flags);
        t->next = NULL;
        *__this_cpu_read(tasklet_vec.tail) = t;
        __this_cpu_write(tasklet_vec.tail, &(t->next));
        raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_restore(flags);
}

使用方法:

tasklet_init->tasklet_schedule

——————————————————————————————————————————————————————————————————————————————————————————————————————————————

wait_queue

用于使进程等待某一事件的发生,无须频繁轮讯,进程在等待期间睡眠,在事件发生时由内核自动唤醒。

用法1(add_wait_queue 和 wake_up组合):

当nand控制器被一个进程使用时,其余进程被放入等待队列;待第一个进程使用结束后,调用wake_up唤醒等待队列,下一个进程获得nand控制器的使用权。

#define DECLARE_WAITQUEUE(name, tsk)                                            wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

#define __WAITQUEUE_INITIALIZER(name, tsk) {                                    .private        = tsk,                                                  .func           = default_wake_function,                                .task_list      = { NULL, NULL } }

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
        unsigned long flags;

        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue(q, wait);
        spin_unlock_irqrestore(&q->lock, flags);
}

<nand_base.c>
static int nand_test_get_device(struct mtd_info *mtd, int new_state)
{
        struct nand_chip *chip = mtd->priv;
        spinlock_t *lock = &chip->controller->lock;
        wait_queue_head_t *wq = &chip->controller->wq;
        DECLARE_WAITQUEUE(wait, current);
retry:
        spin_lock(lock);

        /* Hardware controller shared among independent devices */
        if (!chip->controller->active)
                chip->controller->active = chip;

        if (chip->controller->active == chip && chip->state == FL_READY) {
                chip->state = new_state;
                spin_unlock(lock);
                return 0;
        }
        if (new_state == FL_PM_SUSPENDED) {
                if (chip->controller->active->state == FL_PM_SUSPENDED) {
                        chip->state = FL_PM_SUSPENDED;
                        spin_unlock(lock);
                        return 0;
                }
        }
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(wq, &wait);
        spin_unlock(lock);
        schedule();
        remove_wait_queue(wq, &wait);
        goto retry;
}

static void nand_test_release_device(struct mtd_info *mtd)
{
        struct nand_chip *chip = mtd->priv;

        /* Release the controller and the chip */
        spin_lock(&chip->controller->lock);
        chip->controller->active = NULL;
        chip->state = FL_READY;
        wake_up(&chip->controller->wq);
        spin_unlock(&chip->controller->lock);
}

方法2(prepare_to_wait 和 wake_up组合),可作为实现阻塞的方式:

#define DEFINE_WAIT_FUNC(name, function)                                        wait_queue_t name = {                                                           .private        = current,                                              .func           = function,                                             .task_list      = LIST_HEAD_INIT((name).task_list),             }

#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)

autoremove_wake_function会调用default_wake_function,然后将所属等待队列成员从等待队列中删除。

void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
        unsigned long flags;

        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        if (list_empty(&wait->task_list))
                __add_wait_queue(q, wait);
        set_current_state(state);
        spin_unlock_irqrestore(&q->lock, flags);
}

static long do_CustomEvent_wait(long eventNum)
{
        struct CustomEvent *tmp = NULL;
        struct CustomEvent *prev = NULL;                                                      

        pr_info("Enten into %s, the eventNum is %d\n", __func__, eventNum);
        if((tmp = FindEventNum(eventNum, &prev)) != NULL) {
                DEFINE_WAIT(wait);
                prepare_to_wait(tmp->p, &wait, TASK_INTERRUPTIBLE);                           

                schedule();
                finish_wait(tmp->p, &wait);
                return eventNum;
        }
        return -1;
}

static long do_CustomEvent_signal(long eventNum)
{
        struct CustomEvent *tmp = NULL;
        struct CustomEvent *prev = NULL;

        pr_info("Enten into %s, the eventNum is %d\n", __func__, eventNum);
        if(!(tmp = FindEventNum(eventNum, &prev)))
                return 0;

        wake_up(tmp->p);
        return 1;
}

第三种方法(wait_event 和 wake_up组合):

该方法首先将进程的状态设为 TASK_UNINTERRUPTIBLE, 之后判断条件是否满足;条件不满足时,通过schedule()将该进程从runqueue队列中移除,该进程进入睡眠状态;只有当某进程调用wake_up才能再次唤醒该进程;进程被唤醒后,再次将该进程的状态置为TASK_UNINTERRUPTIBLE,然后判断条件是否满足。条件满足时结束循环,由finish_wait将进程的状态设置为TASK_RUNNING,并从等待队列的链表中移除相应项。

#define __wait_event(wq, condition)                                     do {                                                                            DEFINE_WAIT(__wait);                                                                                                                            for (;;) {                                                                      prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);                    if (condition)                                                                  break;                                                          schedule();                                                     }                                                                       finish_wait(&wq, &__wait);                                      } while (0)

#define wait_event(wq, condition)                                       do {                                                                            if (condition)                                                                  break;                                                          __wait_event(wq, condition);                                    } while (0)

——————————————————————————————————————————————————————————————————————————————————————————————————————————————

completion:

基于等待队列,内核利用该机制等待某一操作结束。由于和wait_queue用法类似,不再详细描述。

completion的操作接口:

init_completion()封装了init_waitqueue_head()接口;

wait_for_completion()封装了wait_for_common()接口;

complete()封装了__wake_up_common()接口。

——————————————————————————————————————————————————————————————————————————————————————————————————————————————

work_queue:

由于在中断中不能进行阻塞型操作,而有时候需要在中断时读取某些内存单元或寄存器的值,此时可以考虑利用工作队列来实现。

工作队列的定义:

工作队列是将操作延期的一种手段。因为他们是通过守护进程在用户上下文执行,函数可以睡眠任意长的时间,这与内核无关。替换了之前的kevented机制。

工作队列的使用:

使用工作队列,主要有以下三个步骤:

1 实现工作任务处理函数

2 创建并初始化工作任务

3 将工作任务添加到某工作队列中,等待系统调度

<kernel/workqueue.c>

int schedule_work(struct work_struct *work)

int schedule_delayed_work(struct work_struct *dwork, unsigned long delay)

1 定义工作任务处理函数:

static void func(struct work_struct *work)
{
......
} 

2 创建并初始化工作任务

可采用两种方式创建并初始化工作任务:

1) 先创建工作任务,后绑定处理函数:

struct work_struct xxx_wq;//创建工作任务

在模块初始化的时候:

INIT_WORK(&xxx_wq,func);//初始化工作任务,工作任务需要执行的是函数func

2)  创建工作任务的同时初始化:

DECLARE_WORK(xxx_wq, func); 

3 在中断处理函数中将工作任务添加到工作队列,等待系统调度

static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
        return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}

用于将某工作任务添加到某工作队列

内核创建了一个标准的工作队列events,内核的各个部分若无必要创建独立的工作队列,可直接使用该工作队列。

static inline bool schedule_work(struct work_struct *work)//将工作任务xxx_wq添加到标准工作队列events中,中断处理完成之后可以立即执行func
{
        return queue_work(system_wq, work);
}
时间: 2024-10-05 06:43:55

tasklet、wait_queue、completion、work_queue用法总结的相关文章

关于对 softirq、work_queue、tasklet 学习后的一点总结

本文基于linux版本:4.14.111 简单的总结下 softirq.work_queue.tasklet 三种中断下半部的工作原理及区别,并附上三种形式的简单实例. 一.运行原理① softirq: void __do_softirq(void) { int max_restart = MAX_SOFTIRQ_RESTART; ///< 10 struct softirq_action *h; ... pending = local_softirq_pending(); ///< 获取到当

IOS开发之UIAlertView与UIAlertController的详尽用法说明

本文将从四个方面对IOS开发中UIAlertView与UIAlertController的用法进行讲解: 一.UIAlertView与UIAlertController是什么东东? 二.我们为什么要用UIAlertView或UIAlertController? 三.如何使用UIAlertView和UIAlertController? 四.阅读提醒. 一.UIAlertView与UIAlertController是什么东东? 一句话,在所有移动应用中,出现的提示窗一般都由UIAlertView或U

IOCP扩展方法AcceptEx, DisconnectEx, GetAcceptExSockaddr用法示例

这篇文章记录了我刚接触IOCP模型时的理解,对于初学者,应该算不错的调试程序,仅有一个400多行代码的dpr文件,可以直接用WriteLn输出信息,前提是会用delphi建立Console Application,当然你也可以很容易的就改成了C控制台程序或是其它语言.附加代码中有详细的注释,如果你已有IOCP经验,那么请不用在此浪费时间,这个示例不适合你.示例仅展示了IOCP中AcceptEx, DisconnectEx, GetAcceptExSockaddr等的用法.在文章最后有推荐的两个连

iOS block-base 动画简单用法+关键帧动画设置线性变化速度的问题

本文转载至 http://www.tuicool.com/articles/aANBF3m 时间 2014-12-07 20:13:37  segmentfault-博客原文  http://segmentfault.com/blog/alan/1190000002411296 iOS的各种动画相漂亮,相信这是吸引很多人买iPhone的原因之一.不仅如此,这还是吸引我做iOS开发的一大原因,因为在iOS上给界面实现一些像样的动画实在是太轻松了! 这里就介绍一下iOS的block-based an

完成端口(Completion Port)详解(转)

手把手叫你玩转网络编程系列之三    完成端口(Completion Port)详解                                                              ----- By PiggyXP(小猪) 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何提笔,所以这篇文档总是在酝酿之中……酝酿了两年之后,终于决定开始动笔了,但愿还不算晚….. 这篇文档我非常详细并且图文并茂的介绍了关于网络编程模型中完成端口的方方

【转】IOS NSTimer 定时器用法总结

原文网址:http://my.oschina.net/u/2340880/blog/398598 NSTimer在IOS开发中会经常用到,尤其是小型游戏,然而对于初学者时常会注意不到其中的内存释放问题,将其基本用法总结如下: 一.初始化方法:有五种初始化方法,分别是 + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; -

UIImagePickerController的用法

在实际的APP开发中,我们经常会见到应用的这样的功能 :需要选取手机相册的照片,还有选取视频,拍视频和照相的操作. 在iOS开发中,实现以上的功能就需要用到 UIImagePickerController. 现将 UIImagePickerController的基本用法总结如下 : 1 #import "ViewController.h" 2 3 @interface ViewController ()<UIImagePickerControllerDelegate,UINavi

Linux内核中的软中断、tasklet和工作队列详解

[TOC] 本文基于Linux2.6.32内核版本. 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的"下半部"(bottom half)演变而来.下半部的机制实际上包括五种,但2.6版本的内核中,下半部和任务队列的函数都消失了,只剩下了前三者. 介绍这三种下半部实现之前,有必要说一下上半部与下半部的区别. 上半部指的是中断处理程序,下半部则指的是一些虽然与中断有相关性但是可以延后执行的任务.举个例子:在网络传输中,网卡接收到数据包这

java的concurrent用法详解

我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便.而当针对高质量Java多线程并发程序设计时,为防止死蹦等现象的出现,比如使用java之前的wait().notify()和synchronized等,每每需要考虑性能.死锁.公平性.资源管理以及如何避免线程安全性方面带来的危害等诸多因素,往往会采用一些较为复杂的安全策略,加重了程序员的开发负担.万幸的是,在JDK1.5出