首页 > 代码库 > 【转】工作队列学习

【转】工作队列学习

首先要注意本文的两个概念:(1)使用内核提供的工作队列, (2)自己创建工作队列

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

 工作的定义

    typedef void(*work_func_t)(struct work_struct *work);
    
    定义中初始化处理函数
    DECLARE_WORK(n,f);
   
    #define DECLARE_WORK(n,f) struct work_struct n = __WORK_INITIALIZER(n, f)
    #define__WORK_INITIALIZER(n, f) {      \
.data = http://www.mamicode.com/WORK_DATA_INIT(0), >
.entry = { &(n).entry, &(n).entry }, \
.func = (f)      \
    }
 
    先定义中后初始化处理函数
    structwork_struct 
    INIT_WORK(structwork_struct *work, func_t);
    #define INIT_WORK(_work,_func) \
do {                     \
__INIT_WORK((_work), (_func), 0); \
} while (
在使用带delay的函数或宏时使用DECLARE_DELAYED_WORK定义和INIT_DELAYED_WORK初始化。

1.使用内核提供的共享列队
    对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
     intschedule_work(struct work_struct *work);
 
    确保没有工作队列入口在系统中任何地方运行。
     voidflush_scheduled_work(void);
 
    延时执行一个任务
     intschedule_delayed_work(struct delayed_struct *work, unsigned longdelay);
 
    从一个工作队列中去除入口;
     intcancel_delayed_work(struct delayed_struct *work);
 
测试例子  
voidmyfunc(struct work_struct*ws);
DECLARE_WORK(mywork,myfunc);               //定义
 
 
void myfunc(struct work_struct*ws)
{
   printk(KERN_ALERT "myfunccurrent->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunccurrent->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunccurrent->pid %d\n",current->pid);
   ssleep(1);
}
 
在加载模块时调用
schedule_work(&mywork);
printk(KERN_ALERT "main current->pid %d\n" ,current->pid);
 
 
在卸载模块的时调用
cancel_delayed_work(&mywork);
测试结果
输出的pid
main current->pid  2883                                               
myfunc current->pid 4                                                 
myfunc current->pid 4                             
myfunc current->pid 4 
 
[root@fontlose module]# ps                                             
  PID USER      VSZ STAT COMMAND                                       
    1 root     2108 S   init                                          
    2 root       0 SW   [ksoftirqd/0]                                  
    3 root       0 SW   [watchdog/0]                                   
    4 root       0 SW<  [events/0]  
 
myfunc运行在pid为4的进程中,查看pid为4的进程为events/0,使用内核提供的共享列队,列队是保持顺序执行的,做完一个工作才做下一个,如果一个工作内有耗时大的处理如阻塞等待信号或锁,那么后面的工作都不会执行。如果你不喜欢排队或不好意思让别人等太久,那么可以创建自己的工作者线程,所有工作可以加入自己创建的工作列队,列队中的工作运行在创建的工作者线程中。
 
2.使用自定义列队 
创建工作列队使用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,structdelay_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);
 
主要测试代码
void myfunc(struct work_struct*ws);
 
struct workqueue_struct *wqueue;
DECLARE_WORK(mywork,myfunc);  
 
void myfunc(struct work_struct*ws)
{
   printk(KERN_ALERT "myfunc1 current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc2 current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc3 current->pid %d\n",current->pid);
   ssleep(1);
}
 
在模块加载是执行
 wqueue=create_workqueue("myqueue");
 queue_work(wqueue,&mywork); 
 printk(KERN_ALERT "main current->pid %d\n" ,current->pid);
 
 
测试结果
main current->pid  1010          
                                    
myfunc 1 current->pid 1016                                             
myfunc 2 current->pid 1016                         
myfunc 3 current->pid 1016  
 
ps
....
1016 root        0 SW< [myqueue/0] 
 
