【摘要】 在工作队列里,我们把推后执行的任务叫做工作(work),描述它的数据结构为work_struct,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,而工作线程就是负责执行工作队列中的工作。系统有默认的工作者线程,自己也可以创建自己的工作者线程。
1. 内核工作队列 工作队列常见的使用形式是配合中断使用,在中断的服务函数里无法调用会导致休眠的相关函数代码,有了工作队列机制以后,可以将需要执行的逻辑代码放在工作队列里执行,只需要在中断服务函数里触发即可,工作队列是允许被重新调度、睡眠。
在工作队列里,我们把推后执行的任务叫做工作(work),描述它的数据结构为work_struct,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,而工作线程就是负责执行工作队列中的工作。系统有默认的工作者线程,自己也可以创建自己的工作者线程。
2. 相关函数、结构介绍 2.1 工作结构定义文件:workqueue.h (linux-3.5\include\linux)原型:struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; /* 工作函数指针 */ #ifdef config_lockdep struct lockdep_map lockdep_map; #endif };在工作结构体里,只需要关心一个成员函数:work_func_t func;这个成员函数是一个函数指针,指向工作函数的指针;内核使用这个结构来描述一个工作,一个工作简单理解就是对应于一个函数,可以通过内核调度函数来调用work_struct中func指针所指向的函数。 2.2 工作函数介绍定义文件 workqueue.h (linux-3.5\include\linux)函数原型 typedef void (*work_func_t)(struct work_struct *work);功能 这是指向工作函数地址的函数指针,编写一个工作的函数。参数 struct work_struct *work,这个参数,指向struct work_struct结构变量本身。 示例:
struct work_struct work;init_work(&work, work_func);初始化一个work结构,work_func工作函数的参数就是指向work结构。 2.3 初始化宏1)初始化一个work结构:init_work(_work, _func) _work: struct work_struct work结构指针。_func:用来填充work_struct work结构的fun成员,就是工作函数指针。2)共享工作队列调度宏:schedule_work(_work)它也是一个宏,作用是调度一个工作_work。_work:要调度工作的结构指针;示例:schedule_work(&work) 2.4 使用共享工作队列的步骤1)定义一个工作结构变量struct work_struct work; 2)初始化工作结构(重点func成员)。先编写一个工作函数:void work_func(struct work_struct * dat){ printk(“%p:”,dat); ……}初始化work:init_work(&work, work_func);3)在适当的地方调度工作如果工作用于中断底部代码,则在中断顶部调度。 schedule_work(&work);不是马上执行,而是等待cpu空闲才执行work_func。 3. 案例代码 3.1 共享工作队列-按键驱动 下面这份代码是在一个按键驱动代码,在按键中断服务函数里调度共享队列,最终在工作函数里完成按键值的检测打印。工作队列采用的是共享工作队列。
#include #include #include #include #include #include #include #include #include static struct work_struct work;static struct m_key_info *key_info_p=null;/*存放按键的信息*/struct m_key_info{ int gpio; char name[50]; int val; int irq;};struct m_key_info key_info[]={ {exynos4_gpx3(2),key_irq_1,0x01}, {exynos4_gpx3(3),key_irq_2,0x02}, {exynos4_gpx3(4),key_irq_3,0x03}, {exynos4_gpx3(5),key_irq_4,0x04},};/*工作函数*/static void key_work_func(struct work_struct *work){ msleep(50); //udelay(n); //mdelay(n); //msleep(unsigned int msecs); if(gpio_get_value(key_info_p->gpio)==0) //判断按键是否按下 { printk(按键值:%#x\n,key_info_p->val); } else { printk(按键值:%#x\n,key_info_p->val|0x80); }}/*中断服务函数*/static irqreturn_t key_irq_handler(int irq, void *dev){ key_info_p=(struct m_key_info*)dev; /*调度工作----工作结构体添加到系统共享工作队列里*/ schedule_work(&work); return irq_handled;}static int __init tiny4412_interrupt_drv_init(void){ /*初始化工作*/ init_work(&work,key_work_func); int i; for(i=0;i 3.2 自定义工作队列-按键驱动 工作队列除了可以使用内核共享队列以外,也可以自己创建队列,下面这份代码就演示如何自己创建队列,并完成初始化、调用。代码原型还是一份按键驱动代码,与上面代码相比,加了字符设备节点注册,替换系统共享工作队列为自定义的工作队列。
#include #include #include /*杂项设备相关结构体*/#include /*文件操作集合头文件*/#include /*使用copy_to_user和copy_from_user*/#include /*使用io端口映射*/#include #include /*设备*/#include /*标准字符设备--分配设备号*/#include /*ioctl操作*/#include /*注册中断相关*/#include /*中断边沿类型定义*/#include /*中断io口定义*/#include /*内核定时器相关*/#include /*等待队列相关*/#include /*等待队列相关*/#include /*poll机制相关*/#include /*自旋锁相关*/#include /*自旋锁相关*/#include /*原子操作相关*/#include /*原子操作相关*/#include /*延时函数*/#include #include /*信号相关头文件*/#include /*工作队列相关*//*---------------------------------------------------- 创建自己的工作队列creator_workqueue测试-----------------------------------------------------*//*定义ioctl的命令*/#define cmd_ledon _io('l',1) //无方向 --开灯#define cmd_ledoff _io('l',0) //无方向 ---关灯/*定义设备号注册相关*/static dev_t keydev; //存放设备号static struct cdev *keycdev; //定义cdev结构体指针static struct class *cls; //定义类结构体指针/*定义按键中断相关*/static unsigned int irq_buff[4]; /*存放中断编号*/static int key_value=0; /*存放按键按下的键值*//*定时器相关*/struct timer_list my_timer;/*全局标志*/static int poll_flag=0;struct mutex ; /* 互斥锁 *//*等待队列相关*/static declare_wait_queue_head(wait);/*初始化等待队列头*/static int condition=0; /*唤醒队列的条件-为假休眠-为真唤醒*//*异步通知助手相关*/static struct fasync_struct *myfasync; /*信号量*/static define_semaphore(name_sem);/*内核工作队列相关结构体*/static struct work_struct my_work;/*延时工作队列相关结构体*/static struct delayed_work my_delay_work;/*创建自己的工作队列相关*/struct workqueue_struct *my_work_queue;struct buttons_data{ char key_name[10]; /*按键的名字*/ char key; /*按键值*/ int gpio; /*gpio口编号*/};/*工作队列的处理函数*/static void my_work_func(struct work_struct *work){ static int count=0; printk(\n\n用户创建的系统共享工作队列调度成功%d 次\n\n,count++);}/*结构体整体赋值*/static struct buttons_data key_interrupt[4]={ {key1,0x01,exynos4_gpx3(2)}, {key2,0x02,exynos4_gpx3(3)}, {key3,0x03,exynos4_gpx3(4)}, {key4,0x04,exynos4_gpx3(5)},};/*按键中断服务函数*/static irqreturn_t irq_handler_function(int irq,void * dat){ struct buttons_data *p =(struct buttons_data *)dat; /*强制转换*/ if(!gpio_get_value(p->gpio)) { key_value=p->key; /*获取按下按键值*/ } else { key_value=p->key|0x80; /*获取松开按键值*/ } mod_timer(&my_timer,jiffies+1); /*修改超时时间*/ return irq_handled;}/*定时器中断服务函数*/static void timer_function(unsigned long data){ printk(按键值读取成功!!0x%x--->!\n,key_value); /*添加延时工作到系统工作队列中等待执行*/ // schedule_delayed_work(&my_delay_work,hz*5); //queue_work(my_work_queue,&my_work); /*调度共享工作队列*/ queue_delayed_work_on(-1,my_work_queue,&my_delay_work,hz*5);}static int key_open(struct inode *my_inode, struct file *my_file){ unsigned char i; for(i=0;iok\n); break; case cmd_ledoff: dat=200; copy_to_user(argv,&dat,4); printk(ledoff_----->ok\n); break; } return 0;}/*poll--*/unsigned int my_poll(struct file *my_file, struct poll_table_struct * p){ /*唤醒休眠的进程*/ poll_wait(my_file,&wait,p);/*添加等待队列--不是立即休眠*/ printk(8888\n); if(condition==1) { printk(drive----poll ----ok!\n); condition=0; /*清除标志*/ return pollin; /*返回事件*/ } return 0; /*返回事件*/ }/*异步通知助手*/int key_fasync(int fd, struct file *my_file,int on) //异步通知{ int error; printk(驱动层收到的文件描述符:%d\n,fd); error=fasync_helper(fd,my_file,on,&myfasync); printk(驱动层异步通知结构体文件描述符:%d\n,myfasync->fa_fd); return error;}static int key_release(struct inode *my_inode, struct file *my_file){ int i; //释放中断 for(i=0;i__file__=%s __line__=%d\n,__file__,__line__); return 0;}//kern_emergstatic void __exit key_exit(void){ device_destroy(cls,keydev); //注销设备节点 class_destroy(cls); //注销分配的类 cdev_del(keycdev); //注销cdev结构体 unregister_chrdev_region(keydev,1); //注销设备 kfree(keycdev); //释放结构体 printk(key drive exit ok!! -->__file__=%s __line__=%d\n,__file__,__line__);}//export_symbol(key_init);module_init(key_init1); /*驱动入口*/module_exit(key_exit); /*驱动出口*/module_license(gpl);(key_info)>(key_info)/sizeof(key_info[0]);i++)>
专访| 奥迪威董事长张曙光:超声波传感器龙头,上市首年更上一层楼
高通骁龙8 Gen 2获得上游Linux支持
重庆,功率半导体新贵!
测量绝缘电阻的正确方法
安防企业助力下 公租房社区迈向“智能+”时代
Linux驱动开发-内核共享工作队列
【机器视觉】欢创播报 | 大疆发布DJI FlyCart 30运载无人机
茶叶加工设备物联网数据采集网关实现茶叶智能生产线监控系统
台工研院MRAM技术,比台积电和三星更稳定
聚碳酸酯(PC) DSC测试玻璃化转变温度
PLC软件系统中常用的编程语言整理
教大家如何计算电阻器自发热影响
无线电力传输技术应用
能耗管理系统简介
AI人脸识别赋能寻亲系统 用技术为破碎家庭保驾护航
一个成功的pcb设计师的传奇经历
三大网络厂商设备怎么选?如何选择交换机?
芯片划片工艺流程及划片工艺关键点解析
随着车联网逐步进入商业化 汽车电子将成为全球汽车产业新的竞争高地
Windows 11和 Windows 10到底有哪些差别