首页 > 系统相关 >Linux下驱动开发_块设备驱动开发(内存模拟存储)

Linux下驱动开发_块设备驱动开发(内存模拟存储)

时间:2022-10-22 15:00:32浏览次数:47  
标签:Linux blkdev dev 开发 tiny4412 驱动 root block 设备


theme: scrolls-light highlight: vs2015


一、前言

块设备驱动块是Linux下3大设备驱动框架之一,块设备主要是针对存储类型的设备设计的驱动,配合文件系统完成数据存储。在应用层的cp、cd、touch、vim、mount等等可以操作文件,可以操作目录的命令都会通过文件系统,通过块设备驱动完成对底层存储设备的访问,实现数据读取或者写入。

所以大致总结下:块设备驱动的目的是给Linux文件系统提供底层接口。

二、编写块设备驱动的思路

既然学到了驱动开发,了解到块设备开发。 那么看这篇文章的小伙伴应该在单片机里裸机方式写过一些flash驱动、SD卡驱动。 对于flash存储设备而言,要存取数据,根据芯片的手册我们主要是封装一个写数据函数和读取函数,封装好了这两个函数才方便上层应用的调用。 对于flash而言常见的读写单位一般是页、扇区。容量大的flash比如SD卡,读写最小单位规定为扇区。  扇区一般大小规定为512字节,那么底层要封装好的函数就是读扇区,写扇区函数。 这两个函数完成与flash空间交互,实现数据存储。

在Linux下完成块设备驱动编写,主要是要完成来至文件系统的存储请求,文件系统让你把数据存到那个扇区,你驱动就去存,文件系统让你从那个扇区读取输出来,驱动就去读取。 只要是完美的处理好了文件系统的请求,那么应用层工作就是一切顺利的。 文件系统不需要管你把数据存在什么设备上。是SD卡?是FlashW25Q64是eeprom?还是RAM内存里?对文件系统而言不关系,它只关心存进去的数据下次可以完美的读取出来便是。

那么为了方便介绍块设备的驱动开发,我这里会先用malloc在驱动申请一块内存来当做FLASH设备,这样就不需要接任何硬件,降低了难度,纯软件的方式理解驱动框架运作流程。

下面这张图是解释应用层 使用文件目录操作命令操作块设备时,与底层驱动之间的调用大致过程。

Linux下驱动开发_块设备驱动开发(内存模拟存储)_设备号

块设备与字符设备比较:

(1) 块设备设备节点名称自己定义的,没有标准。

(2)块设备的主设备号可以动态分配,次设备号时通过文件系统对块设备分区时,自动填充。

/dev/sdb fdisk命令进行分区。 /dev/sdb1 /dev/sdb2

(2)在块设备驱动里可以设置最大支持的分区数量

块设备处理数据的方式

Linux下驱动开发_块设备驱动开发(内存模拟存储)_块设备_02

下面是块设备驱动的数据结构:

Linux下驱动开发_块设备驱动开发(内存模拟存储)_设备驱动_03

Linux下驱动开发_块设备驱动开发(内存模拟存储)_设备驱动_04

Linux下驱动开发_块设备驱动开发(内存模拟存储)_块设备_05

块设备注册与注销函数

1. 注册函数
int register_blkdev(unsigned int major, const char *name)
函数功能介绍: 注册一个新的块设备
函数参数介绍:
@major:块设备的主设备号[1..255]。 如果 major = 0,表示尝试分配未使用的主设备号,返回值就表示分配
成功的主设备号。
@name:新块设备的名称。 注意: 该名称必须保证在系统中是唯一的。
注册示例:
int Tiny4412_block_major = register_blkdev(0, "Tiny4412_block");


2. 注销函数
void unregister_blkdev(unsigned int major, const char *name)
函数功能介绍: 注销已注册的块设备。
函数参数介绍:
@major: 主设备号
@name: 设备名称
注销示例:
unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");

三、块设备的示例代码

3.1 驱动代码

这份代码里存储数据的空间是申请了一段内存来模拟的。没有依赖于硬件,所以:可以在任何Linux下编译安装测试,完成块设备驱动的了解学习。

#include <linux/module.h> 
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/version.h>
#include <linux/vmalloc.h>

/*
* insmod tiny4412_blkdev.ko
* # or insmod tiny4412_blkdev.ko size=numK/M/G/T
* fdisk /dev/tiny4412_blkdev # create 2 patitions
* mkfs.ext2 /dev/tiny4412_blkdev1
* mkfs.ext2 /dev/tiny4412_blkdev2
* mount /dev/tiny4412_blkdev1 /mnt/temp1/
* mount /dev/tiny4412_blkdev2 /mnt/temp2/
* # play in /mnt/temp1/ and /mnt/temp2/
* umount /mnt/temp1/
* umount /mnt/temp2/
* rmmod tiny4412_blkdev.ko
*
*/
static int Tiny4412_block_major=0;
static struct request_queue *tiny4412_blkdev_queue;
static struct gendisk *tiny4412_blkdev_disk;

