linux中断处理下文: 工作队列分析

1、工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。

那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。

2、工作、工作队列和工作者线程

如前所述,我们把推后执行的任务叫做工作(work),描述它的数据结构为work_struct,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,而工作线程就是负责执行工作队列中的工作。系统默认的工作者线程为events,自己也可以创建自己的工作者线程。

表示工作的数据结构

工作用<linux/workqueue.h>中定义的work_struct结构表示:

struct work_struct{


unsigned long pending;


struct list_head entry;


void (*func) (void *);


void *data;


void *wq_data;



struct timer_list timer;


};




这些结构被连接成链表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。工作被执行完毕,它就将相应的work_struct对象从链表上移去。当链表上不再有对象的时候,它就会继续休眠。


3、 创建推后的工作


要使用工作队列,首先要做的是创建一些需要推后完成的工作。可以通过DECLARE_WORK在编译时静态地建该结构:


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


这样就会静态地创建一个名为name,待执行函数为func,参数为data的work_struct结构。


同样,也可以在运行时通过指针创建一个工作:


INIT_WORK(struct work_struct *work, woid(*func) (void *), void *data);


这会动态地初始化一个由work指向的工作。


4、工作队列中待执行的函数


工作队列待执行的函数原型是:


void work_handler(void *data)


这个函数会由一个工作者线程执行,因此,函数会运行在进程上下文中。默认情况下,允许响应中断,并且不持有任何锁。如果需要,函数可以睡眠。需要注意的是,尽管该函数运行在进程上下文中,但它不能访问用户空间,因为内核线程在用户空间没有相关的内存映射。通常在系统调用发生时,内核会代表用户空间的进程运行,此时它才能访问用户空间,也只有在此时它才会映射用户空间的内存。


5、 对工作进行调度


现在工作已经被创建,我们可以调度它了。想要把给定工作的待处理函数提交给缺省的events工作线程,只需调用


schedule_work(&work);


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


有时候并不希望工作马上就被执行,而是希望它经过一段延迟以后再执行。在这种情况下,可以调度它在指定的时间执行:


schedule_delayed_work(&work, delay);


这时,&work指向的work_struct直到delay指定的时钟节拍用完以后才会执行。

6 、创建自己的工作队列

使用内核提供的共享列队,列队是保持顺序执行的,做完一个工作才做下一个,如果一个工作内有耗时大的处理如阻塞等待信号或锁,那么后面的工作都不会执行。如果你不喜欢排队或不好意思让别人等太久,那么可以创建自己的工作者线程,所有工作可以加入自己创建的工作列队,列队中的工作运行在创建的工作者线程中。

创建工作列队使用3个宏 成功后返回workqueue_struct *指针,并创建了工作者线程。三个宏主要区别在后面两个参数singlethread和freezeable,singlethread为0时会为每个cpu上创建一个工作者线程,为1时只在当前运行的cpu上创建一个工作者线程。freezeable会影响内核线程结构体thread_info的PF_NOFREEZE标记
 

if (!cwq->freezeable)  
     current->flags |= PF_NOFREEZE;  
 set_user_nice(current, -5);  


在线程函数内设置了测试点如下

if (cwq->freezeable)  
    try_to_freeze();  
如果设置了PF_NOFREEZE这个flag,那么系统挂起时候这个进程不会被挂起。


主要函数

#define create_workqueue(name) __create_workqueue((name), 0, 0)                          //多处理器时会为每个cpu创建一个工作者线程                
#define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1)       //只创建一个工作者线程,系统挂起是线程也挂起  
#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0)    //只创建一个工作者线程,系统挂起是线程线程不挂起  
以上三个宏调用__create_workqueue函数定义  
extern struct workqueue_struct *__create_workqueue(const char *name,int singlethread, int freezeable);  
  
释放创建的工作列队资源  
void destroy_workqueue(struct workqueue_struct *wq)  
  
延时调用指定工作列队的工作  
queue_delayed_work(struct workqueue_struct *wq,struct delay_struct *work, unsigned long delay)  
  
取消指定工作列队的延时工作  
cancel_delayed_work(struct delay_struct *work)  
  
将工作加入工作列队进行调度  
queue_work(struct workqueue_struct *wq, struct work_struct *work)  
  
等待列队中的任务全部执行完毕。  
void flush_workqueue(struct workqueue_struct *wq);  



7、 工作队列的简单应用


#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>


static struct workqueue_struct *queue = NULL;
static struct work_struct work;


static void work_handler(struct work_struct *data)
{
        printk(KERN_ALERT "work handler function./n");
}


static int __init test_init(void)
{
        queue = create_singlethread_workqueue("helloworld"); 
        if (!queue)
                goto err;


        INIT_WORK(&work, work_handler);
        schedule_work(&work);


        return 0;
err:
        return -1;
}


static void __exit test_exit(void)
{
        destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值