目前已经学习两种应用层IO模型的使用
非阻塞:立即返回结果,如果想得到期望的结果,要不停的调用这个方***询),非常耗费资源
阻塞:没有得到真正的数据前,不返回结果。此时,进程进入阻塞(休眠)态,直到有数据唤醒进程,这个过程不耗资源。
PS:linux应用中,大部分的函数接口都是阻塞
驱动程序将进程进入休眠状态的过程
1将当前进程加入到等待队列头中
2将当前进程状态设置成TASK_INTERRUPTIBLE
3让出调度--休眠
就是说,将进程加入一个队列中。如果改变它的状态成TASK_INTERRUPTIBLE,并让CPU不再调度它,那这就是进入阻塞(休眠)态了。在linux驱动开发中,通过一个接口完成上述过程
wait_event_interruptible(wq, condition)
写驱动程序的的步骤
1等待队列头
init_waitqueue_head(wait_queue_head_t *q);
2在需要等待(没有数据)的时候,进行休眠
wait_event_interruptible(wait_queue_head_t wq, condition) // 内部会构建一个等待队列项/节点wait_queue_t
3在一个合适的时候(有数据),会将进程唤醒
wake_up_interruptible(wait_queue_head_t *q)
驱动程序
在驱动模块加载时,也就是初始化函数key_drv_init中,加入 等待列队头。当应用程序调用read接口函数时,使用wait_event_interruptible函数,condition参数为1说明有数据就继续执行,没有则阻塞等待。wake_up_interruptible用来唤醒休眠中的进程。
//key_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/fcntl.h>
irqreturn_t key_irq_handler(int irqno, void *devid);
ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
int key_drv_open (struct inode *, struct file *);
int key_drv_close (struct inode *, struct file *);
#define GPXCON_REG 0x11000C20
#define KEY_ENTER 28
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
};
struct key_event{
int code; // 按键的类型
int value; // 状态
};
struct key_desc{
unsigned int dev_major;
struct class *cls;
struct device *dev;
int irqno;
void *reg_base;
int key_state; //表示是否有数据
struct key_event event;
wait_queue_head_t wq_head;
};
struct key_desc *key_dev;
//static int irqno;
int get_irqno_from_node(void)
{
//从设备树路径,查找节点
struct device_node *np = of_find_node_by_path("/key_int_node");
if(np){
printk("find node ok\n");
}else{
printk("find node failed\n");
}
int irqno = irq_of_parse_and_map(np, 0);
printk("irqno = %d\n", irqno);
return irqno;
}
static int __init key_drv_init(void)
{
int ret;
//对象实例化
key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
//申请主设备号
key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
//创建设备结点
key_dev->cls = class_create(THIS_MODULE, "key_cls");
key_dev->dev = device_create(key_dev->cls, NULL,
MKDEV(key_dev->dev_major,0), NULL, "key0");
//硬件初始化
key_dev->irqno = get_irqno_from_node();
ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"key3_eint10", NULL);
if(ret != 0)
{
printk("request_irq error\n");
return ret;
}
key_dev->reg_base = ioremap(GPXCON_REG, 8);
// 初始化等待队列头
init_waitqueue_head(&key_dev->wq_head);
return 0;
}
static void __exit key_drv_exit(void)
{
iounmap(key_dev->reg_base); //去映射
free_irq(key_dev->irqno, NULL); //释放中断资源
device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
class_destroy(key_dev->cls); //
unregister_chrdev(key_dev->dev_major, "key_drv"); //注销主设备号
kfree(key_dev); //释放结构体内存
}
irqreturn_t key_irq_handler(int irqno, void *devid)
{
printk("-------%s-------------\n", __FUNCTION__);
int value = readl(key_dev->reg_base + 4) & (1<<2);
if(value){// 1
printk("key3 up\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 0;
}else{// 0
printk("key3 pressed\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 1;
}
// 表示有数据,需要去唤醒整个进程/等待队列
wake_up_interruptible(&key_dev->wq_head);
//同时设置标志位
key_dev->key_state = 1;
return IRQ_HANDLED;
}
ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
int ret;
//2,在需要等待(没有数据)的时候,进行休眠
wait_event_interruptible(key_dev->wq_head, key_dev->key_state);
// 表示有数据
ret = copy_to_user(buf, &key_dev->event, count);
if(ret > 0)
{
printk("copy_to_user error\n");
return -EFAULT;
}
// 清除key_dev->event的数据记录
memset(&key_dev->event, 0, sizeof(key_dev->event));
key_dev->key_state = 0;
return count;
}
ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
int key_drv_open(struct inode *inode, struct file *filp)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
int key_drv_close (struct inode *inode, struct file *filp)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");