一、概念
应用程序使用API接口,如open、read等来最终操作驱动,有两种结果--成功和失败。成功,很好处理,直接返回想要的结果;但是,失败,是继续等待,还是返回失败类型呢? 如果继续等待,将进程休眠,那么这类驱动设计就是阻塞式的;如果不等待,返回失败的类型(原因),那么这类驱动的设计就是非阻塞式的。
在应用程序打开驱动文件的时候,可以通过参数向驱动传递使用驱动的方式(阻塞或者非阻塞),通过flags这个参数来传递。当flags中包含“O_NONBLOCK”,就是非阻塞,否则就是阻塞式的。
fd = open("/dev/xxx", O_RDWR | O_NONBLOCK);
二、具体实现
以按键驱动为例进行说明,主要是利用互斥锁功能来实现。
1、实现open函数的阻塞非阻塞功能
static DECLARE_MUTEX(buttons_lock); static int buttons_open(struct inode *inode, struct file *file) { if (file->f_flags & O_NONBLOCK) { if (down_trylock(&buttons_lock)) return -EBUSY; } else { down(&buttons_lock); } ............ return 0; }
open()函数的目的是打开驱动文件/dev/buttons,而通常驱动文件允许打开的进程数量是有限制的,本例中是独占式的,所以需要利用互斥锁。函数down_trylock()和函数down(),分别具有非阻塞和阻塞的特性,所以利用这种特性,很容易实现open()函数的需求。
注意在buttons_close()函数,不要忘记调用up(&buttons_lock)把互斥锁释放。
2、实现read函数的阻塞非阻塞功能
static int buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; if (filp->f_flags & O_NONBLOCK) { if(0 == ev_press) { return -EAGAIN; } } else { /* 如果没有按键动作, 休眠 */ wait_event_interruptible(button_waitq, ev_press); } /* 执行到这里时,ev_press等于1,将它清0 */ ev_press = 0; /* 将按键状态复制给用户,并清0 */ err = copy_to_user(buff, &keys_val, count); return err ? -EFAULT : 0; }
read()函数的目的是来读取/dev/buttons文件,所收到的按键键值。当被按下,中断程序令ev_press置1,读取完之后清0。
显然,如果已经被按下,已经存入了键值,那么read函数很容易成功返回。但是,如果没有按键按下,这个时候阻塞的处理就是继续等待,将进程休眠;而非阻塞则是返回-EAGAIN,重新来读。
参考资料:韦东山linux教学视频