#define TINY4412_BLK_DEV_BYTES (1024*1024*50) /*设置块设备的大小*/
static unsigned char *sizeof_p;

/*
* Handle an I/O request.
* 实现扇区的读写

unsigned long sector: 当前扇区位置
unsigned long nsect : 扇区读写数量
char *buffer : 读写的缓冲区指针
int write : 是读还是写
*/
static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
{
/*块设备最小单位是一个扇区,一个扇区的字节数是512字节*/
unsigned long offset = sector; /*写入数据的位置*/
unsigned long nbytes = nsect; /*写入的长度*/
if((offset + nbytes)>TINY4412_BLK_DEV_BYTES)
{
printk("写超出范围,强制结束(%ld %ld)\n", offset, nbytes);
return;
}
if(write) /*为真,表示是写*/
memcpy(sizeof_p + offset, buffer, nbytes);
else /*读操作*/
memcpy(buffer,sizeof_p + offset, nbytes);
}

/*
处理请求
*/
static int tiny4412_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
int dir;
unsigned long long dsk_offset;
struct bio_vec *bvec;
int i;
void *iovec_mem;

/*判断读写方向*/
if(bio_data_dir(bio) == WRITE) dir = 1;
else dir = 0;
dsk_offset = bio->bi_sector << 9;
bio_for_each_segment(bvec, bio, i)
{
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;

//起始位置,长度,源数据,方向
Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);

kunmap(bvec->bv_page);
dsk_offset += bvec->bv_len;
}
bio_endio(bio, 0);
return 0;
}


struct block_device_operations tiny4412_blkdev_fops =
{
.owner= THIS_MODULE,
};


static int __init tiny4412_blkdev_init(void)
{
sizeof_p=vmalloc(TINY4412_BLK_DEV_BYTES);
if(sizeof_p==NULL)
{
printk("空间申请失败!\n");
return 0;
}
/*动态分配请求队列*/
tiny4412_blkdev_queue = blk_alloc_queue(GFP_KERNEL);

/*绑定请求队列*/
blk_queue_make_request(tiny4412_blkdev_queue,tiny4412_blkdev_make_request);

/*动态分配次设备号结构*/
/*每一个磁盘(分区)都是使用一个gendisk结构保存*/
tiny4412_blkdev_disk = alloc_disk(64);

/*磁盘名称赋值*/
strcpy(tiny4412_blkdev_disk->disk_name, "tiny4412_blkdev");

/*注册一个块设备,自动分配主设备号*/
Tiny4412_block_major = register_blkdev(0,"Tiny4412_block");
printk("Tiny4412_block_major=%d\n",Tiny4412_block_major);

tiny4412_blkdev_disk->major=Tiny4412_block_major; /*主设备号*/
tiny4412_blkdev_disk->first_minor = 0; /*次设备号*/
tiny4412_blkdev_disk->fops = &tiny4412_blkdev_fops; /*文件操作结合*/
tiny4412_blkdev_disk->queue = tiny4412_blkdev_queue; /*处理数据请求的队列*/

/*设置磁盘结构 capacity 的容量*/
/*注意: 块设备的大小使用扇区作为单位设置,而扇区的大小默认是512字节。
cat /sys/block/xxxx/size 可以查看到设置的大小
把字节为单位的大小转换为以扇区为单位时,我们需要除以512,或者右移9位
*/
set_capacity(tiny4412_blkdev_disk,TINY4412_BLK_DEV_BYTES>>9);

//添加磁盘信息到内核
add_disk(tiny4412_blkdev_disk);
return 0;
}

static void __exit tiny4412_blkdev_exit(void)
{
//删除磁盘
del_gendisk(tiny4412_blkdev_disk);

put_disk(tiny4412_blkdev_disk);

//清除队列
blk_cleanup_queue(tiny4412_blkdev_queue);

/*注销块设备*/
unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");

vfree(sizeof_p);
}

module_init(tiny4412_blkdev_init);
module_exit(tiny4412_blkdev_exit);
MODULE_LICENSE("GPL");

3.2 安装测试

[root@wbyq code]#insmod tiny4412_block_device.ko

[ 6920.590000] tiny4412_sda: unknown partition table

[ 6920.590000] 块设备注册成功!

[root@wbyq code]#ls /dev/tiny4412_sda -l

brw-rw---- 1 root root 253, 0 Nov 14 2018 /dev/tiny4412_sda

[root@wbyq code]#mkfs.ext2 /dev/tiny4412_sda //格式化块设备

Filesystem label=

OS type: Linux

Block size=1024 (log=0)

