linux设备驱动之工作队列学习(workqueue)

首先要注意本文的两个概念:(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 = 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初始化。

1.使用内核提供的共享列队
      对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。
      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);


在卸载模块的时调用
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,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.



实例说明

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 "My name 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 "Create Workqueue successful!\n");  

  

    INIT_DELAYED_WORK(&test_dwq, delay_func);  

      

    ret = queue_delayed_work(test_wq, &test_dwq, 5000);  

    printk(KERN_INFO "first ret=%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 "second ret=%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, * 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");


*********************************************************************************

以前内核里对这个函数是这样定义的 #define INIT_WORK(_work, _func, _data),可以理解为INIT_WORK会在你定义的_work工作队列里面增加一个工作任务,该任务就是_func。_func这个任务会需要一些数据作为参数,这个参数就是通过_data传递的。

    而现在看驱动的时候你会发现调用INIT_WORK的时候是只有两个参数,去掉了数据的部分。也许你会问怎么传递data呢,等下会讲述到。其实现在我对这个理解还是比较模糊,希望各位说说你们的理解,顺便也让我理解更透彻一点。看许多驱动模块的时候会发觉work就是一个工作队列,一般是结构体work_struct,主要的目的就是用来处理中断的。比如在中断里面要做很多事,但是比较耗时,这时就可以把耗时的工作放到工作队列。说白了就是系统延时调度的一个自定义函数。
   现在已goodix的触摸屏列举一下步骤:
   1. 在探测函数里goodix_ts_probe初始化
   INIT_WORK(&ts->work, goodix_ts_work_func);//struct work_struct  work,ts是client私有数据结构体
   主要目的就是因为就算没有中断,在第一次开机时也有检测设备,所以这个任务一开始就执行,只不过到后来中断发生后就再执行.
   2. 现在当然轮到static void goodix_ts_work_func(struct work_struct *work)这个工作任务了
   在这个函数里会有一句:
   struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work);
这函数的主要目的就是解决前面所说的我们的data跑哪去了,使用container_of这个函数来求出我们的data的指针
   3.当然你不能忘记注册你的中断函数了
