在Linux驱动中,有时候驱动程序有时候无法立即响应用户的需要,比如:read的时候没有数据返回给用户,或者write的时候缓冲区满了。 在这种情况下驱动程序应该阻塞该进程,将其置于休眠状态直到请求可继续。为了将进程以安全的方式进入休眠,我们需要牢记两条规则:
1.永远不要在原子上下文中休眠。因此驱动程序不能在拥有自旋锁、seqlock或者RCU锁时休眠。
2. 当休眠后被唤醒时,我们永远无法知道休眠了多长时间,或者休眠期间发生了什么事情。因此在休眠被唤醒时要重新检查等待的条件是否为真。
1.头文件
#include <linux/wait.h>
2.数据结构
wait_queue_head_t;
3.初始化
/*静态初始化*/
DECLARE_WAIT_QUEUE_HEAD(name);
/*动态初始化*/
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
4.休眠
/*非中断休眠*/
int wait_event(queue, condition);
/*可中断休眠,返回非0值表示被信号中断,驱动程序要返回-ERESTARTSYS*/
int wait_event_interruptible(queue, condition);
/*非中断休眠,最多等待限定时间timeout*/
int wait_event_timeout(queue, condition, timeout);
/*可中断休眠,最多等待限定时间timeout*/
int wait_event_interruptible_timeout(queue, condition, timeout);
上面所有的形式中,queue是等待队列头。注意他通过值传递,而不是通过指针。condition是休眠条件,在该条件为真之前,进程或保持休眠。下面为休眠的一段示例代码:
struct fifo_dev
{
char *buf; // 缓冲区
unsigned long buf_size; // 缓冲区总大小
unsigned long wr; // 写指针
unsigned long rd; // 读指针
struct semaphore sem; // 信号量
wait_queue_head_t rd_qeue; // 读操作等待队列
wait_queue_head_t wr_qeue; // 写操作等待队列
};
static ssize_t my_fifo_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
int read_size = 0;
struct fifo_dev *devp = filp->private_data;
int remain_data = 0;
if (down_interruptible(&devp->sem)) { /*sem P操作*/
return -ERESTARTSYS;
}
while (devp->rd == devp->wr) { /*当前没有数据可读*/
up(&devp->sem);
if (filp->f_flags & O_NONBLOCK) { /*非阻塞读,直接返回*/
return -EAGAIN;
}
if (wait_event_interruptible(devp->rd_qeue, (devp->rd != devp->wr))) {
return -ERESTARTSYS; /*等待被信号唤醒了*/
}
/*继续循环等待数据,先获取锁*/
if (down_interruptible(&devp->sem)) {
return -ERESTARTSYS;
}
}
...
}
5.唤醒
/*唤醒等待在queue上的所有进程*/
void wake_up(wait_queue_head_t * queue);
/*只唤醒执行可中断休眠的进程*/
void wake_up_interruption(wait_queue_head_t * queue);
最后列出完整代码地址:https://github.com/zhaoxd298/Linux_drivers/tree/master/my_fifo