块设备驱动:
内核中自带的nand 驱动程序通过它来了解,在工作中最多做的是分区表。
1.在linux内核中常见的块设备:磁盘,SD卡,FLASH(NOR),NAND.Linux内核中最早出现的块设备驱动时硬盘驱动,后续出现的支持SD卡,flash驱动程序都是从硬盘驱动衍生出来的
2.块设备为什么需要缓冲机制?
带磁头
不带磁头
3.内核中块设备的机制?
用户空间:有一系列read(write)操作通过系统调用到内核中每一次I/O操作在内核中缓存成一个bio结构,每次I/O操作对应一个boi结构,在拿boi去遍历requeue_queue中有哪个没处理的请求可以加进去也就是合并(把多个boi通过一定的算法合并成一个struct request),request 会被放到 request_queue请求队列中,内核中有个gendisk,写一个块设备就是实例化一个gendisk,如:有一个硬盘(分区)在内核中就有一个gendisk结构与之对应,yougegendisk,该gendisk就有唯一的一个request_queue.
需要关系的数据结构:
struct bio
{
....
}
struct quest
{
sector_t __sector //I/O操作的起始扇区
char *buffer//buffer缓冲区 I/O源或目标地址
unsigned int cmd_flags 标识是读还是写的操作
}
struct requeue
{
request_fn //队列中request处理函数
}
struct gendisk
{
struct boclk_device_operations
struct request_queue *queue;
}
3,内核中提供的操作函数
alloc_disk 申请一个gendisk空间
虚拟块设备程序:
#include <linux/init.h>
#include<linux/module.h>
...
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
MODULE_LICENSE()
struct request_queue *virblk_queue =NULL;
struct _virblk_device
{
struct gendisk *gd;
unsigned long size;//device的存储容量
u8 *data; //拿内存模拟NAND,指向存储位置
spinlock_t lock ;//request_queue操作过程中会使用到
};
struct _virblk_device virblk_device;
int logical_srctor_size = 512;//一个扇区多大字节
int nesctors = 1024 //设备共有的扇区个数
int *dev =0
struct blocke_device_operations virblk_fops=
{
.owner = .....
}
/*
sector, 该request 要操作的起始扇区
nsect, 该 request要连续操作的扇区个数
buffer 该request 操作的源或目标缓存
write 1代表对设备进行写
0 代表对设备进行读
*/
void virblk_transfer(sector_t sector,unsigned long nsect,char *buffer, int write)
{
unsigned long offset = sector *logical_sector_size;
unsigned long nbytes = nsrct *logical_sector_size;
if(iffset+nbytes)>virblk_device.size)
{
printk("beyond end! \n");
return 0;
}
if(write)
{
mencpy(virblk_device.data+iffest,buffer,nbytes);
}
else
{
memcpy(buffer,virblk_device.data_offest,nbytes);
}
}
void virblk_request(struct request_queue *q)
{
structrequest*request=NULL;
//获得请求
req= blk_fetch_request(q);
while(req! =NULL)
{
//处理请求
virblk_transfer(blk_rq_pos(req),blk_rq_cur_sectors(req),req->buffer,rq_data_dir(req));
//通知内核该request正确处理完成
if(!__blk_end_request_cur(req,0))
{
//取下一个请求
req = blk_fetch_request(q);
}
}
}
int __init virblk_init(void)
{
virblk_device.size=nsectors *logical_sector_size;//空间大小
//申请一段内存空间
virblk_device.data=vmalloc(virblk_device.size);
if(ERR(virblk_device.data))
{
return -ENOMEM;
}
//申请块设备号
dev = register_blkdev();//如果major为零则动态分给一个未使用的设备号
//申请gendisk空间
virblk_device.gd=alloc_disk (1);
//初始化gendisk
virblk_device.gd->major = blk_dev;
virblk_device.gd->first_minor = 0;
virblk_device.gd->fops = &virblk_fops;
strcpy(virblk_device.gd->disk_name, "virblk0");
//设置该设备的容量和扇区数
set_capacuty(virblk_device.gd,nsctors);//设置磁盘容量 size 单位是扇区
spin_lick_init(&virblk_device.lock);
//创建 request_queue
virblk_queue=blk_init_queue(virblk_request,&virblk_device.lock;
//关联 gendisk 对应的request_queue
virblk_device.gd->queue = virblk_queue;
//添加 gendisk到内核中去
add_disk(virblk_device.gd);
return 0;
}
void _exit virblk_exit(void)
{
del_gendisk(virblk_device.gd);
put_disk(virblk_device.gd)//引用计数减一
blk_cleanup_queue(virblk_queue);
unregister_blkdev(blk_dev,"virblk");
vfree(virblk_device.data);
}
module_init(vieblk_init)
module_exit(virblk_exit)
实验:
可以cat/proc/devices
ls /dev/virblk0 -l
mke2fs /dev/virblk0
mount /dev/virblk0 /mnt
之后想普通文件一样操作它
umount /mnt/
本文出自 “毛散人” 博客,请务必保留此出处http://songmao.blog.51cto.com/11700139/1880790
原文地址:http://songmao.blog.51cto.com/11700139/1880790