天天看点

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

在上一章实现了简单的按键驱动,但是我们通过insmod到内核,按下按键以后,查看CPU资源运行情况,发现就是一个简单的按键驱动竟然用了20%多的资源,tiny4412可是一个4核的cpu!!!

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

为神马会出现这种情况,我们来分析一下并且改进它

单纯的用按键测试驱动,并不会出现资源占用过大,但是用了app去测试却发现有这种情况

在app中

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

有一个while函数,里面有一个read函数,无论两个if语句是否成立,都会反复执行循环,调用read函数,所以就会有这样一个情况,系统资源被反复调用read函数浪费

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

所以,我们判断,当有数据的时候才read,没有数据时候就不读,一直阻塞在那里

所以在这里我们用到阻塞模型

代码在上一节代码稍作改动即可!

如何判断没有数据时候就不read呢?

在驱动read函数中我们进行判断,如果有数据就copy_to_user,没有数据就讲该进程上下文休眠

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

我们通过按键描述结构体中的state来判断,1代表按下,有数据。0代表没有按下,没有数据,进行休眠。

所以在按键按下时候,我们需要在中断服务函数irqreturn_t key_handle_t中唤醒进程

中断处理------文件IO模型 (二)阻塞模型 基于tiny4412开发板

在唤醒函数wake_up_interruptible中需要一个参数,等待队列头

在之前的休眠进程函数中我们也用到了等待队列头这个参数,其实这个是相当于将休眠的进程将入到一个队列排队,之后再将这些队列给依次唤醒

所以我们需要在结构体中定义这个等待队列头

当驱动被装在进内核时候就应该进这个等待的队列初始化

改动后的驱动代码如下:

#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
#include<linux/fs.h>
#include <asm/uaccess.h>
#include<linux/interrupt.h> //中断注册注销头文件
#include<linux/gpio.h>   //gpio相关的头文件
#include<linux/cdev.h>
#include <linux/string.h>
#include<linux/sched.h>


struct key_event{
        int code;//按键类型
        int value;//按键状态,按下或者抬起(    0  /  1 )
};

struct key_dsc{
         int major;
         struct cdev *key_cdev;
        struct  class *key_class;
        struct  device *key_dev;
        int irq;//中断号
         unsigned long flag;
        struct key_event my_event;
		wait_queue_head_t key_hand_queue;
		int key_state;
};
#define device_name "qin_key"
#define class_name "qin_class"
#define KEY_ENTER 28

struct key_dsc my_key;


ssize_t key_read(struct file *file, const char __user *buffer, size_t count, loff_t *fpos){
        //  ****************read函数目的是将案按键的数组传到应用层,kbuf[4]   ************

		//没有数据将会休眠进行到这一步,不会再往下执行
 		wait_event_interruptible(my_key.key_hand_queue,my_key.key_state);//将进程休眠,用my_ke中的state状态判断

		//有数据将会继续往下执行
        if(copy_to_user(buffer,&my_key.my_event,sizeof(struct key_event)))//copy_to_user 返回零代表copy失败,1代表成功
                {
                        printk("\n   copy  fail!!   \n");
                }
    
                memset(&my_key.my_event,0,sizeof(struct key_event));
				my_key.key_state=0;//本次数据传送完需要将状态置0,继续休眠
        return count;
}

ssize_t key_write (struct file *file, const char __user *buf, size_t count, loff_t *fpos){
        printk("\n              key_ is write!!         \n");

}


int  key_open (struct inode *inode, struct file *file){
                printk("\n              key_ is open!!          \n");
                                return 0;
}

int key_close(struct inode *inode, struct file *file){
                printk("\n              key_ is close!!         \n");
}

irqreturn_t key_handle_t(int irq,void *dev_id){
        int dn=0;
        dn=gpio_get_value(EXYNOS4_GPX3(2));//根据这个函数获取GPX3_2的按键状态
        if(!dn)
                {
                        printk("\n key   down !! \n");
                                                my_key.my_event.code=KEY_ENTER;
                                                my_key.my_event.value=1;
                }
        else
                {
                        printk("\n key   up !! \n");
                                                my_key.my_event.code=KEY_ENTER;
                                                my_key.my_event.value=0;
                }
		wake_up_interruptible(&my_key.key_hand_queue);//唤醒进程
		my_key.key_state=1;//此时有数据,将状态置1

        return IRQ_HANDLED;

}

static struct file_operations myfops={
        .owner=THIS_MODULE,
        .open=key_open,
        .release=key_close,
        .write=key_write,
        .read=key_read,
};

static int __init mykey_init(void)
{
                my_key.irq=0;
        my_key.major=250;

        my_key.flag=IRQF_DISABLED |
                    IRQF_TRIGGER_FALLING |
                    IRQF_TRIGGER_RISING;//设置属性,上升,下降沿触发

        my_key.irq=gpio_to_irq(EXYNOS4_GPX3(2));//获取中断号
        request_irq(my_key.irq,key_handle_t,my_key.flag,"key1",NULL);//将这个按键注册到内核,这样才能识别
                if(register_chrdev(my_key.major,"qin",&myfops))//返回值为1则失败
                        {
                                        printk("\nregister is faile !\n");
                        }
                else
                        {
                                                printk("\nregister is ok !\n");
                        }

        my_key.key_class=class_create(THIS_MODULE,"class");
        my_key.key_dev=device_create(my_key.key_class,NULL,MKDEV(my_key.major,0),NULL,"qin");
        printk("\nI am key_dev\n");

		my_key.key_state=0;
		init_waitqueue_head(&my_key.key_hand_queue);//初始化等待队列头

		
        return 0;
}
static int __exit mykey_exit(void)
{
        free_irq(my_key.irq,NULL); //注销key1中断
                device_destroy(my_key.key_class,MKDEV(my_key.major,0));//注销dev
                class_destroy(my_key.key_class);//注销class
        unregister_chrdev(my_key.major,"qin");

        printk("\n   bye      bye~~~~~~~\n");
        return 0;
}
module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");


           

app不需要做改动

总结:

此源代码在上一个的修改有
#include<linux/sched.h>  这里添加的函数需要用到这个头文件

1.wait_queue_head_t key_hand_queue;
   		结构体中定义等待队列头,定义按键中的state用于之后判断是否将进程休眠,
   		因为结构体没有初始化所以需要在入口函数进行置0,一开始是没有数据的
		my_key.key_state=0;

   		

2.init_waitqueue_head(&my_key.key_hand_queue);//初始化等待队列头

在init入口函数中初始化等待队列头	

3.wait_event_interruptible(my_key.key_hand_queue,my_key.key_state);//将进程休眠,用my_ke中的state状态判断

在read函数中判断是是否将进程休眠

4.当有数据来的时候(按键按下),在irqreturn_t key_handle_t(int irq,void *dev_id)函数中进行唤醒和状态置1

wake_up_interruptible(&my_key.key_hand_queue);//唤醒进程
		my_key.key_state=1;//此时有数据,将状态置1
		

*/