块设备驱动:

内核中自带的nand 驱动程序通过它来了解,在工作中最多做的是分区表。

1.linux内核中常见的块设备:磁盘,SD卡,FLASH(NOR),NAND.Linux内核中最早出现的块设备驱动时硬盘驱动,后续出现的支持SD卡,flash驱动程序都是从硬盘驱动衍生出来的

2.块设备为什么需要缓冲机制?

   带磁头

   不带磁头

3.内核中块设备的机制?

 用户空间:有一系列readwrite)操作通过系统调用到内核中每一次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_transferblk_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=vmallocvirblk_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_capacutyvirblk_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_diskvirblk_device.gd;

       

 

    return 0;

}

void _exit virblk_exit(void)

{

del_gendisk(virblk_device.gd);

put_disk(virblk_device.gd)//引用计数减一

blk_cleanup_queuevirblk_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/