request_irq(client->irq, goodix_ts_irq_handler,pdata->irq_edge ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING,client->name, ts);
所以用一个结构体ts就可以把中断,任务,设备名等全部囊括进来
我理解就差不多是这样的。希望大叔级任务指出不正之处。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
8192CU LINUX驱动/****************************************************************************** * * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * ******************************************************************************/ /* * Public General Config */ #define AUTOCONF_INCLUDED #define RTL871X_MODULE_NAME "92CU" #define DRV_NAME "rtl8192cu" #define CONFIG_USB_HCI 1 #define CONFIG_RTL8192C 1 #define PLATFORM_LINUX 1 //#define CONFIG_IOCTL_CFG80211 1 #ifdef CONFIG_IOCTL_CFG80211 #define CONFIG_CFG80211_FORCE_COMPATIBLE_2_6_37_UNDER //#define CONFIG_DEBUG_CFG80211 1 #endif /* * Internal General Config */ //#define CONFIG_PWRCTRL //#define CONFIG_H2CLBK #define CONFIG_EMBEDDED_FWIMG 1 //#define CONFIG_FILE_FWIMG #ifdef CONFIG_WAKE_ON_WLAN #define CONFIG_WOWLAN 1 #endif //CONFIG_WAKE_ON_WLAN #define CONFIG_R871X_TEST 1 #define CONFIG_80211N_HT 1 #define CONFIG_RECV_REORDERING_CTRL 1 //#define CONFIG_TCP_CSUM_OFFLOAD_RX 1 //#define CONFIG_DRVEXT_MODULE 1 #ifndef CONFIG_MP_INCLUDED #define CONFIG_IPS 1 #ifdef CONFIG_IPS //#define CONFIG_IPS_LEVEL_2 1 //enable this to set default IPS mode to IPS_LEVEL_2 #endif #define SUPPORT_HW_RFOFF_DETECTED 1 #define CONFIG_LPS 1 #define CONFIG_BT_COEXIST 1 //befor link #define CONFIG_ANTENNA_DIVERSITY //after link #ifdef CONFIG_ANTENNA_DIVERSITY #define CONFIG_SW_ANTENNA_DIVERSITY //#define CONFIG_HW_ANTENNA_DIVERSITY #endif #define CONFIG_IOL #else //#ifndef CONFIG_MP_INCLUDED #define CONFIG_MP_IWPRIV_SUPPORT 1 #endif //#ifndef CONFIG_MP_INCLUDED #define CONFIG_AP_MODE 1 #define CONFIG_NATIVEAP_MLME 1 // Added by Albert 20110314 #define CONFIG_P2P 1 #ifdef CONFIG_P2P // Added by Albert 20110812 // The CONFIG_WFD is for supporting the Wi-Fi display //#define CONFIG_WFD 1 // Unmarked if there is low p2p scanned ratio; Kurt //#define CONFIG_P2P_AGAINST_NOISE 1 #define CONFIG_P2P_REMOVE_GROUP_INFO //#define CONFIG_DBG_P2P #endif // Added by Kurt 20110511 //#define CONFIG_TDLS 1 #ifdef CONFIG_TDLS #define CONFIG_TDLS_AUTOSETUP 1 #define CONFIG_TDLS_AUTOCHECKALIVE 1 #endif #ifdef CONFIG_AP_MODE #ifndef CONFIG_NATIVEAP_MLME #define CONFIG_HOSTAPD_MLME 1 #endif #define CONFIG_FIND_BEST_CHANNEL 1 #endif #define CONFIG_SKB_COPY 1//for amsdu #define CONFIG_LED #ifdef CONFIG_LED #define CONFIG_SW_LED #ifdef CONFIG_SW_LED //#define CONFIG_LED_HANDLED_BY_CMD_THREAD #endif #endif // CONFIG_LED #define USB_INTERFERENCE_ISSUE // this should be checked in all usb interface #define CONFIG_GLOBAL_UI_PID #define CONFIG_LAYER2_ROAMING #define CONFIG_LAYER2_ROAMING_RESUME //#define CONFIG_ADAPTOR_INFO_CACHING_FILE // now just applied on 8192cu only, should make it general... //#define CONFIG_RESUME_IN_WORKQUEUE //#define CONFIG_SET_SCAN_DENY_TIMER #define CONFIG_LONG_DELAY_ISSUE #define CONFIG_NEW_SIGNAL_STAT_PROCESS //#define CONFIG_SIGNAL_DISPLAY_DBM //display RX signal with dbm #ifdef CONFIG_IOL #define CONFIG_IOL_LLT #define CONFIG_IOL_MAC #define CONFIG_IOL_BB_PHY_REG #define CONFIG_IOL_BB_AGC_TAB #define CONFIG_IOL_RF_RF90_PATH_A #define CONFIG_IOL_RF_RF90_PATH_B #endif #define CONFIG_BR_EXT 1 // Enable NAT2.5 support for STA mode interface with a L2 Bridge #ifdef CONFIG_BR_EXT #define CONFIG_BR_EXT_BRNAME "br0" #endif // CONFIG_BR_EXT #define CONFIG_TX_MCAST2UNI 1 // Support IP multicast->unicast //#define CONFIG_CHECK_AC_LIFETIME 1 // Check packet lifetime of 4 ACs. /* * Interface Related Config */ //#define CONFIG_USB_INTERRUPT_IN_PIPE 1 #ifndef CONFIG_MINIMAL_MEMORY_USAGE #define CONFIG_USB_TX_AGGREGATION 1 #define CONFIG_USB_RX_AGGREGATION 1 #endif #define CONFIG_PREALLOC_RECV_SKB 1 //#define CONFIG_REDUCE_USB_TX_INT 1 // Trade-off: Improve performance, but may cause TX URBs blocked by USB Host/Bus driver on few platforms. //#define CONFIG_EASY_REPLACEMENT 1 /* * CONFIG_USE_USB_BUFFER_ALLOC_XX uses Linux USB Buffer alloc API and is for Linux platform only now! */ #define CONFIG_USE_USB_BUFFER_ALLOC_TX 1 // Trade-off: For TX path, improve stability on some platforms, but may cause performance degrade on other platforms. //#define CONFIG_USE_USB_BUFFER_ALLOC_RX 1 // For RX path /* * USB VENDOR REQ BUFFER ALLOCATION METHOD * if not set we'll use function local variable (stack memory) */ //#define CONFIG_USB_VENDOR_REQ_BUFFER_DYNAMIC_ALLOCATE #define CONFIG_USB_VENDOR_REQ_BUFFER_PREALLOC #define CONFIG_USB_VENDOR_REQ_MUTEX #define CONFIG_VENDOR_REQ_RETRY //#define CONFIG_USB_SUPPORT_ASYNC_VDN_REQ 1 /* * HAL Related Config */ #define RTL8192C_RX_PACKET_NO_INCLUDE_CRC 1 #define SUPPORTED_BLOCK_IO #define RTL8192CU_FW_DOWNLOAD_ENABLE 1 #define CONFIG_ONLY_ONE_OUT_EP_TO_LOW 0 #define CONFIG_OUT_EP_WIFI_MODE 0 #define ENABLE_USB_DROP_INCORRECT_OUT 0 #define RTL8192CU_ASIC_VERIFICATION 0 // For ASIC verification. #define RTL8192CU_ADHOC_WORKAROUND_SETTING 1 #define DISABLE_BB_RF 0 #define RTL8191C_FPGA_NETWORKTYPE_ADHOC 0 #ifdef CONFIG_MP_INCLUDED #define MP_DRIVER 1 #undef CONFIG_USB_TX_AGGREGATION #undef CONFIG_USB_RX_AGGREGATION #else #define MP_DRIVER 0 #endif /* * Platform Related Config */ #ifdef CONFIG_PLATFORM_MN10300 #define CONFIG_SPECIAL_SETTING_FOR_FUNAI_TV #if defined (CONFIG_SW_ANTENNA_DIVERSITY) #undef CONFIG_SW_ANTENNA_DIVERSITY #define CONFIG_HW_ANTENNA_DIVERSITY #endif #endif #ifdef CONFIG_WISTRON_PLATFORM #endif #ifdef CONFIG_PLATFORM_TI_DM365 #define CONFIG_USE_USB_BUFFER_ALLOC_RX 1 #endif /* * Debug Related Config */ //#define CONFIG_DEBUG_RTL871X #define DBG 0 #define CONFIG_DEBUG_RTL819X #define CONFIG_PROC_DEBUG 1 //#define DBG_IO //#define DBG_DELAY_OS //#define DBG_MEM_ALLOC //#define DBG_IOCTL //#define DBG_TX //#define DBG_XMIT_BUF //#define DBG_TX_DROP_FRAME //#define DBG_RX_DROP_FRAME //#define DBG_RX_SEQ //#define DBG_RX_SIGNAL_DISPLAY_PROCESSING //#define DBG_RX_SIGNAL_DISPLAY_SSID_MONITORED "jeff-ap" //#define DBG_EXPIRATION_CHK //#define DBG_SHOW_MCUFWDL_BEFORE_51_ENABLE //#define DBG_ROAMING_TEST //#define DBG_HAL_INIT_PROFILING #define DBG_MEMORY_LEAK 1 #define DBG_CONFIG_ERROR_DETECT //#define DBG_CONFIG_ERROR_RESET

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值