IO模型之阻塞模型

其实上一篇博客的写法就是非阻塞模型,感兴趣可以参考一下~

传统方式和设备树方式获取中断_灵魂之Ca的博客-CSDN博客

前言:

框架:字符设备驱动框架

什么是IO模型呢?

        IO顾名思义就是输入输出,对于linux驱动而言,输入输出就是file_operations里的read和write操作。至于模型,这个词听起来牛批,其实就是io读写的不同模式,比如非阻塞io,从应用程序的角度(驱动是服务于应用的),无论有没有read到数据,都会返回一个值,程序不会卡在原地;而阻塞io,就是应用程序,没有read到数据就会卡在原地不动(后面我们会知道其实是这个应用程序的read所对应的驱动被吊起来了,既不进行下去,也不返回,造成了应用程序卡在那里的情况~)

        其他的io模型还有poll多路监听模型,异步io模型。

听起来好像把应用程序卡住不是好事,但是呢从cpu资源的角度,有些占用资源的空操作(如没有数据还一直读取),就应该吊起来。如下如图,如果应用程序是while读取按键数据并且是以非阻塞的IO模型,那么cpu资源的占用是非常炸烈的。

如下:        

        运行非阻塞IO模型的按键驱动和应用程序,可见应用程序的资源占用达到了惊人的24%,然而它只是一个小小的readkey程序而言。

        而用阻塞IO模型(在驱动中,驱动read--判断没有数据就将驱动进程挂起,当有按键中断时写语句将驱动进程唤醒从而回应循环read的应用进程,将数据返回过去)这样的模型,cpu资源占用就很小很小!因为没有数据的时候驱动进程没有任何操作,顶多cpu也就执行应用程序的read这一条代码而已,没有调用到驱动程序一系列代码。

可见,这样不仅能有正常的读取按键功能,而且cpu资源占用率也很低。

代码思路:

阻塞io模型 即没有资源时把app进程挂起,有资源了再唤醒app进程来读
实现思路:0.初始化等待队列 1.将驱动进程加入等待队列 2.将驱动进程状态设为TASK_INTERRUPTIBLE 3.使之休眠
有数据时:4.中断唤醒驱动进程

以下是代码~和相关注释:

代码:

hw_charDev_describe.h

//仍然先是描述外设的结构体
//#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/wait.h>

struct key_event{
	int code;				//表示按键的内涵:home/esc/Q/W/E/R/T/ENTER
	int value;				//按下1,抬起0
	unsigned char state;	//按键状态 有没有按
};

struct hw_describe{
	char *dev_name;			//外设名字
	unsigned int dev_major;	//外设设备号
	struct class *hw_class;	//外设设备类型
	struct device *dev; 	//外设设备节点
	struct cdev mycdev;		//注册cdev到系统
	struct device_node *mynode;					//设备节点
	unsigned int phyreg_base;					//外设寄存器物理基地址
	volatile unsigned int *virreg_base;			//外设寄存器虚拟基地址
	
	unsigned int regmap_size;	//ioremap的长度,一个寄存器是4,即4Byte,32位
	int mygpio;					//gpio号
	int irqno;					//中断号

	//外设类型
	struct key_event mykey_event;

	//IO操作 实现阻塞IO的关键--等待队列
	wait_queue_head_t wq_head;
	
};




6th_charDev_pendingApp.c

//阻塞io模型 即没有资源时把app进程挂起,有资源了再唤醒app进程来读
//实现思路:1.将驱动进程加入等待队列 2.将驱动进程状态设为TASK_INTERRUPTIBLE 3.使之休眠
//以上三步都由函数	wait_event_interruptible    直接完成
//有数据时:4.中断唤醒驱动进程

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("LCH");

#include "hw_charDev_describe.h"
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>

#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <uapi/linux/input-event-codes.h>



struct hw_describe *key_dev;//用于描述硬件外设
unsigned char key_state = 0;//0表示没有按下,1表示有按下



