Linux设备驱动——阻塞型I/O

在Linux驱动中,有时候驱动程序有时候无法立即响应用户的需要,比如:read的时候没有数据返回给用户,或者write的时候缓冲区满了。 在这种情况下驱动程序应该阻塞该进程,将其置于休眠状态直到请求可继续。为了将进程以安全的方式进入休眠,我们需要牢记两条规则:

1.永远不要在原子上下文中休眠。因此驱动程序不能在拥有自旋锁seqlock或者RCU锁时休眠。
2. 当休眠后被唤醒时,我们永远无法知道休眠了多长时间,或者休眠期间发生了什么事情。因此在休眠被唤醒时要重新检查等待的条件是否为真。

1.头文件

#include 	

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

你可能感兴趣的:(Linux驱动)