1.块设备是针对存储设备的,比如 SD 卡、 EMMC、 NAND Flash、 Nor Flash、 SPI Flash、机械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动,块设备驱动相比字符设备驱动的主要区别如下:
①、块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统(VFS)基本的数据传输单位。字符设备是以字节为单位进行数据传输的,不需要缓冲。
②、块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后在一次性将缓冲区中的数据写入块设备中。
这么做的目的为了提高块设备寿命,大家如果仔细观察的话就会发现有些硬盘或者 NAND Flash就会标明擦除次数(flash 的特性,写之前要先擦除),比如擦除 100000 次等。因此,为了提高块设备寿命而引入了缓冲区,数据先写入到缓冲区中(所以linux中对存储设备操作完,需要使用sync同步,才能写入将数据写入到存储设备,不然可能数据还在缓存区中),等满足一定条件后再一次性写入到真正的物理存储设备中,这样就减少了对块设备的擦除次数,提高了块设备寿命。
2.字符设备是顺序的数据流设备,字符设备是按照字节进行读写访问的。字符设备不需要缓冲区,对于字符设备的访问都是实时的,而且也不需要按照固定的块大小进行访问。
3.块设备结构的不同其 I/O 算法也会不同,比如对于 EMMC、 SD 卡、 NAND Flash 这类没有任何机械设备的存储设备就可以任意读写任何的扇区(块设备物理存储单元)。但是对于机械硬盘这样带有磁头的设备,读取不同的盘面或者磁道里面的数据,磁头都需要进行移动,因此对于机械硬盘而言,将那些杂乱的访问按照一定的顺序进行排列可以有效提高磁盘性能, linux 里面针对不同的存储设备实现了不同的 I/O 调度算法。
4.linux驱动程序中字符设备和块设备的三点区别
1)字符设备只能以字节为最小单位访问,而块设备以块为单位访问,例如512字节,1024字节等
2)块设备可以随机访问,但是字符设备不可以
3)字符和块没有访问量大小的限制,块也可以以字节为单位来访问
5.驱动
5.1驱动入口函数
static int __init ramdisk_init(void)
{
int ret = 0;
/* 1、先申请内存 */
ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
/* 2、注册块设备 */
ramdisk.major = register_blkdev(0, RAMDISK_NAME);
/* 3、申请gendisk */
ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);
/* 4、初始化自旋锁 */
spin_lock_init(&ramdisk.lock);
/* 5、申请并初始化请求队列 */
ramdisk.queue = blk_init_queue(ramdisk_request_fn, &ramdisk.lock);
/* 6、初始化 gendisk */
ramdisk.gendisk->major = ramdisk.major; /* 主设备号 */
ramdisk.gendisk->first_minor = 0; /* 起始次设备号 */
ramdisk.gendisk->fops = &ramdisk_fops; /* 操作函数 */
ramdisk.gendisk->private_data = &ramdisk; /* 私有数据 */
ramdisk.gendisk->queue = ramdisk.queue; /* 请求队列 */
sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME); /* 名字(拷贝名字) */
set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512); /* 设置gendisk容量,单位扇区 */
add_disk(ramdisk.gendisk); /* 向内核添加一个磁盘设备 */
return 0;
blk_queue_fail:
put_disk(ramdisk.gendisk);
gendisk_alloc_fail:
unregister_blkdev(ramdisk.major, RAMDISK_NAME);
ramdisk_register_blkdev_fail:
kfree(ramdisk.ramdiskbuf); /* 释放内存 */
ramalloc_fail:
return ret;
}
5.2定义设备结构体
/* ramdisk设备结构体 */
struct ramdisk_dev{
int major; /* 主设备号 */
unsigned char *ramdiskbuf; /* 模拟磁盘的空间 */
struct gendisk *gendisk; /* 描述一个磁盘设备 */
struct request_queue *queue; /* 请求队列 */
spinlock_t lock; /* 自旋锁 */
};
5.3块设备请求处理函数
/* 请求函数 */
static void ramdisk_request_fn(struct request_queue *q)
{
int err = 0;
struct request *req;
req = blk_fetch_request(q); /* 有电梯调度算法, */
while (req) {
/* 具体的数据读写操作 */
ramdisk_transfer(req);
/* 判断是否为最后一个请求,如果不是的话就获取下一个请求循环处理完请求队列中的所有请求。*/
if (!__blk_end_request_cur(req, err))
req = blk_fetch_request(q);
}
}
5.4具体的块数据读写操作
static void ramdisk_transfer(struct request *req)
{
/* 数据传输三要素:源,目的(地),长度。*/
unsigned long start = blk_rq_pos(req) << 9;
unsigned long len = blk_rq_cur_bytes(req);
void *buffer = bio_data(req->bio);
if (rq_data_dir(req) == READ) /* 读操作(读数据 ) */
memcpy(buffer, ramdisk.ramdiskbuf + start, len);
else /* 写操作(写数据) */
memcpy(ramdisk.ramdiskbuf + start, buffer, len);
}
5.5块设备操作集
/* 块设备操作集 */
static const struct block_device_operations ramdisk_fops =
{
.owner = THIS_MODULE,
.open = ramdisk_open,
.release = ramdisk_release,
.getgeo = ramdisk_getgeo, /* 获取磁盘信息,包括磁头、柱面和扇区等信息。 */
};
6.驱动测试
6.1查看磁盘设备:fdisk -l
6.2格式化/dev/ramdisk
格式化完成以后就可以挂载/dev/ramdisk 来访问了,挂载点可以自定义,这里就将其挂
载到/tmp
6.3 挂载设备:mount /dev/ramdisk /tmp/disk_test/
6.4卸载设备:umount /dev/ramdisk
6.5卸载设备后,重新挂载设备,查看存储设备中的内容是否还存在。
7.根目录下文件简单介绍:
7.1、/bin目录:binary(二进制的):许多指令对应的可执行程序文件目录。
7.2、/sbin目录:super binary超级的二进制许多指令对应可执行程序文件目录,该目录文件对应指令都是root用户可以执行的指令普通用户不能使用该目录里的命令:普通用户 用$开发、root用户 用#开头。
7.3、/usr目录:unix system resource,Unix系统资源文件目录,该目录类似于Windows系统的C:/Program files目录,该目录经常用于安装各种软件,软件安装完毕会形成对应的指令,该指令对应的可执行程序文件就存放在以下目录。/usr/bin许多指令对应的可执行程序文件目录/usr/sbin root用户执行的指令对应的可执行程序文件目录。
7.4、/dev目录:device系统硬件设备目录,Linux系统所有的硬件都通过文件表示,比如:/dev/cdrom是光驱/dev/sda 是第一块scsi硬盘。
7.5、/home目录:存放所有用户文件的根目录,是用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示。
7.6、/proc目录:内存映射目录,该目录可以查看系统的相关信息。
7.7、/var目录:用于存放运行时需要改变数据的文件,也是某些大文件的溢出区,比如说各种服务的日志文件等。
7.8、/boot目录:系统启动核心目录,用于储存系统启动文件。
7.9、/etc目录:存放系统管理和配置文件,比如:/ect/passwd 用于存储用户信息的文件、/etc/group 用于存储组别信息的文件。
7.10、/lib目录:存放跟文件系统中的程序运行所需要的共享库及内核模块。共享库又叫作动态链接共享库,作用类似Windows里的.dll文件,存放了根文件系统程序运行所需的共享文件。
7.11、/mnt目录:系统管理员安装临时文件系统的安装点,系统提供这个目录是让用户临时挂载其他的文件系统。
7.12、/tmp目录:用于存放各种临时文件,是公用的临时文件存储点。