/*打开操作*/
static int chardevnode_open(struct inode *inode, struct file *file){
	printk(KERN_EMERG "chardevnode_open is success!\n");
	
	return 0;
}
/*关闭操作*/
static int chardevnode_release(struct inode *inode, struct file *file){
	printk(KERN_EMERG "chardevnode_release is success!\n");
	
	return 0;
}
/*IO操作*/
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
//	printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %ld,arg is %ld \n",cmd,arg);
	
	return 0;
}

ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
	//用户要读,ret = copy_to_user(buf用户的,给用户数据的指针,count);ret!=0是不正常的
	
	int ret;
	//如果是非阻塞模型,并且没有数据,就返回一个出错码
	//否则作阻塞模型处理
	if(file->f_flags&O_NONBLOCK && !key_dev->mykey_event.state){
		return -EAGAIN;
	}

	//判断有无数据,没有就让驱动进程休眠 参数2 1:不等待 0:等待
	wait_event_interruptible(key_dev->wq_head,key_dev->mykey_event.state);
	
	ret = copy_to_user(buf,&key_dev->mykey_event,count);
	if(ret>0){
		printk(KERN_EMERG "copy_to_user error!\n");
		return -EFAULT;
	}
	memset(&key_dev->mykey_event,0,sizeof(key_dev->mykey_event));
	key_dev->mykey_event.state=0;
	
	return 0;
}

ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
	//用户写进来,ret = copy_from_user(存数据的指针,buf用户写进来的,count);ret!=0是不正常的

	return 0;
}


struct file_operations fops ={
	.open = chardevnode_open,
	.release = chardevnode_release,
	.unlocked_ioctl = chardevnode_ioctl,
	.read = chardevnode_read,
	.write = chardevnode_write,
};

static irqreturn_t keyirq_handler(int irqno,void *devid){
	int value;
    printk("receive a interrupt 9!\n");
//	4.1非设备树方式
//	value = *(key_dev->virreg_base+1)&(0x1<<1);

//	4.2设备树方式
	value = gpio_get_value(key_dev->mygpio);
	if(value==1){	//没按下		==  key_event.value = 0
		key_dev->mykey_event.code = KEY_ENTER;
		key_dev->mykey_event.value = 0;
    	printk("key up!\n");
	}
	else if(value==0){			//按键按下
		key_dev->mykey_event.code = KEY_ENTER;
		key_dev->mykey_event.value = 1;
		printk("key down!\n");
	}
	//有数据了,唤醒驱动进程~
	wake_up_interruptible(&key_dev->wq_head);
	
	key_dev->mykey_event.state = 1;
	return IRQ_HANDLED;
}

