linux驱动学习之工作队列使用

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

       1.工作的定义

    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 = WORK_DATA_INIT(0),    \
	.entry	= { &(n).entry, &(n).entry }, \
	.func = (f)					       \
     }

    先定义中后初始化处理函数
    struct work_struct 
    INIT_WORK(struct work_struct *work, func_t);
    #define INIT_WORK(_work, _func)				\
	do {							                        \
		__INIT_WORK((_work), (_func), 0);		\
	} while (
       在使用带delay的函数或宏时使用DECLARE_DELAYED_WORK定义和INIT_DELAYED_WORK初始化。

      2.使用内核提供的共享列队

     对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
     int schedule_work(struct work_struct *work);

     确保没有工作队列入口在系统中任何地方运行。
     void flush_scheduled_work(void);

     延时执行一个任务
     int schedule_delayed_work(struct delayed_struct *work, unsigned long delay);

     从一个工作队列中去除入口;
     int cancel_delayed_work(struct delayed_struct *work);
 

测试例子         

void myfunc(struct work_struct*ws);
DECLARE_WORK(mywork,myfunc);                  //定义


void myfunc(struct work_struct*ws)
{
   printk(KERN_ALERT "myfunc current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc current->pid %d\n",current->pid);
   ssleep(1);
}

在加载模块时调用
schedule_work(&mywork);
printk(KERN_ALERT "main current->pid  %d\n" ,current->pid);
测试结果

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

       3.使用自定义列队 

         创建工作列队使用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);

主要测试代码

void myfunc(struct work_struct*ws);

struct workqueue_struct *wqueue;
DECLARE_WORK(mywork,myfunc);  

void myfunc(struct work_struct*ws)
{
   printk(KERN_ALERT "myfunc 1 current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc 2 current->pid %d\n",current->pid);
   ssleep(1);
   printk(KERN_ALERT "myfunc 3 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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值