waiting queue
two. Waiting queue basics
When our process accesses the device, it often needs to wait for a specific event to occur before continuing to run. At this time, we need to implement in the driver that the process sleeps when the conditions are not met, and wakes up the process by the kernel when the conditions are met. . Then the waiting queue implements conditional waiting on the event.
<1> Waiting queue head The waiting queue head is the head of a waiting queue. Each process accessing the device is a queue item. When the device is unavailable, the corresponding waiting queue items of these processes should be added to the waiting queue. . The waiting queue head is represented by the structure wait_queue_head_t. This structure is defined in the file include/linux/wait. The content of the structure is as follows:
struct __wait_queue_head {
spinlock_t lock; // 自旋锁
struct list_head task_list; // 链表头
};
typedef struct __wait_queue_head wait_queue_head_t;
The type name is wait_queue_head_t, just remember this.
Define a waiting queue head: wait_queue_head_t test_wq; // Define a waiting queue
head After defining the waiting queue head, it needs to be initialized. You can use the init_waitqueue_head function to initialize the waiting queue head. The function prototype is as follows:
void init_waitqueue_head(wait_queue_head_t *q)
You can also use the macro DECLARE_WAIT_QUEUE_HEAD to complete the definition and initialization of the wait queue head at one time.
DECLARE_WAIT_QUEUE_HEAD (wait_queue_head_t *q);
three. Waiting for queue related functions
<1> init_waitqueue_head macro
Prototype: void init_waitqueue_head(wait_queue_head_t *q)
Function: Dynamically initialize the waiting queue head structure
Parameters:
q is the wait_queue_head_t pointer
<2>wait_event macro
Prototype: wait_event(wq, condition)
Function: Uninterruptible blocking wait, let the calling process enter an uninterruptible sleep state, sleep in the waiting queue until the condition becomes true and wake up by the kernel.
Parameters:
wq : variable of type wait_queue_head_t.
condition is the waiting condition, and can enter sleep only when it is false.
Note: When calling, confirm whether the condition value is true or false. If the calling condition is true, it will not sleep.
<3>wait_event_interruptible macro
Prototype: wait_event_interruptible(wq, condition)
Function: interruptible blocking wait, allowing the calling process to enter an interruptible sleep state until the condition becomes true and is awakened by the kernel or interrupted by a signal.
Parameters:
wq : variable of type wait_queue_head_t.
condition is the waiting condition, when it is false, it can enter sleep and
return: judge whether the condition is true, return if true, otherwise check if the process is awakened by a signal, and return the ERESTARTSYS error code. If the condition is true, return 0
<4> wake_up macro
Prototype: wake_up(x)
function: wake up all dormant processes
Parameters:
x : pointer to the structure of the head of the waiting queue.
<5> wake_up_interruptible macro
Prototype: wake_up_interruptible(x)
Function: wake up interruptible sleep process
Parameters:
x : pointer to the structure of the head of the waiting queue.
practice course
Define and initialize the wait queue:
DECLARE_WAIT_QUEUE_HEADER(key_wq);
int wq_flags = 0;//定义标志位
Interrupt function:
irq_handler_t test_key(int irq, void *args)//中断处理函数
{
value = !value;
wq_flags = 1;
wake_up(&key_wq);//唤醒进程
return IRQ_HANDLED;
}
Code passed to the application layer
int misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
wait_event_interruptible(key_wq,wq_flags);
if(copy_to_user(ubuf,value,sizeof(value))!=0)
{
printk("copy to user error\n");
return -1;
}
wq_flags = 0;
return 0;
}
Experimental result: blocking printing.
work queue
1. Basic Concepts
1. What is a work queue?
A workqueue (workqueue) is one of the mechanisms for implementing interrupt contexts, a form of postponing the execution of work. How is the work queue different from the tasklet mechanism we learned earlier? Tasklet is also a mechanism for implementing interrupt context. The main difference between the two is that tasklets cannot sleep, while workqueues can sleep. So, tasklets can be used to handle more time-consuming things, while work queues can handle very complex and more time-consuming things.
2. The working principle of the workqueue The
Linux system will create a kernel thread during startup. After the thread is created, it will be in the sleep state, and then the thread will always read in the queue to see if there are any tasks, and execute them if there are any. Sleep if not. The implementation mechanism of the work queue is actually very complex, and you only need to understand these basic concept interfaces in the beginner stage. Analogy understanding:
machinery on the assembly line: The Linux system automatically creates one. Many different materials use the same assembly line machine, then this is the concept of shared work queue.
If the current assembly line machinery cannot meet the materials we process, we need to re-customize an assembly line machine. This is the concept of a custom work queue.
Shared work queues have disadvantages:
you don't need to create them yourself, but if the previous work is time-consuming, it will affect the later work. What are the pros and cons of custom work queues?
You need to create it yourself, and the system overhead is high. The advantage is that it will not be affected by other jobs. (Because this assembly line is dedicated to processing this kind of parts.)
two. Work queue related API
Although the implementation mechanism of the work queue is very complex, the use of the work queue is actually to add your own materials to the pipeline and wait for execution.
A specific work (the analogy is the material on the assembly line) is described by the structure work_struct, which is defined in Linux\work\queue.h.
So the first step in using a work queue is to define a work queue.
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
}
In this structure, you only need to pay attention to the func member, which is a function pointer, because the work that needs to be done is written in this function.
<1> Macro DECLARE_WORK Prototype: #define DECLARE_WORK(n, f)
Function: statically define and initialize the work queue.
<2> Macro INIT_WORK Prototype: #define INIT_WORK(_work, _func)
Function: Dynamically define and initialize work structure
Parameters:
work : work queue address;
func : work function
举例:
静态初始化:
struct work_struct test;
在模块的初始化函数中:
INIT_WORK(&test, func) ;
动态初始化:
相当于:DECLARE_WORK(test, func);
<3> schedule_work
prototype: int schedule_work(struct work_struct *work);
Role: schedule work, hang work_struct on the CPU-related work structure queue list, and wait for worker thread processing.
Parameters: _work The address of the work queue;
it should be noted that if the work is scheduled, it will not be executed immediately, but will be added to the shared work queue, and it will be executed when it is his turn.
If we call the same task multiple times, if the last task has not been processed, it is invalid to schedule the same task multiple times.
practice course
Include header files:
#include <linux/workqueue.h>
Define the structure:
struct work_struct key_test;
Initialize in probe:
//初始化
INIT_WORK(&key_test,test);
Interrupt function initialization
void test(unsigned long data){
int i = 100;
while(i--)
printk("test_key is %d\n",i);
}
irq_handler_t test_key(int irq, void *args)//中断处理函数
{
schedule_work(&key_test);
return IRQ_HANDLED;
}
Phenomenon: Press the button to print the value of i 100 times.