In the previous section, we talked about how to implement the tasklet small task mechanism
http://blog.csdn.net/morixinguan/article/details/69666935
In this section, we implement the interruption of the lower half of the work queue:
Before writing this demo, we need to understand the data structure of the work queue and the API.
Header files to be included:
#include <linux/workqueue.h>
Basic data structure:
Of course, if you need to wait for a certain time to execute the work queue, you can apply for a kernel timer with the following structure://Work queue structure struct work_struct { atomic_long_t data; //Link List Processing struct list_head entry; //Work Processing Function work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
Generally, don't use work queues easily, because whenever a work queue is created, the kernel creates a kernel thread for the work queue.//Assign time for work queue to execute struct delayed_work { //Initialization struct work_struct work; //kernel timer struct timer_list timer; };
Task queue is located in the process context, which is different from soft interrupt and tasklet. Delay and sleep operations are allowed in the work queue, while soft interrupt and tasklet are located in the interrupt context. Sleep and delay operations are not allowed.
Refer to the difference between the task queue and tasklet written by the blogger I forwarded:
http://blog.csdn.net/morixinguan/article/details/69666642
Work queue is another form of putting work behind execution, which is different from the tasklet discussed earlier. Work queues can defer work to a kernel thread, which means that the second half can be executed in the process context. In this way, the code executed through the work queue can take all the advantages of the process context. Most importantly, the work queue is allowed to be rescheduled or even sleep.
Then, under what circumstances work queues are used and under what circumstances tasklet is used. If delayed tasks require sleep, select the work queue; if delayed tasks do not require sleep, select tasklet. In addition, if you need to use an entity that can be rescheduled to perform your lower processing, you should also use the work queue. It is the only mechanism that can be implemented in the lower half of the process context, and only it can sleep. This means that it can be very useful when large amounts of memory are needed, semaphores are needed, and blocking I/O operations are needed. If you don't need a kernel thread to defer execution, consider tasklet.
Next let's look at what API s need to be used:
If you create a queue, you will have a kernel thread. Generally, it is not easy to create a queue. Located in the process context - > Sleeping Definition: struct work_struct work; Initialization: INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work)); Define and initialize: DECLARE_WORK(name, void (*func)(struct work_struct *work)); =========================================================== Dispatch: int schedule_work(struct work_struct *work); Return 1 successful, 0 has been added to the queue Delay scheduling: int schedule_delayed_work(struct work_struct *work, unsigned long delay); =========================================================== Create new queues and new worker threads: struct workqueue_struct *create_workqueue(const char *name); Schedule the specified queue: int queue_work(struct workqueue_struct *wq, struct work_struct *work); Delay scheduling specified queue: int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay); Destroy the queue: void destroy_workqueue(struct workqueue_struct *wq);
After compiling the program, put zImage on the board:#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/fb.h> #include <linux/backlight.h> #include <linux/err.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <linux/timer.h> /*timer*/ #include <asm/uaccess.h> /*jiffies*/ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/workqueue.h> struct tasklet_struct task_t ; struct workqueue_struct *mywork ; //Define a work queue structure struct work_struct work; static void task_fuc(unsigned long data) { if(in_interrupt()){ printk("%s in interrupt handle!\n",__FUNCTION__); } } //Work queue processing function static void mywork_fuc(struct work_struct *work) { if(in_interrupt()){ printk("%s in interrupt handle!\n",__FUNCTION__); } msleep(2); printk("%s in process handle!\n",__FUNCTION__); } static irqreturn_t irq_fuction(int irq, void *dev_id) { tasklet_schedule(&task_t); //Scheduling schedule_work(&work); if(in_interrupt()){ printk("%s in interrupt handle!\n",__FUNCTION__); } printk("key_irq:%d\n",irq); return IRQ_HANDLED ; } static int __init tiny4412_Key_irq_test_init(void) { int err = 0 ; int irq_num1 ; int data_t = 100 ; //Create new queues and new worker threads mywork = create_workqueue("my work"); //Initialization INIT_WORK(&work,mywork_fuc); //Scheduling specified queues queue_work(mywork,&work); tasklet_init(&task_t,task_fuc,data_t); printk("irq_key init\n"); irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2)); err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1"); if(err != 0){ free_irq(irq_num1,(void *)"key1"); return -1 ; } return 0 ; } static void __exit tiny4412_Key_irq_test_exit(void) { int irq_num1 ; printk("irq_key exit\n"); irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2)); //Destroy a work queue destroy_workqueue(mywork); free_irq(irq_num1,(void *)"key1"); } module_init(tiny4412_Key_irq_test_init); module_exit(tiny4412_Key_irq_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("YYX"); MODULE_DESCRIPTION("Exynos4 KEY Driver");
We can see that when we press the button, we enter the external interrupt service function. At this time task_fuc is called first, then mywork_fuc is called, and the information in mywork_fuc is printed. From this point, we verify by program that the work queue is in the process context, not the interrupt context, and tasklet is in the process context. There are differences.