• Linux设备驱动程序 之 工作队列


    工作队列可以把工作推后,交给一个内核线程去执行–这个下半部分总是会在进程上下文中执行;通过工作队列执行的代码占尽进程上下文的优势;最重要的是工作队列允许重新调度甚至睡眠;

    在工作队列和软中断/tasklet中做出选择很容易;如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择软中断或者tasklet;

    如果需要用一个可以重新调度的实体来执行下半部处理,那么应该使用工作队列;它是唯一能在进程上下文中运行的下半部机制,也只有它才可以睡眠;这意味着如果需要获取大量的内存,或者在需要获取信号量时,或者需要执行阻塞式IO操作时,它会非常有用;如果不需要一个内核线程来推后执行工作,那么考虑使用tasklet;

    创建推后的工作

    可以通过DECLARE_WORK在编译时静态的创建work_struct结构实例并初始化:

    1 #define DECLARE_WORK(n, f)                        
    2     struct work_struct n = __WORK_INITIALIZER(n, f)
    3 
    4 #define DECLARE_DELAYED_WORK(n, f)                    
    5     struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0)
    6 
    7 #define DECLARE_DEFERRABLE_WORK(n, f)                    
    8     struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, TIMER_DEFERRABLE)

    或者在运行时动态的创建一个work_struct实例,然后使用下面函数进行初始化:

     1 #define INIT_WORK(_work, _func)                        
     2     __INIT_WORK((_work), (_func), 0)
     3 
     4 #define INIT_WORK_ONSTACK(_work, _func)                    
     5     __INIT_WORK((_work), (_func), 1)
     6 
     7 #define INIT_DELAYED_WORK(_work, _func)                    
     8     __INIT_DELAYED_WORK(_work, _func, 0)
     9 
    10 #define INIT_DELAYED_WORK_ONSTACK(_work, _func)                
    11     __INIT_DELAYED_WORK_ONSTACK(_work, _func, 0)
    工作队列处理函数

    工作队列处理函数的原型是:

    1 typedef void (*work_func_t)(struct work_struct *work);

    这个函数会由一个工作者线程执行,因此,函数会运行在进程上下文中;默认情况下,允许响应中断,并不持有任何锁;如果需要,函数可以睡眠;

    需要注意的是,尽管操作处理函数运行在进程上下文,但它不能访问用户空间,因为内核线程在用户空间没有相关的内存映射;通常在发送系统调用时,内核会代表用户空间进程运行,也只有此时它才会映射用户空间的内存;

    调度工作(缺省工作队列)

    如果要把给定工作的处理交给缺省的工作线程,则需要调用:

    1 bool schedule_work(struct work_struct *work)

    work马上会被调度,一旦其所在的处理器上的工作线程被唤醒,它就会被执行;

    如果不希望马上被执行,而是希望它经过一段时间的延迟后才执行,则需要下面函数,可以在指定时间之后执行:

    1 bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)

    这时,dwork会在delay指定的始终节拍用完之后才会执行;

    刷新操作

    排入队列的工作会在工作者线程下一次被唤醒时执行;有时,在继续下一步工作之前,必须保证一些操作已经执行完毕了;这一点对模块来说就很重要,在卸载之前,它就有可能需要调用下面的函数;而在内核的其他部分,为了防止竞争条件的出现,也可能需要确保不再有待处理的工作;

    为了,内核准备了用于刷新指定工作队列的函数:

    1 bool flush_work(struct work_struct *work);
    2 
    3 bool flush_delayed_work(struct delayed_work *dwork);

    函数会一直等待,直到队列中的所有对象都被执行以后才返回;在等待所有待处理的工作执行的时候,该函数会进入休眠状态,所以只能在进程上下文中使用;

    内核也提供了取消执行工作的函数:

    1 bool cancel_work(struct work_struct *work);
    2 bool cancel_work_sync(struct work_struct *work);
    3 
    4 bool cancel_delayed_work(struct delayed_work *dwork);
    5 bool cancel_delayed_work_sync(struct delayed_work *dwork);
    创建新的工作队列

    如果缺省队列不能满足需求,则应该创建一个新的工作队列和与之对应的工作者线程;由于这么做会在每个处理器上都创建一个工作者线程,所以只有在你明确了必须要靠自己一套线程来提高性能的情况下,再创建自己的队列;

    创建一个新的任务队列和与之相关的工作者线程,需要使用下面函数,name参数用于该内核线程的命名;

    1 #define create_workqueue(name)                        
    2     alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
    3 #define create_freezable_workqueue(name)                
    4     alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND |    
    5             WQ_MEM_RECLAIM, 1, (name))
    6 #define create_singlethread_workqueue(name)                
    7     alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)

    这样就会在每个处理器上创建工作者线程,并准备好开始处理工作之前的准备工作;

    创建一个工作的时候无须考虑工作队列的类型,在创建之后,可以调用下面的函数;这些函数与schedule_work()相似,唯一的区别是它们针对给定的工作队列而不是缺省的队列进行操作;

    1 bool queue_work(struct workqueue_struct *wq,
    2                   struct work_struct *work)
    3 
    4 bool queue_delayed_work(struct workqueue_struct *wq,
    5                       struct delayed_work *dwork,
    6                       unsigned long delay)

    刷新指定的工作队列可以使用下面函数:

    1 void flush_workqueue(struct workqueue_struct *wq);

    结束对工作队列的使用后,可以使用下面函数释放资源:

    1 void destroy_workqueue(struct workqueue_struct *wq);
  • 相关阅读:
    aws centos 基本环境安装
    aws 安装python解释器
    odoo 开发环境部署
    graphql规范
    python 字符串format使用
    设计模式
    集合的常见操作
    字典常见操作
    python实现简单的购物车
    python实现简单的登录管理
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11761812.html
Copyright © 2020-2023  润新知