白云区建网站/seo策略
在最简单的硬件驱动开发和内容使用当中,使用最简单的阻塞和非阻塞两种方式完成应用和硬件数据交互工作。将阻塞和非阻塞两种方式简单,将两部分内容写在同一块内容当中。
1. 概念
应用程序使用API接口,如open、read等来最终操作驱动,有两种结果--成功和失败。成功,很好处理,直接返回想要的结果;但是,失败,是继续等待,还是返回失败类型呢? 如果继续等待,将进程休眠,那么这类驱动设计就是阻塞式的;如果不等待,返回失败的类型(原因),那么这类驱动的设计就是非阻塞式的。
在应用程序打开驱动文件的时候,可以通过参数向驱动传递使用驱动的方式(阻塞或者非阻塞),通过flags这个参数来传递。当flags中包含“O_NONBLOCK”,就是非阻塞,否则就是阻塞式的。在底层设备驱动代码中,使用相同的标识位进行代码隔离区分,确定不同类型的设备读写方式完成驱动开发。
fd = open("/dev/xxx", O_RDWR | O_NONBLOCK);
2. 应用层
2.1 阻塞
在无法获取数据时,会将当前线程睡眠,知道底层有数据的才会被唤醒返回read读数据结果。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>/* seconddrvtest */
int main(int argc, char **argv)
{int fd;unsigned char key_vals[4];int cnt = 0;fd = open("/dev/buttons", O_RDWR);if (fd < 0){printf("can't open!\n");}while (1){read(fd, key_vals, sizeof(key_vals));if (!key_vals[0] || !key_vals[1] || !key_vals[2] || !key_vals[3]){printf("%04d key pressed: %d %d %d %d\n", cnt++, key_vals[0], key_vals[1], key_vals[2], key_vals[3]);}}return 0;
}
2.2 非阻塞
int main(int argc, char **argv)
{unsigned char key_val;int ret;int Oflags;fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);if (fd < 0){printf("can't open!\n");return -1;}while (1){ret = read(fd, &key_val, 1);printf("key_val: 0x%x, ret = %d\n", key_val, ret);sleep(5); // 非阻塞上面直接返回,这里睡眠一下防止平凡打印}return 0;
}
2.3 驱动实现
以按键驱动为例进行说明,主要是利用互斥锁功能来实现。
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,重新来读。