块设备驱动(一)
以数据块为访问单位的设备,数据块的大小不固定一般为512的倍数,块设备和字符设备的区别读写数据的基本单元不同,块设备读写数据的基本单元为块,字符设备基本单元为字节,块设备可以随机访问,字符设备只能顺序访问。
块设备在Linux结构中体系
Disk Caches(缓存):当用户发起文件访问请求时,首先回到Disk Caches中寻找文件是否被缓存了,如果在Disk Caches中则直接读取文件,如果数据不再缓存中,则必须要到具体的文件系统中读取数据。
Mapping Layer里主要是文件的磁盘系统,把用户对文件的访问转换成对设备的访问,映射的作用,调用具体文件系统的函数访问文件的inode,inode文件在磁盘上的物理信息,确定请求的数据在磁盘上的逻辑地址。
Generic Block Layer(通用块层):把磁盘块设备做一个抽象,所有的块设备都被抽象成一个模型。
I/O scheduler layer:磁盘工作:访问数据磁头移动到相应的柱面上,然后盘片转动读取数据,由I/O调度算法决定有数据读取时,磁头怎么移动,常用调度算法为电梯调度算法:如果电梯朝上运动,如果当前楼层的上方和下方都有请求,则先响应现有的上方请求,然后向下响应下方的请求;如果电梯向下运动,则刚好相反。
Block Device Driver:发送命令给磁盘控制器实现数据传输。
Linux内核使用struct gendisk描述块设备(磁盘、SD卡.......)
struct gendisk {
int major ; //主设备号
int firest_major ; //次设备号
int minors ;
char disk_name[DISK_NAME_LEN] ; //驱动名
struct block_device_operation *fops ; //块设备操作
struct request_queue *queue ; //请求队列
..............................
} //实现驱动主要就是实现这个结构
void add_disk(struct gendisk *gd) //向内核注册块设备
struct block_device_operations { //没有write 和 read
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
....................................................
};
块设备的主要是处理I/O请求读写的问题:使用struct request表示等待处理的块设备I/O读写请求。
struct request {
struct list_head queuelist ; //链表结构
sector_t sector ; //要操作的整个扇区
unsigned long nr_sectors ; //要操作的扇区数目
struct bio *bio ; //请求bio结构的链表
struct bio *biotail ; //请求bio结构的链表尾
.........................................};//把对磁盘上文件的访问,算出文件对应那个扇区,然后转换成对扇区的访问请求I/O请求。
请求队列就是I/O请求request所形成的队列,由struct request_queue描述。
对于这些请求链表有如下函数:
struct request_queue *blk_init_queue(request_fn_proc *rfn , spinlock_t *lock)//函数指针,自旋锁
初始化请求队列,一般在块设备驱动模块加载时调用。rfn:当访问数据时,就会调用rfn指向的函数,完成最终的数据访问。
void blk_cleanup_queue (request_queue_ t *q) 清除请求队列
struct request *elv_next_request (request_queue_t *queue) 得到下一个要处理的请求,请求由一个个request组成,形成链表。
void blkdev_dequeue_request (struct request *req) 从队列中删除一个请求
简单的块设备驱动程序(ramdisk:内存上模拟磁盘,在内存上划出空间来做磁盘,按照访问的磁盘的方式去访问):
#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR
#define SIMP_BLKDEV_DISKNAME "simp_blkdev"
#define SIMP_BLKDEV_BYTES (16*1024*1024) //划出16M内存模拟磁盘
static struct request_queue *simp_blkdev_queue;
static struct gendisk *simp_blkdev_disk;
unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];
static void simp_blkdev_do_request(struct request_queue *q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {
if ((req->sector + req->current_nr_sectors) << 9//最后访问的扇区编号
//每个扇区512K,最后访问扇区的大小
> SIMP_BLKDEV_BYTES) {
printk(KERN_ERR SIMP_BLKDEV_DISKNAME
": bad request: block=%llu, count=%u\n",
(unsigned long long)req->sector,
req->current_nr_sectors);
end_request(req, 0);
continue;
}
switch (rq_data_dir(req)) { //判断数据流向
case READ: //磁盘--->用户空间
memcpy(req->buffer,
simp_blkdev_data + (req->sector << 9),//从那个扇区开始
req->current_nr_sectors << 9);
end_request(req, 1);
break;
case WRITE:
memcpy(simp_blkdev_data + (req->sector << 9),
req->buffer, req->current_nr_sectors << 9);
end_request(req, 1);
break;
default:
/* No default because rq_data_dir(req) is 1 bit */
break;
}
}
}
struct block_device_operations simp_blkdev_fops = {
.owner = THIS_MODULE,
};
static int __init simp_blkdev_init(void)
{
int ret;
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);
//主要实现imp_blkdev_do_request 请求处理函数,当上次形成请求时,就把请求挂到simp_blkdev_queue 。
if (!simp_blkdev_queue) {
ret = -ENOMEM;
goto err_init_queue;
}
simp_blkdev_disk = alloc_disk(1); //分配gendisk
if (!simp_blkdev_disk) {
ret = -ENOMEM;
goto err_alloc_disk;
}
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
simp_blkdev_disk->first_minor = 0;
simp_blkdev_disk->fops = &simp_blkdev_fops;
//主要实现
simp_blkdev_disk->queue = simp_blkdev_queue;
set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
add_disk(simp_blkdev_disk); //添加块设备
return 0;
err_alloc_disk:
blk_cleanup_queue(simp_blkdev_queue);
err_init_queue:
return ret;
}
static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk);
put_disk(simp_blkdev_disk);
blk_cleanup_queue(simp_blkdev_queue);
}
module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);
块设备驱动的测试:1:insmod simple-blk.ko
2:ls /dev/simple_blkdev -l
下面块设备是依赖文件系统的,在块设备之上建立文件系统,磁盘一般使用ext3文件系统
3:mkfs.ext3 /dev/simple_blkdev //格式化磁盘块设备
把一些数据放到磁盘上,然后再拿出来。在Linux上使用磁光盘要mount到某个目录
4:mkdir -p /mnt/blk
5:mount /dev/simple_blkdev /mnt/blk
能够往目录里拷贝、读取数据,创建成功