1. 什么是阻塞与非阻塞
当应用程序去调用read或者write函数去对底层的设备进行操作时,如果不能及时的获取资源,那么可以有两种处理方式,阻塞,和非阻塞。
阻塞方式调用时,在调用到对应的函数时,获得结果之前,不会立即返回,在驱动中会将进程挂起,此时该进程不占用CPU资源,等待条件满足时,在合适的位置唤醒进程。
非阻塞方式调用时,在函数不能获得结果之前,该函数不会阻塞当前进程,依然会占用CPU资源去执行其他的逻辑。
2. 等待队列
进程访问设备时,需要等待有特定的事件发生后继续向下运行,如果当前条件不满足,那么需要在驱动中将进程休眠,条件满足时,唤醒进程。
2.1 头文件
#include <linux/wait.h>
#include <linux/sched.h>
2.2 定义并初始化等待队列
2.2.1 方法一
wait_queue_head_t key_wq;
init_waitqueue_head(&key_wq);
2.2.2 方法二
DECLARE_WAIT_QUEUE_HEAD(key_wq);
2.3 等待事件
wait_event_interruptible(wq, condition);
参数 | 描述 |
wq | 等待队列头的名字 |
condition | 等待事件之前,如果该标志为0,则进程进入休眠,否则不休眠 |
2.3.1 所有的等待事件
wait_event(queue, condition) //不可被信号打断
wait_event_interruptible(queue, condition) //可被信号打断
wait_event_timeout(queue, condition, timeout) //不可被信号打断,当condition为负数时立刻返回。如果在睡眠期间,condition为真,则返回剩余的时间,否则直到timeout超时,返回0
wait_event_interruptible_timeout(queue, condition, timeout) //同上,可被信号打断
2.4 唤醒进程
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
成对使用
wake_up | wait_event wait_event_timeout |
wake_up_interruptible | wait_event_interruptible wait_event_interruptible_timeout |
3.编程过程
3.1 阻塞队列编程流程
1.定义并初始化等待队列
static DECLARE_WAIT_QUEUE_HEAD(q_key_wait);
2.初始化condition
int key_val = 0;
3.在适当的位置休眠
wait_event_interruptible(queue, condition);
注:休眠后需要将condition置为'0',不然在中断触发的时候,可能会没有按预想的情况,被异常唤醒。
4.在isr function中唤醒等待队列
wake_up_interruptible(&q_key_wait);
注:唤醒的条件为,condition为 'true' 时,执行该函数进程才会被唤醒。
3.2 伪代码
3.2.1 linux driver
#include <linux/wait.h>
#include <linux/sched.h>
static DECLARE_WAIT_QUEUE_HEAD(q_key_wait);
int key_val = 0;
irqreturn_t key_fun(int irq,void *args) //中断函数
{
printk(KERN_ERR "test fun is action!!\n");
key_val = 3; //唤醒的condition条件 为'true' 时才能被唤醒。
wake_up_interruptible(&q_key_wait); //在这里唤醒队列
return IRQ_HANDLED;
}
ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){
int err;
wait_event_interruptible(q_key_wait, key_val); //在这里休眠
err = copy_to_user(buf,&key_val,1);
printk(KERN_ERR "KEY VAL is %d \n",key_val);
key_val = 0; //休眠后需要将condition置为0
return 0;
}
3.2.2 linux app
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, const char ** argv)
{
int fd;
int val;
fd = open("/dev/aloncharTest",O_RDWR);
if(fd < 0)
{
printf("APP can not open node!\n");
return 0;
}
printf("open /dev/aloncharTest ok!\n");
while(1)
{
read(fd,&val,1);
printf("key value is %d \n",val);
}
close(fd);
return 0;
}
3.2.3 实验
将代码编译后,在开发板上加载驱动模块,后台执行app,执行top命令可以看到app在后台执行,没有占用cpu资源,进程是被挂起的状态
按下按键时,可以看到打印了driver和app里面的log,说明按下按键时,app被唤醒。