块设备(一)

块设备驱动()

以数据块为访问单位的设备,数据块的大小不固定一般为512的倍数,块设备和字符设备的区别读写数据的基本单元不同,块设备读写数据的基本单元为块,字符设备基本单元为字节,块设备可以随机访问,字符设备只能顺序访问。

块设备在Linux结构中体系

块设备(一)_第1张图片

Disk Caches(缓存):当用户发起文件访问请求时,首先回到Disk Caches中寻找文件是否被缓存了,如果在Disk Caches中则直接读取文件,如果数据不再缓存中,则必须要到具体的文件系统中读取数据。

Mapping Layer里主要是文件的磁盘系统,把用户对文件的访问转换成对设备的访问,映射的作用,调用具体文件系统的函数访问文件的inodeinode文件在磁盘上的物理信息,确定请求的数据在磁盘上的逻辑地址。

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);

块设备驱动的测试:1insmod simple-blk.ko

2ls /dev/simple_blkdev -l


下面块设备是依赖文件系统的,在块设备之上建立文件系统,磁盘一般使用ext3文件系统

3mkfs.ext3 /dev/simple_blkdev   //格式化磁盘块设备

块设备(一)_第2张图片

把一些数据放到磁盘上,然后再拿出来。在Linux上使用磁光盘要mount到某个目录

4mkdir -p /mnt/blk

5:mount /dev/simple_blkdev /mnt/blk

能够往目录里拷贝、读取数据,创建成功 

 


你可能感兴趣的:(Linux,设备驱动程序)