Linux设备驱动一 (1)阻塞型IO及非阻塞型IO
来源:互联网 发布:多级提成的软件 编辑:程序博客网 时间:2024/05/06 04:21
阻塞是指没有获得资源则挂起进程,直到获得资源为止。被挂起的进程进入休眠状态,被调度器的运行队列移走,直到等待条件被满足。
非阻塞是不能进行设备操作时不挂起,或放弃,或反复查询,直到可以进行操作为止。
驱动程序常需要这种能力:当应用程序进行read(),write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应该在设备驱动程序的xxx_read(), xxx_write()等操作中将进程阻直到资源可以获取,以后,应用程序read(),write()等调用返回,整个过程仍然进行了正确的设备访问,用户并没有感知到;若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read(),xxx_write()等操作应立即返回,read(),write()等系统调用也随即被访问。
阻塞不是低效率,如果设备驱动不阻塞, 用户想获取设备资源只能不断查询,消耗CPU资源,阻塞访问时,不能获取资源的进程将进入休眠,将CPU资源让给其他资源。阻塞的进程会进入休眠状态,因此,必须确保有一个地方能唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。
阻塞型IO
首先说明一下等待队列相关的两个函数
wait_event_interruptible(),这个函数先将当前进程的状态设置成 TASK_INTERRUPTIBLE,然后调用schedule(), 而schedule()会将位于TASK_INTERRUPTIBLE状态的当前进程从runqueue 队列中删除。从runqueue队列中删除的结果是,当前这个进程将不再参与调度,除非通过其他函数将这个进程重新放入这个runqueue队列中, 这就是wake_up()的作用了。
由于这一段代码位于一个由condition控制的for(;;)循环中,所以当由shedule()返回时(当然是被wake_up之后,通过其他进程的schedule()而再次调度本进程),如果条件condition不满足,本进程将自动再次被设置为TASK_INTERRUPTIBLE状态,接下来执行schedule()的结果是再次被从runqueue队列中删除。这时候就需要再次通过wake_up重新添加到runqueue队列中。
如此反复,直到condition为真的时候被wake_up. 可见,成功地唤醒一个被wait_event_interruptible()的进程,需要满足:
1)condition为真的前提下,2) 调用wake_up()。
所以,如果你仅仅修改condition,那么只是满足其中一个条件,这个时候,被wait_event_interruptible()起来的进程尚未位于runqueue队列中,因此不会被 schedule。这个时候只要wake_up一下就立刻会重新进入运行调度。
关于wait_event_interruptible的返回值,根据 wait_event_interruptible 的宏定义知:
1) 条件condition为真时调用这个函数将直接返回0,而当前进程不会被 wait_event_interruptible 和从 runqueue队列中删除。
2) 如果要被wait_event_interruptible的当前进程有nonblocked pending signals, 那么会直接返回-ERESTARTSYS(i.e. -512),当前进程不会被wait_event_interruptible 和从runqueue队列中删除。
3) 其他情况下,当前进程会被正常的wait_event_interruptible,并从runqueue队列中删除,进入TASK_INTERRUPTIBLE状态退出运行调度,直到再次被唤醒加入runqueue队列中后而参与调度,将正常返回0。
接下来就一按键中断为例(基于HI3520D)
1、创建等待队列
等待队列是一个存放着等待某个特定事件进程链表。
定义并初始化队列头有两种方法:
1) 静态定义并初始化;定义并初始化一个叫name的等待队列
DECLARE_WAIT_QUEUE_HEAD(name);
2) 分开两步执行。
定义
wait_queue_head_t test_queue;
初始化
init_waitqueue_head(&test_queue);
2、在read函数中就实现了进程休眠,使用了函数 ”wait_evenr_interruptible”
wait_event_interruptible(wq, condition)
使用:
如果condition为真,当前进程不会被 wait_event_interruptible 和从 runqueue队列中删除
返回值:
为真时调用这个函数将直接返回0
3、在中断函数中唤醒休眠,使用了函数 wake_up_interruptible(wait_queue_head_t *queue)”
wait_up_interruptible(&wq);
以下贴出示例代码
创建等待队列和condition
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int event_press = 0;
read函数
static int button_dev_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err = 0;
/* event_press=0, 则表示无按键按下, 此时无数据可读, 进程进入休眠 */
if(!event_press)
{
wait_event_interruptible(button_waitq, event_press);
}
event_press = 0;
P_DEBUG("button_dev_read run!\r\n");
/* 把按键值传会用户空间 */
err = copy_to_user(buff, &key, min(sizeof(key),count));
return err ? -EFAULT : 0;
}
在中断中唤醒
static irqreturn_t button_dev_irq_interrupt(int irqno, void *param)
{
event_press = 1;
wake_up_interruptible(&button_waitq);
}
应用程序代码
fd = open("/dev/gkpollbutton", O_RDWR);
if (fd < 0)
{
printf("error: can't open /dev/gkbutton!\n");
return -1;
}
read(fd, readbuff, 4);
printf("key[0]=%d\r\n",readbuff[0]);
printf("key[1]=%d\r\n",readbuff[1]);
非阻塞型IO
实现非阻塞操作也很简单,判断filp->f_flags中的是否存在O_NONBLOCK标志(标志在定义,并被自动包含),如果有就返回-EAGAIN。
其中O_NONBLOCK是在应用程序打开设备函数中定义,如下;
fd = open("/dev/gkpollbutton", O_RDWR|O_NONBLOCK);
在驱动中增加如下代码,红色部分
static int button_dev_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err = 0;
if(filp->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
/* event_press=0, 则表示无按键按下, 此时无数据可读, 进程进入休眠 */
if(!event_press)
{
wait_event_interruptible(button_waitq, event_press);
}
event_press = 0;
P_DEBUG("button_dev_read run!\r\n");
/* 把按键值传会用户空间 */
err = copy_to_user(buff, &key, min(sizeof(key),count));
return err ? -EFAULT : 0;
}
应用程序代码
fd = open("/dev/gkpollbutton", O_RDWR|O_NONBLOCK);
if (fd < 0)
{
printf("error: can't open /dev/gkbutton!\n");
return -1;
}
err = read(fd, readbuff, 4);
if(err == -1)
{
perror("open");
printf("errno = %d\n", errno);
}
else
{
printf("key[0]=%d\r\n",readbuff[0]);
printf("key[1]=%d\r\n",readbuff[1]);
}
errno是-EAGAIN错误号返回给用户态的
- Linux设备驱动一 (1)阻塞型IO及非阻塞型IO
- linux设备驱动中的阻塞IO和非阻塞IO
- Linux设备驱动中的阻塞与非阻塞IO
- linux设备驱动中的阻塞和非阻塞IO
- linux设备驱动中的阻塞与非阻塞IO
- linux设备驱动--阻塞IO
- linux设备驱动--非阻塞IO与select,poll调用
- linux设备驱动--阻塞IO(续)
- Linux设备驱动中的阻塞与非阻塞IO与并发控制
- linux设备驱动的阻塞与非阻塞的IO操作
- linux设备中的阻塞与非阻塞io
- linux设备驱动--非阻塞IO与select,poll调用 (续1)
- Linux驱动阻塞与非阻塞IO之等待队列
- linux驱动复习之阻塞与非阻塞的IO操作 [一]
- 阻塞、非阻塞IO
- 阻塞型io和非阻塞型io访问
- linux下五种IO模型小结(阻塞IO、非阻塞IO、IO复用、信号驱动式IO、异步IO)
- linux设备驱动中的阻塞与非阻塞(一)
- navicat premium 11.0.19 破解版
- mongodb优化
- iOS开发UI篇—Quartz2D使用(截屏,可以截取view)
- 动态代理使用以及Proxy的内部实现
- filter,map,reduce,apply函数
- Linux设备驱动一 (1)阻塞型IO及非阻塞型IO
- 二维数组
- int(1) 和 int(11)的区别
- Charles的安装以及使用
- Android中view的加载机制(二)
- Codeforces 748E dp
- Windows控制面板命令大全
- ANROID动态加载技术 系列索引
- .NET基础加强第五天(XML文档)