Linux设备阻塞访问-等待队列
1、概念
a) 阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再执行
b) 被挂起的进程进入休眠状态,从调度器的运行队列移走,直到条件等待的条件被满足时进程再度被唤醒
2、需要阻塞访问的应用
a) 当应用程序使用read()或者write()等系统调用时,若设备的资源无法获取,而用户又希望等待到设备有数据自行读取或写入(阻塞方式访问数据)后才返回
b) 优点:阻塞时进程会进入休眠状态,将CPU使用权交个其他进程
3、阻塞访问技术-等待队列
a) 基础数据结构-队列
b) 与调度机制紧密结合
c) 能够实现内核中的异步事件通知机制
d) 等待队列可用来同步对系统资源的访问
4、等待队列基本操作
a) 定义等待队列头
wait_queue_head_t xxx_q;
b) 初始化等待队列头
init_waitqueue_head(&xxx_q);
快捷操作:DECLARE_WAIT_QUEUE_HEAD(xxx_q):定义并初始化一个名为xxx_q的等待队列
c) 添加等待队列——将等待队列wait添加到等待队列头q指向的等待队列链表中
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
d) 删除等待队列——将等待队列wait从等待队列头q指向的等待队列链表中删除
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
e) 等待事件,condition 为布尔类型
wait_event(wq, condition);——进程睡眠等待,直到条件为真;不能被信号打断,既进 程进入TASK_UNINTERRUPTIBLE状态
wait_event_timeout(wq, condition, timeout);——等待条件为真既返回,超时也返回
wait_event_interruptible(wq, condition)——可被信号打断;既进程进入
TAKS_INTERRUPTIBLE状态
f) 唤醒队列
wake_up(wait_queue_t *q)---从等待队列q中唤醒状态为:
TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,
TASK_KILLABLE 的所有进程
wake_up_interruptible(wait_queue_t *q)--从等待队列q中唤醒
状态为TASK_INTERRUPTIBLE 的进程
5、程序设计流程
a) 定义等待队列头
b) 初始化等待队列
c) 在设备操作中进入等待队列,让进程睡眠,直到唤醒
d) 在某个条件成立时唤醒等待队列
按键驱动程序阻塞访问设计
/******************************************
author : hntea
date: 2016/3/16
function :
1.检测按键中断
2.在中断底半部交个tasklet处理
3.增加设备阻塞访问,当按键按下时唤醒等待队列
******************************************/
#include "key_hard.h"
/******************************************
定时器操作
******************************************/
struct timer_list key_timer;
static void key_do_timer(unsigned long data)
{
printk("I am key1_task -------runing timer work\n");
}
/******************************************
函数名: key_open
函数功能:文件操作
******************************************/
int key_open(struct inode *inode, struct file *filp)
{
key_hardinit();
return 0;
}
/******************************************
函数名: key_read
函数功能:阻塞访问测试
******************************************/
wait_queue_head_t key_queue;
static int key_val = 0;
ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *lp)
{
int ret = 0;
/*等待直到有数据才读取数据*/
wait_event(key_queue,key_val);
/*将数据传递到用户空间*/
ret = copy_to_user(buf,&key_val,4);
key_val = 0; /*条件要记得清楚,要不下次读取就没有阻塞效果啦*/
return ret;
}
/******************************************
函数名:
函数功能:文件操作
******************************************/
struct file_operations key_fop =
{
.read = key_read,
.open = key_open,
};
struct miscdevice key=
{
.minor = DEV_MINIR ,
.name = "Key",
.fops = &key_fop,
};
/******************************************
中断底半部用任务实现
******************************************/
/*定义 task */
struct tasklet_struct key1_task;
struct tasklet_struct key2_task;
void key1_taskfun(unsigned long x)
{
int ret = 0;
/*3初始化定时器*/
init_timer(&key_timer);
key_timer.function = key_do_timer;
add_timer(&key_timer);
printk("I am key1_task ------- My argument is:%ld\r\n",x);
ret = mod_timer(&key_timer, jiffies + HZ); /*1S后执行超时函数*/
}
void key2_taskfun(unsigned long x)
{
printk("I am key2_task ------- My argument is:%ld\r\n",x);
printk("I am key2_task ------- I try to wake up key_queue\n");
key_val = 4;
printk("Kernel->key_val is: %d\r\n",key_val);
wake_up(&key_queue);
}
/*关联结构体和服务函数*/
DECLARE_TASKLET(key1_task,key1_taskfun,1);
DECLARE_TASKLET(key2_task,key2_taskfun,2);
/******************************************
函数功能:中断服务函数入口
******************************************/
static irqreturn_t key1_interrupt (int irq, void *dev_id)
{
/*1、顶部,硬件操作*/
/*2、底部,交个工作队列来处理*/
tasklet_schedule(&key1_task);
return 0;
}
static irqreturn_t key2_interrupt (int irq, void *dev_id)
{
/*1、顶部,硬件操作*/
/*2、底部,交个工作队列来处理*/
tasklet_schedule(&key2_task);
return 0;
}
/**********************************
函数名:
函数功能:初始化函数
**********************************/
static int keyInit(void)
{
int ret = 0;
/*1、注册设备文件*/
ret = misc_register(&key);
/*2、注册中断号*/
ret = request_irq(IRQ_EINT0,key1_interrupt,IRQF_TRIGGER_FALLING, "key1", 0);
printk("ret val is:%d\n",ret);
ret = request_irq(IRQ_EINT1,key2_interrupt,IRQF_TRIGGER_FALLING, "key2", 0);
printk("ret val is:%d\n",ret);
/*3、初始化等待队列*/
init_waitqueue_head(&key_queue);
return 0;
}
static void keyExit(void)
{
int ret = 0;
/*注销中断*/
free_irq(IRQ_EINT0, 0);
free_irq(IRQ_EINT1,0);
/*注销混杂设备*/
ret = misc_deregister(&key);
/*注销设备*/
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hntea");
module_init(keyInit);
module_exit(keyExit);
驱动测试
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#define PATH "/dev/Key"
int main(void)
{
int ret = 0;
int fd = 0;
int key_val=0;
if( (fd = open(PATH,O_RDWR)) < 0 )
printf("UserSpace ----> No such file!\n");
ret = read(fd,&key_val,4);
printf("\r\nUserSpace ---->key_val is: %d\n",key_val);
close(fd);
return ret;
}