Driver of a blocking character device
[Overview]
First, it is clear that whether you are sleeping, sleep, blocking, or hanging, processes are essentially put on the waiting queue.
[Sleep implementation]
Sleep is processed by waiting for the queue. A waiting queue is a simple linked list composed of processes waiting for certain events. The kernel uses wake_queue_head_t to represent the waiting queue. The waiting queue can be created through declare_waitqueue () or dynamically by init_waitqueue_head. The process puts itself in the waiting queue and sets it to unexecutable. When an event related to the waiting queue occurs, the process on the queue is awakened. In order to avoid competition conditions, there is no leakage in the implementation of sleep and wakeup.
For sleep, the kernel provides some simple interfaces. But those interfaces will bring about competition conditions: it may cause the process to start to sleep after the judgment condition is true, which will cause the process to sleep indefinitely, the recommended sleep operation in the kernel is complicated:
Define_wait (wait); add_wait_queue (Q, & wait); While (! Condition) {prepare_to_wait (& Q, & wait, task_interruptible); If (signal_pending (current)/* processing signal */schedule ();} finish_wait (& Q, & wait );
1) Call macro define_wait () to create a waiting queue item;
2) Call add_wait_queue () to add yourself to the queue. The queue will wake up when the conditions for waiting by the process are met. Of course, we must write relevant code elsewhere, when an event occurs, perform the wake_up () operation on the waiting queue;
3) Call the prepare_to_wait () method to change the process status to task_interruptible or task_uninterruptible. And the function will return the process home to the waiting queue if necessary, which is required in the next loop traversal;
4) if the status is set to task_interruptible, the signal will wake up the process. This is the so-called pseudo-wakeup, so check and process the signal;
5) when the process is awakened, it checks again whether the condition is true. If yes, it will exit the loop; if not, it will call schedule () again and repeat this step;
6) when the conditions are met, the process sets itself as task_running and calls the finish_wait () method to remove itself from the waiting queue;
If you need to understand the implementation of these functions, you can refer to the http://edsionte.com/techblog/archives/1854
[A blocking FIFO instance]
I have an understanding of the above sleep implementation theory. It is not difficult to look at the following code.
[FIFO struct]
struct simple_chrdev_fifo { structcdev cdev; charfifo[MAX_SIZE]; intfifo_size; intcurrent_pos; /* read and writeposition in fifo */ structsemaphore sem; /* semaphore */ wait_queue_head_treadq, writeq; /* read queue andwrite queue */};
[Blocking read method]
static ssize_t simple_read(struct file*filp, char __user *buf, size_t count, loff_t*f_pos){ structsimple_chrdev_fifo *dev = filp->private_data; intret = 0; DEFINE_WAIT(wait); if(down_interruptible(&dev->sem)) return-ERESTARTSYS; add_wait_queue(&dev->readq,&wait); while(dev->current_pos == 0) { up(&dev->sem); if(filp->f_flags & O_NONBLOCK) return- EAGAIN; prepare_to_wait(&dev->readq,&wait, TASK_INTERRUPTIBLE); if(signal_pending(current)) { ret = - ERESTARTSYS; gotoout2; } schedule(); if(down_interruptible(&dev->sem)) { ret= -ERESTARTSYS; gotoout2; } } if(count > dev->current_pos) count= dev->current_pos; if(copy_to_user(buf, (void *)(dev->fifo), count)) { ret= -EFAULT; gotoout; } dev->current_pos-= count; ret= count; wake_up_interruptible(&dev->writeq);out: up(&dev->sem);out2: finish_wait(&dev->readq,&wait); returnret;}
[Module initialization function]
static int __initsimple_chrdev_fifo_init(void){ …… /* * initialze wait queue */ init_waitqueue_head(&dev->readq); init_waitqueue_head(&dev->writeq); init_MUTEX(&dev->sem); ……}
Use the init_waitqueue_head macro to initialize the waiting read queue header and waiting for write queue header;
Only important code is posted here. If you need complete code, please send it to me.