Fragment size=1024 (log=0)

2560 inodes, 10240 blocks

512 blocks (5%) reserved for the super user

First data block=1

Maximum filesystem blocks=262144

2 block groups

8192 blocks per group, 8192 fragments per group

1280 inodes per group

Superblock backups stored on blocks:

8193

[root@wbyq code]#mount /dev/tiny4412_sda /mnt/ //挂载块设备

[root@wbyq code]#cd /mnt/

[root@wbyq mnt]#ls

lost+found

[root@wbyq mnt]#mkdir 123

[root@wbyq mnt]#ls

123 lost+found

[root@wbyq mnt]#touch 123.c

[root@wbyq mnt]#ls

123 123.c lost+found

[root@wbyq mnt]#cd /

[root@wbyq ]#umount /mnt/







[root@wbyq ]#cat /sys/class/block/tiny4412_sda/size

20480




[root@wbyq ]#mount /dev/tiny4412_sda /mnt/

[root@wbyq ]#df -h

Filesystem Size Used Available Use% Mounted on

192.168.10.11:/work/rootfs/

46.8G 15.5G 28.9G 35% /

/dev/tiny4412_sda 9.7M 14.0K 9.2M 0% /mnt



 作业:

1. 看懂块设备框架,使用的模拟的内存。

2. 加入SD卡的驱动,配合块设备框架,完成完整的块设备驱动编写。

3.3 分区之后的情况

[root@wbyq code]#ls /dev/tiny4412_sda

tiny4412_sda tiny4412_sda1

[root@wbyq code]#ls /dev/tiny4412_sda1

[root@wbyq code]#ls /dev/tiny4412_sda* -l

brw-rw---- 1 root root 253, 0 Nov 14 2018 /dev/tiny4412_sda

brw-rw---- 1 root root 253, 1 Nov 14 2018 /dev/tiny4412_sda1

标签:Linux,blkdev,dev,开发,tiny4412,驱动,root,block,设备
From: https://blog.51cto.com/u_11822586/5785838

相关文章

  • Linux下RTC驱动开发(硬件采用DS1302)
    theme:scrolls-lighthighlight:xcode​一、前言在Linux系统上主要有两个时间基准,一个数是系统时间和,一个是RTC时间。其中系统时间是系统运行时由定时器(滴答定时器)维护......
  • Linux网络服务之SSH(远程访问及控制)
    1SSH1.1SSH概念SSH(SecureShell)是一种安全通道协议,主要用来实现字符界面的远程登录、远程复制等功能SSH协议对通信双方的数据传输进行了加密处理,其中包括用户登录时......
  • 英码科技荣获黄埔区、广州开发区2022年度第一批“绿+”企业认定
    近日,广州开发金融工作局公布了《黄埔区、广州开发区2022年度第一批“绿+”企业和绿色企业名单》,英码科技榜上有名,荣获“绿+”企业认定。此次认定表明了英码科技的主营业务......
  • Linux 常用命令
    LinuxShell:tail命令动态显示日志时高亮显示某些关键字1、思路把Linux输出的内容再做一次管道处理,比较符合Linux系统管道处理的思想2、高亮显示单个关键词tail-f......
  • linux实现 五子棋(人人对战)
    分步解析对于game函数的解析进入game函数中,通过创建一个二维数组来打印棋盘,进入dowhile循环中,策略是先打印出棋盘,然后先让用户1落子,进行判定,看是否需要继续,若需......
  • 敏捷(SCRUM)学习笔记 1 —— 《SCRUM敏捷软件开发》 (美)Mike Cohn)著 清华大学出版社2
     关键词:《SCRUM敏捷软件开发》——(美)MikeCohn著,清华大学出版社2011版,读书笔记(一) 第一章  为什么敏捷转型难(但值得) 为什么转型困难一、变化来得比以往更快......
  • sqlmap的使用方法(linux)
    原文来自:https://blog.csdn.net/weixin_52084568/article/details/123839776sqlmap的使用方法(linux)sqlmap--version查看sqlmap版本sqlmap-h查看sqlmap帮助  ......
  • web开发常见问题
    http常见哪headerucccuseragentconncect:keepalivecontent-lengthcontent-type5xx状态码http状态码,500502503504分别分别表示什么nginx反向代理ng......
  • 【STM32】按键开发!
    按键开发基础按键扫描的基本原理按键信号的识别:一般来说,按键的两个引脚的一端通过电阻上拉到高电平,另一端则接地在没有按键按下的时候,输入引脚为高电平当有按键按下,输......
  • 《MiniPRO H750开发指南》第六十四章 综合测试实验
    第六十四章综合测试实验​为了方便大家使用和验证综合例程,本章内容是综合例程的使用介绍。目的是展示STM32H7的强大处理能力,并且可以测试开发板的大部分功能。本实验代码只......