static int __init charDev_init(void)
{
	int ret =0 ;

	
	printk(KERN_EMERG "charDev_init\n");

	
	//0.给外设对象分配空间
	key_dev = kmalloc(sizeof(struct hw_describe),GFP_KERNEL);
	if(key_dev ==NULL){
		printk(KERN_EMERG "kmallock error\n");
		return(-ENOMEM);
	}
	key_dev->dev_name = "mykey";

	
	//1.申请主设备号 设备号是MKDEV(key_dev->dev_major,自选)
	key_dev->dev_major=register_chrdev(0,key_dev->dev_name,&fops);
	
	if(key_dev->dev_major<0)
	{
		printk(KERN_EMERG "register_chrdev error\n");
		ret = -ENODEV;
		goto err_0;
	}
	else
	{
		printk(KERN_EMERG "dev_major is %d\n",key_dev->dev_major);
	}

	//2.注册设备到系统 /proc/devices
	
    cdev_init(&(key_dev->mycdev),&fops);
    (key_dev->mycdev).owner = THIS_MODULE;
    (key_dev->mycdev).ops = &fops;

    
    ret = cdev_add(
&(key_dev->mycdev),MKDEV(key_dev->dev_major,0),1);
    if(ret){
        		printk(KERN_EMERG "cdev_add is fail! %d\n",ret);
				goto err_1;
    }else{
        		printk(KERN_EMERG "cdev_add is success!\n");
    }

	//3.生成设备节点
	key_dev->hw_class = class_create(THIS_MODULE,"charDev_class");
	if(IS_ERR(key_dev->hw_class))
	{
		printk(KERN_EMERG "class_create error\n");
		ret = PTR_ERR(key_dev->hw_class);
		goto err_2;
	}
	key_dev->dev=device_create(key_dev->hw_class,NULL,MKDEV(key_dev->dev_major,0),NULL,key_dev->dev_name);//设备节点名字
	if(IS_ERR(key_dev->dev))
	{
		printk(KERN_EMERG "device_create error\n");
		ret = PTR_ERR(key_dev->dev);
		goto err_3;
	}
	else
	{
		printk(KERN_EMERG "dev_name is %s\n",key_dev->dev_name);
	}

	//4.获取硬件资源
	/*
		//	4.1直接从原理图得到硬件信息
		//key GM_INT9--GPX1_1--0x11000C20 
		key_dev->regmap_size = 4;
		key_dev->phyreg_base = 0x11000C20 ;	//寄存器基地址

		//+0是CON	+1是DAT		+2是PUD		+3是DRV
		key_dev->virreg_base = ioremap(key_dev->phyreg_base,4*key_dev->regmap_size);//映射4个寄存器
		if(key_dev->virreg_base==NULL){
			printk(KERN_EMERG "ioremap error\n");
			ret = -ENOMEM;
			goto err_4;

		}

		//配置GPX1-1为输入
		*(key_dev->virreg_base) &= (~(0xF<<4));	//清除4-7位
		*(key_dev->virreg_base) |=((0x0)<<4);	//将4-7位置为0x1
		//初始化GPX1-1为高电平,即和原理图一样
		*(key_dev->virreg_base+1) &= (~(0x1<<1));	//清除第1位
		*(key_dev->virreg_base+1) |=((0x1)<<1);	//将第1位置为0x1,即高电平
		//初始化GPX1-1为上拉,即和原理图一样
		*(key_dev->virreg_base+2) &= (~(0x3<<2));	//清除【3:2】位
		*(key_dev->virreg_base+2) |=((0x3)<<2);	//将其置为0x3,即上拉
	*/
	//	4.2设备树方式获得硬件信息
	
		//	/mykey compatible = "gpio-key"
		key_dev->mynode = of_find_node_by_name(NULL,"mykey");
		if(key_dev->mynode==NULL){
			printk(KERN_EMERG "of_find_node_by_name error\n");
			ret = -ENOMEM;
			goto err_4;
		}
		key_dev->mygpio = of_get_named_gpio_flags(key_dev->mynode,"gpios",0,NULL);
		if (!gpio_is_valid(key_dev->mygpio))
			printk("gpio isn't valid\n");
		else printk("gpio num=%d",key_dev->mygpio);

		ret= gpio_request(key_dev->mygpio, "gpios");
		if(ret!=0){
			printk(KERN_EMERG "gpio_request error\n");
			ret = -ENOMEM;
			goto err_5;
		}
		gpio_direction_input(key_dev->mygpio);//初始化为输入模式
		
		//获取中断号--申请中断
		key_dev->irqno = of_irq_get(key_dev->mynode,0);
    	ret = request_irq(key_dev->irqno,keyirq_handler,IRQ_TYPE_EDGE_BOTH,"keyirq",NULL);
		if(ret==0){ //返回0说明正确
			printk(KERN_EMERG "key_dev->irqno  is %d\n",key_dev->irqno);
		}
		
		//阻塞io:初始化等待队列头
		init_waitqueue_head(&key_dev->wq_head);
	
	return 0;

err_5:
	gpio_free(key_dev->mygpio);

err_4:
	device_destroy(key_dev->hw_class,MKDEV(key_dev->dev_major,0));
err_3:
	class_destroy(key_dev->hw_class);
err_2:
	unregister_chrdev(key_dev->dev_major,key_dev->dev_name);
err_1:
	cdev_del(&(key_dev->mycdev));
err_0:
	kfree(key_dev);
	return(ret);


}
static void __exit  charDev_exit(void)
{
	free_irq(key_dev->irqno,NULL);
	gpio_free(key_dev->mygpio);
	//iounmap(key_dev->virreg_base);
	device_destroy(key_dev->hw_class,MKDEV(key_dev->dev_major,0));
	class_destroy(key_dev->hw_class);
	cdev_del(&(key_dev->mycdev));
	unregister_chrdev(key_dev->dev_major,key_dev->dev_name);
	kfree(key_dev);

}

module_init(charDev_init);
module_exit(charDev_exit);

app_readkey_pending.c

其实和上一个是一样的

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>


struct key_event{
	int code;		//表示按键的内涵:home/esc/Q/W/E/R/T/ENTER
	int value;		///按下1,抬起0
	unsigned char state;	//按键状态 有没有按
};

//打开设备节点  传入cmd ,
int main(int argc ,char **argv){
    int fd,res,digital,readNum;
	struct key_event mykey;
    char *readSWDigitalNode="/dev/mykey";
	unsigned char readBuffer;
    //打开节点
    fd = open(readSWDigitalNode,O_RDWR);
    if(fd>0)printf("app open readSWDigitalNode success\n");
    else {
        printf("app open readSWDigitalNode err\n");
		return -1;
    }
	while(1){
		readNum = read(fd, (void *)(&mykey),sizeof(struct key_event));//读取1B的数据
		if(mykey.state==1){
			if(mykey.value==1){ //按下1,抬起0
	        	printf("app key down\n");
			}
			else if(mykey.value==0){
	        	printf("app key up\n");
			}
			mykey.state=0;
		}
	}
    close(fd);
    return ;
}



完~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值