块设备是指只能以块(512Byte)为单位进行访问的设备, 块大小一般是512个字节的整数倍。常见的块 设备包括硬件,SD卡,光盘等。
2.快速体验
- insmod simple-blk.ko
- ls /dev/simp_blkdev0
- mkfs.ext3 /dev/simp_blk0
- mkdir –p /mnt/blk
- mount /dev/simp_blk0 /mnt/blk
- cp /etc/init.d/* /mnt/blk
- ls /mnt/blk
- umount /mnt/blk
- ls /mnt/blk
3.块设备驱动系统架构
VFS是对各种具体文件系统的一种封装 ,为 用户程序访问文件提供 统一的接口 。
4.系统架构-Cache
当用户发起文件访问请求的时候,首先会到
Disk Cache中寻找文件是否被缓存了,如果
在cache中,则直接从cache中读取。
如果数据不在缓存中,就必须要到具体的文
件系统中读取数据了。
5.Mapping Layer(映射层、FS文件系统)
1. 首先确定文件系统的block size,然后计算
所请求的数据包含多少个block。
2. 调用具体文件系统的函数来访问文件的
inode
结构,确定所请求的数据在磁盘上的地址。
6. Generic Block Layer
Linux内核把把块设备看作是由若干个扇区组 成的数据空间。上层的读写请求在通用块层被构造成一个或多个bio结构。
7. I/O Scheduler Layer
I/O调度层负责采用某种算法(如:电梯调度 算法)将I/O操作进行排序。
8. I/O Scheduler Layer
电梯调度算法的基本原则:如果电梯现在朝 上运动,如果当前楼层的上方和下方都有请 求,则先响应所有上方的请求,然后才向下 响应下方的请求;如果电梯向下运动,则刚 好相反。
9. 块设备驱动
在块系统架构的最底层,由块设备驱动根据 排序好的请求,对硬件进行数据访问。
10.块设备驱动实例分析
11、 简单块设备驱动设计
错误总结:一开始写的时候,编译完加载进内核。直接死机,因为代码有点长。就从逻辑处一点一点注释掉来分析,果然有好多错误。
第一次遇到的问题,内核中常用goto跳转处理错误情况。如果跳转处前面没有return就坑爹了,第一次遇到找了好久
6. Generic Block Layer
Linux内核把把块设备看作是由若干个扇区组 成的数据空间。上层的读写请求在通用块层被构造成一个或多个bio结构。
7. I/O Scheduler Layer
I/O调度层负责采用某种算法(如:电梯调度 算法)将I/O操作进行排序。
8. I/O Scheduler Layer
电梯调度算法的基本原则:如果电梯现在朝 上运动,如果当前楼层的上方和下方都有请 求,则先响应所有上方的请求,然后才向下 响应下方的请求;如果电梯向下运动,则刚 好相反。
9. 块设备驱动
在块系统架构的最底层,由块设备驱动根据 排序好的请求,对硬件进行数据访问。
10.块设备驱动实例分析
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/init.h>
-
- #include <linux/sched.h>
- #include <linux/kernel.h> /* printk() */
- #include <linux/slab.h> /* kmalloc() */
- #include <linux/fs.h> /* everything... */
- #include <linux/errno.h> /* error codes */
- #include <linux/timer.h>
- #include <linux/types.h> /* size_t */
- #include <linux/fcntl.h> /* O_ACCMODE */
- #include <linux/hdreg.h> /* HDIO_GETGEO */
- #include <linux/kdev_t.h>
- #include <linux/vmalloc.h>
- #include <linux/genhd.h>
- #include <linux/blkdev.h>
- #include <linux/buffer_head.h> /* invalidate_bdev */
- #include <linux/bio.h>
-
- MODULE_LICENSE("Dual BSD/GPL");
-
- static int major = 0;
-
- static int sect_size = 512;
-
- static int nsectors = 1024;
-
- /*
- * The internal representation of our device.
- */
- struct blk_dev{
- int size; /* Device size in sectors */
- u8 *data; /* The data array */
- struct request_queue *queue; /* The device request queue */
- struct gendisk *gd; /* The gendisk structure */
- };
-
- struct blk_dev *dev;
-
-
- /*
- * Handle an I/O request, in sectors.
- */
- static void blk_transfer(struct blk_dev *dev, unsigned long sector,
- unsigned long nsect, char *buffer, int write) //扇区访问函数
- {
- unsigned long offset = sector*sect_size;
- unsigned long nbytes = nsect*sect_size;
-
- if ((offset + nbytes) > dev->size) {
- printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
- return;
- }
- if (write)
- memcpy(dev->data + offset, buffer, nbytes); //对内存读写
- else
- memcpy(buffer, dev->data + offset, nbytes);
- }
-
- /*
- * The simple form of the request function.
- */
- static void blk_request(struct request_queue *q) //实现读写请求处理函数
- {
- struct request *req;
-
- req = blk_fetch_request(q); //从队列中取出一个请求
- while (req != NULL) {
- struct blk_dev *dev = req->rq_disk->private_data;
-
- blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
-
- if(!__blk_end_request_cur(req, 0)) //判断请求队列是否为空
- {
- req = blk_fetch_request(q); //嵌套处理
- }
- }
- }
-
- /*
- * The device operations structure.
- */
- static struct block_device_operations blk_ops = {
- .owner = THIS_MODULE,
- };
-
-
- /*
- * Set up our internal device.
- */
- static void setup_device()
- {
- /*
- * Get some memory.
- */
- dev->size = nsectors*sect_size; //获取设备大小
- dev->data = vmalloc(dev->size); //获取数据指针
- if (dev->data == NULL) {
- printk (KERN_NOTICE "vmalloc failure.\n");
- return;
- }
-
- dev->queue = blk_init_queue(blk_request, NULL); //请求队列初始化,blk_request是处理上层传下来的请求的
- if (dev->queue == NULL)
- goto out_vfree;
-
- blk_queue_logical_block_size(dev->queue, sect_size); //指明扇区大小
- dev->queue->queuedata = dev;
- /*
- * And the gendisk structure.
- */
- dev->gd = alloc_disk(1); //为块设备分配gendisk结构,并初始化
- if (! dev->gd) {
- printk (KERN_NOTICE "alloc_disk failure\n");
- goto out_vfree;
- }
- dev->gd->major = major;
- dev->gd->first_minor = 0;
- dev->gd->fops = &blk_ops;
- dev->gd->queue = dev->queue;
- dev->gd->private_data = dev;
- sprintf (dev->gd->disk_name, "simp_blk%d", 0);
- set_capacity(dev->gd, nsectors*(sect_size/sect_size));
- add_disk(dev->gd); //注册块设备
- return;
-
- out_vfree:
- if (dev->data)
- vfree(dev->data);
- }
-
- static int __init blk_init(void)
- {
- /*
- * Get registered.
- */
- major = register_blkdev(major, "blk"); //注册块设备驱动,major若为0会自动分配
- if (major <= 0) {
- printk(KERN_WARNING "blk: unable to get major number\n");
- return -EBUSY;
- }
-
- dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL); //分配一个blk_dev空间
- if (dev == NULL)
- goto out_unregister;
-
- setup_device(); //调用函数
-
- return 0;
-
- out_unregister:
- unregister_blkdev(major, "sbd");
- return -ENOMEM;
- }
-
- static void blk_exit(void)
- {
-
- if (dev->gd) {
- del_gendisk(dev->gd);
- put_disk(dev->gd);
- }
- if (dev->queue)
- blk_cleanup_queue(dev->queue);
- if (dev->data)
- vfree(dev->data);
-
- unregister_blkdev(major, "blk");
- kfree(dev);
- }
-
- module_init(blk_init);
- module_exit(blk_exit);
11、 简单块设备驱动设计
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/errno.h>
- #include <linux/blkdev.h>
- #include <linux/bio.h>
- #include <linux/string.h>
- #include <asm/uaccess.h>
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/genhd.h>
-
- MODULE_LICENSE("GPL");
-
- static int major = 0;
-
- static int sect_size = 512;
- static int nsectors = 1024;
-
- struct blk_dev {
- int size;
- u8 *data;
- struct request_queue *queue;
- struct gendisk *gd;
- };
-
- struct blk_dev *dev;
-
- static void blk_transfer(struct blk_dev *dev, unsigned long sector, unsigned long nsect,char *buffer, int write)
- {
- unsigned long offset = sector * sect_size;
- unsigned long nbyte = nsect * sect_size;
-
- if((offset + nbyte) > dev->size)
- {
- printk(KERN_NOTICE"Beyond-end write (%ld %ld)\n", offset, nbyte);
- return;
- }
-
- if(write)
- {
- memcpy(dev->data + offset, buffer, nbyte);
- }
- else
- {
- memcpy(buffer, dev->data + offset, nbyte);
- }
- }
-
- void blk_request(struct request_queue *q)
- {
- struct request *req;
- req = blk_fetch_request(q);
-
- while(req != NULL)
- {
- struct blk_dev *dev = req->rq_disk->private_data;
- //处理该请求
- blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
-
- if(!__blk_end_request_cur(req, 0))
- req = blk_fetch_request(q);
- }
-
- }
-
- static struct block_device_operations blk_ops = {
- .owner = THIS_MODULE,
- };
-
- static void setup_device(void)
- {
- dev->size = nsectors * sect_size;
- dev->data = vmalloc(dev->size);
- if(dev->data == NULL) {
- printk(KERN_NOTICE "vmalloc failure.\n");
- return;
- }
-
- dev->queue = blk_init_queue(blk_request, NULL);
- if(dev->queue == NULL)
- {
- goto out_vfree;
- }
-
- blk_queue_logical_block_size(dev->queue, sect_size);
- dev->queue->queuedata = dev;
-
- dev->gd = alloc_disk(1);
- if(!dev->gd)
- {
- printk(KERN_NOTICE "alloc_disk failure\n");
- goto out_vfree;
- }
-
- dev->gd->major = major;
- dev->gd->first_minor = 0;
- dev->gd->fops = &blk_ops;
- dev->gd->queue = dev->queue;
- dev->gd->private_data = dev;
- sprintf(dev->gd->disk_name, "simp_blk%d", 0);
-
- set_capacity(dev->gd, nsectors);
- add_disk(dev->gd);
- return;
-
- out_vfree:
- if(dev->data)
- vfree(dev->data);
- }
-
- static int __init blk_init(void)
- {
- major = register_blkdev(0, "blk");
- if(major <= 0)
- {
- printk(KERN_WARNING "register blk dev fail!\n");
- return -EBUSY;
- }
-
- dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
- if(dev == NULL)
- goto out_unregister;
-
- setup_device();
-
- return 0;
- out_unregister:
- unregister_blkdev(major, "sbd");
- return -ENOMEM;
- }
-
- static void blk_exit(void)
- {
- del_gendisk(dev->gd);
- blk_cleanup_queue(dev->queue);
- vfree(dev->data);
- unregister_blkdev(major, "blk");
- kfree(dev);
- }
-
- module_init(blk_init);
- module_exit(blk_exit);
错误总结:一开始写的时候,编译完加载进内核。直接死机,因为代码有点长。就从逻辑处一点一点注释掉来分析,果然有好多错误。
第一次遇到的问题,内核中常用goto跳转处理错误情况。如果跳转处前面没有return就坑爹了,第一次遇到找了好久