自学linux驱动从入门到放弃(十四)阻塞与非阻塞--等待队列

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被唤醒。

 

 

おすすめ

転載: blog.csdn.net/weixin_44948595/article/details/121991942