可见函数运行在pid为1016的进程中,ps查看进程名为myqueue/0.
 
 
实例说明
1.使用自定义工作队列
 
//=========
#include>linux /kernel.h>
#include>linux/module.h
#include>linux/proc_fs.h>
#include>linux/workqueue.h>
#include>linux/sched.h>
#include>linux/init.h>
#include>linux/interrupt.h>
#include>linux/delay.h>
struct workqueue_struct *test_wq;  
struct delayed_work test_dwq;  
  
void delay_func(struct work_struct *work); 
  
void delay_func(struct work_struct *work) 
{  
    int i; 
  
    printk(KERN_INFO "Myname is delay_func!\n");  
    for (i = 0; i < 3;i++) {  
       printk(KERN_ERR "delay_fun:i=%d\n", i); 
       msleep(1000);  
    } 
}  
  
static int __init example_init(void)  
{  
    int i; 
    int ret; 
  
    test_wq =create_workqueue("test_wq");  
    if (!test_wq) { 
       printk(KERN_ERR "No memory for workqueue\n"); 
       return 1;    
    } 
    printk(KERN_INFO "CreateWorkqueue successful!\n");  
  
   INIT_DELAYED_WORK(&test_dwq, delay_func); 
     
    ret =queue_delayed_work(test_wq, &test_dwq, 5000); 
    printk(KERN_INFO "firstret=%d!\n", ret);  
     
    for (i = 0; i < 3;i++) {   
       printk(KERN_INFO "Example:ret= %d,i=%d\n", ret,i);  
       msleep(100);  
    } 
  
    ret =queue_delayed_work(test_wq, &test_dwq, 0); 
    printk(KERN_INFO "secondret=%d!\n", ret);  
  
    return 0; 
}  
  
static void __exit example_exit(void)  
{  
    int ret; 
    ret =cancel_delayed_work(&test_dwq);  
   flush_workqueue(test_wq);  
   destroy_workqueue(test_wq);  
    printk(KERN_INFO"Goodbay! ret=%d\n", ret);  
}  
module_init(example_init);  
module_exit(example_exit);  
MODULE_LICENSE("GPL");  
运行结果:
kernel: Create Workqueue successful!  
kernel: first ret=1!  
kernel: Example:ret= 1,i=0  
kernel: Example:ret= 1,i=1  
kernel: Example:ret= 1,i=2  
kernel: second ret=0!  
kernel: Goodbay! ret=1  
kernel: Create Workqueue successful! 
说明将任务添加到工作队列后,如果工作队列还在执行该任务,则queue_delayed_work()返回1,否则返回0,如上实例所述;
主线程delay_wq将任务添加到工作队列后,使得工作队列在延迟delay后执行函数delay_func(),而delay_wq线程继续执行;
 
2.使用内核工作队列实例

#include>linux/module.h>
#include>linux/init.h>
#include>linux/kernel.h>
#include>linux/net.h>
#include>net/sock.h>
#include>linux/in.h>
#include>linux/types.h>
#include>linux/kthread.h>
#include>linux/wait.h>
#include>linux/skbuff.h>
#include>linux/string.h>
#include>asm-generic/unaligned.h>
#include>linux/sysctl.h>
#include>linux/netfilter.h>
#include>linux/netfilter_ipv4.h>
#include>asm/checksum.h>
#include>linux/ip.h>
#include>linux/workqueue.h>

#define err(msg) printk(KERN_INFO "%s failed.\n", msg)

static void defense_work_handler(struct work_struct *work);

static DECLARE_DELAYED_WORK(defense_work, defense_work_handler);

static void defense_work_handler(struct work_struct *work)
{
    printk(KERN_INFO "defense_work_handler function.\n");
}

static int __init main_init(void)
{
    schedule_delayed_work(&defense_work, 3 * HZ);

    return 0;
}

static void __exit main_exit(void)
{
    cancel_delayed_work_sync(&defense_work);
}

module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");

【转】工作队列学习