#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #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> static int sbull_major = 0;/* 块设备号,0就自动分配*/ module_param(sbull_major, int, 0);//模参数,这个主设备号可以在加载模块时指定一个值 static int hardsect_size = 512; /* 硬件扇区大小 */ module_param(hardsect_size, int, 0); /* 同上 */ static int nsectors = 1024;/* 硬件扇区数目 */ module_param(nsectors, int, 0); /* 同上 */ static int ndevices = 4; module_param(ndevices,int,0); /* The different "request modes " we can use. */ enum { RM_SIMPLE = 0, RM_FULL = 1, RM_NOQUEUE = 2, }; static int request_mode = RM_SIMPLE; module_param(request_mode, int, 0); #define SBULL_MINORS 16 #define KERNEL_SECTOR_SIZE 512 #define INVALIDATE_DELAY 30*HZ /*sbull 设备结构*/ struct sbull_dev{ int size; /* 以扇区为单位, 设备的大小 */ u8 *data; /* 数据数组 */ short users; /* 用户数目 */ short media_change; /* 介质改变标志 */ spinlock_t lock; /* 用于互斥 */ struct request_queue *queue; /* 设备请求队列 */ struct gendisk *gd; /* gendisk结构 */ struct timer_list timer; /* 用来模拟介质改变 */ }; static struct sbull_dev *Devices = NULL; /* * Handle an I/O request. 处理 I/O 拷贝数据的 函数 */ static void sbull_transfer(struct sbull_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write) { unsigned long offset = sector * KERNEL_SECTOR_SIZE; unsigned long nbytes = nsect * KERNEL_SECTOR_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 sbull_request(struct request_queue *q) { struct request *req; //定义请求结构体 req = blk_fetch_request(q); while(req != NULL){ struct sbull_dev *dev = req->rq_disk->private_data;//获得队列中第一个未完成请求 if(req->cmd_type != REQ_TYPE_FS){ //判断是否为文件系统请求 printk(KERN_NOTICE "Skip non-fs request\n"); blk_end_request_all(req, -EIO); //通知请求处理失败,EIO为i/o error. continue; } sbull_transfer(dev, blk_rq_pos(req), //扇区光标所在的位置 blk_rq_cur_sectors(req), //需要传输的扇区数目 req->buffer, //要传输或者接受的数据缓存区 rq_data_dir(req)); //获取传输方向,0表示读,1表示写 if(!__blk_end_request_cur(req, 0)){ //下一个请求 req = NULL; } } } /* * Transfer a single BIO. bio处理函数 */ static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio) { int i; struct bio_vec *bvec; //定义实际的 vec 列表 sector_t sector = bio->bi_sector; //定义要传输的第一个扇区 bio_for_each_segment(bvec,bio,i){ //下面的宏遍历bio的每一段,获得一个内核虚拟地址来存取缓冲 char *buffer = __bio_kmap_atomic(bio,i,KM_USER0);//通过kmap_atomic()函数获得返回bio的第i个缓冲区的虚拟地址 sbull_transfer(dev, sector,// 开始扇区的索引号 bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE, // 需要传输的扇区数 buffer, // 传输数据的缓冲区指针 bio_data_dir(bio) == WRITE); // 传输方向,0表述从设备读,非0从设备写 sector += bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE;//返回扇区数 __bio_kunmap_atomic(bio,KM_USER0);//返回由 __bio_kmap_atomic()获得的内核虚拟地址 } return 0; } /* * Transfer a full request.请求处理函数 */ static int sbull_xfer_request(struct sbull_dev *dev, struct request *req) { struct bio *bio; int nsect = 0; __rq_for_each_bio(bio, req) { //此宏遍历请求中的每个bio,传递用于sbull_xfer_bio()传输的指针 sbull_xfer_bio(dev, bio); //调用 bio 处理函数 nsect += bio->bi_size / KERNEL_SECTOR_SIZE; //传递的字节数/扇区大小等于扇区数 } return nsect; } /* * Smarter request function that "handles clustering". */ static void sbull_full_request(struct request_queue *q) { struct request *req; struct sbull_dev *dev = q->queuedata; req = blk_fetch_request(q); /* 遍历每个请求 */ while ( req != NULL ) { if(req->cmd_type != REQ_TYPE_FS) {//判断请求是否是个文件系统请求 printk(KERN_NOTICE "Skip non-fs request\n"); blk_end_request_all(req, -EIO); //该请求是一个I/O error continue; } sbull_xfer_request(dev, req); //调用请求处理函数 if (!blk_end_request_cur(req, 0)) { //req指向下一个请求 req = NULL; } } } /* * The direct make request version. */ static int sbull_make_request(struct request_queue *q, struct bio *bio) { struct sbull_dev *dev = q->queuedata; int status; status = sbull_xfer_bio(dev,bio); bio_endio(bio, status); // bio_endio()函数通知处理结束 return 0; } /* * open()函数 */ static int sbull_open(struct block_device *bd, fmode_t mode) { struct sbull_dev *dev = bd->bd_disk->private_data; del_timer_sync(&dev->timer); /* 介质移除定时器: 销毁定时器 */ spin_lock(&dev->lock); /* lock */ if(!dev->users) check_disk_change(bd); dev->users++; spin_unlock(&dev->lock); /* unlock */ return 0; } static int sbull_release(struct gendisk *gd, fmode_t mode) { struct sbull_dev *dev = gd->private_data; spin_lock(&dev->lock); dev->users--; if(!dev->users){ dev->timer.expires = jiffies + INVALIDATE_DELAY; add_timer(&dev->timer); /* 介质移除定时器: 加载定时器 ,30s */ } spin_unlock(&dev->lock); return 0; } /* * Look for a (simulated) media change. */ int sbull_media_change(struct gendisk *gd) { struct sbull_dev *dev = gd->private_data; return dev->media_change; } /* * Revalidate.we do not take the lock here,for fear of deadlocking with open. * That needs to be reevaluated. * 调用此函数内核将试着重新读取分区表,在这里这个函数这是简单的重置 media_change 的标志位,并 * 清除内存空间以模拟插入一张磁盘 */ int sbull_revalidate(struct gendisk *gd) { struct sbull_dev *dev = gd->private_data; if(dev->media_change){ dev->media_change = 0; memset(dev->data, 0, dev->size); } return 0; } /* * The "invalidte"function runs out of the device timer;it sets a flag to  * simulate the removal of the media. */ void sbull_invalidate(unsigned long ldev) { struct sbull_dev *dev = (struct sbull_dev *)ldev; spin_lock(&dev->lock); if(dev->users || !dev->data) printk (KERN_WARNING "sbull: timer sanity check failed\n"); else dev->media_change = 1; spin_unlock(&dev->lock); } /* *ioctl: *暂时处理一个命令: 对设备的物理信息的查询请求 */ int sbull_ioctl(struct block_device *bd, fmode_t mode, unsigned cmd, unsigned long arg) { long size; struct hd_geometry geo; struct sbull_dev *dev = bd->bd_disk->private_data; switch (cmd) { case HDIO_GETGEO: size = dev->size*(hardsect_size / KERNEL_SECTOR_SIZE); geo.cylinders = (size & ~0x3f) >> 6; geo.sectors = 16; geo.heads = 4; geo.start = 4; if(copy_to_user((void __user*)arg, &geo, sizeof(geo))) return -EFAULT; return 0; } return -ENOTTY; } /* 获取几何信息 */ static int sbull_getgeo(struct block_device *bd, struct hd_geometry *geo) { long size; struct sbull_dev *dev = bd->bd_disk->private_data; size = dev->size *(hardsect_size / KERNEL_SECTOR_SIZE); geo->cylinders = (size & ~0x3f) >> 6; geo->heads = 4; geo->sectors = 16; geo->start = 4; return 0; } /* * The device operations structure. */ static struct block_device_operations sbull_ops = { .owner = THIS_MODULE, .open = sbull_open, .release = sbull_release, .media_changed = sbull_media_change, /* 用户检查介质是否被改变(移除) */ .revalidate_disk = sbull_revalidate, .ioctl = sbull_ioctl, .getgeo = sbull_getgeo }; /*初始化 sbull_dev 数据结构的具体实现*/ static void sbull_setup_device(struct sbull_dev *dev) { printk(KERN_INFO "sbull: step into into setup_device\n"); memset(dev, 0, sizeof(struct sbull_dev)); dev->size = nsectors * hardsect_size; //整个块设备大小1024(扇区个数) * 512(扇区大小) dev->data = vmalloc(dev->size); /* 开辟虚拟存储空间 */ if(dev->data == NULL){ printk(KERN_NOTICE "vmalloc failure\n"); return; } spin_lock_init(&dev->lock); /* 初始化自旋锁 */ init_timer(&dev->timer); /* 初始化定时器 */ dev->timer.data = (unsigned long)dev; dev->timer.function = sbull_invalidate; /* 超时处理函数 */ /* * The I/O queue, depending on whether we are using our own * make_request function or not. */ switch (request_mode) { case RM_NOQUEUE: dev->queue = blk_alloc_queue(GFP_KERNEL); /* 分配“请求队列” */ if(dev->queue == NULL) goto out_vfree; blk_queue_make_request(dev->queue, sbull_make_request);/*绑定"制造请求"函数 */ break; case RM_FULL: dev->queue = blk_init_queue(sbull_full_request, &dev->lock); /*请求队列初始化*/ if(dev->queue == NULL) goto out_vfree; break; case RM_SIMPLE: dev->queue = blk_init_queue(sbull_request, &dev->lock);/*请求队列初始化*/ if(dev->queue == NULL) goto out_vfree; break; default: printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode); } blk_queue_logical_block_size(dev->queue, hardsect_size);/* 硬件扇区尺寸设置 */ dev->queue->queuedata = dev; dev->gd = alloc_disk(SBULL_MINORS); if(!dev->gd){ /* 动态分配 gendisk 结构体*/ printk(KERN_NOTICE "alloc_disk failure\n"); goto out_vfree; } dev->gd->major = sbull_major; /* 主设备号 */ dev->gd->first_minor = SBULL_MINORS; /* 次设备号 */ dev->gd->fops = &sbull_ops; /* 块设备操作结构体 */ dev->gd->queue = dev->queue; /* 请求队列 */ dev->gd->private_data = dev; /* 私有数据 */ snprintf(dev->gd->disk_name, 32, "sbull"); /* 次设备的名字 */ /* 每个请求的大小都是扇区大小的整数倍,内核总是认为扇区大小是512字节,因此必须进行转换 */ set_capacity(dev->gd, nsectors * (hardsect_size / KERNEL_SECTOR_SIZE)); add_disk(dev->gd);/* 完成以上初始化后,调用 add_disk 函数来注册这个磁盘设备 */ return; out_vfree: if(dev->data) vfree(dev->data); } static int __init sbull_init(void) { int i; printk(KERN_WARNING "sbull: start init\n"); // 注册块设备,第一个参数是设备号,0为动态分配,第二个参数是设备名 sbull_major = register_blkdev(sbull_major, "sbull"); if (sbull_major <= 0) { printk(KERN_WARNING "sbull: unable to get major number\n"); return -EBUSY; } printk(KERN_INFO "sbull: start kmalloc\n"); Devices = kmalloc(ndevices * sizeof(struct sbull_dev), GFP_KERNEL);/* 为块核心数据结构 sbull_dev 分配空间 */ if(Devices == NULL) goto out_unregister; printk(KERN_WARNING "sbull: start setup_device\n"); sbull_setup_device(Devices); /* 初始化 sbull_dev 核心数据结构 ,并add_disk*/ return 0; out_unregister: unregister_blkdev(sbull_major, "sbull"); return -ENOMEM; } static void sbull_exit(void) { struct sbull_dev *dev = Devices; del_timer_sync(&dev->timer); if (dev->gd) { del_gendisk(dev->gd); put_disk(dev->gd); } if (dev->queue) { if (request_mode == RM_NOQUEUE) kobject_put(&(dev->queue)->kobj); else blk_cleanup_queue(dev->queue); } if (dev->data) vfree(dev->data); unregister_blkdev(sbull_major, "sbull"); kfree(Devices); } MODULE_LICENSE("Dual BSD/GPL"); module_init(sbull_init); module_exit(sbull_exit);

linux 块设备驱动(四)——简单的sbull实例的更多相关文章

  1. linux块设备驱动之实例

    1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major  =  register_blkdev(sbull_major, "sbull&quo ...

  2. Linux块设备驱动详解

    <机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...

  3. Linux块设备驱动(一) _驱动模型

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...

  4. linux块设备驱动

    块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ...

  5. linux块设备驱动---概念与框架(转)

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  6. Linux 块设备驱动 (一)

    1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...

  7. Linux块设备驱动(二) _MTD驱动及其用户空间编程

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  8. Linux块设备驱动_WDS

    推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步 ...

  9. linux 块设备驱动 (三)块设备驱动开发

    一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...

  10. linux块设备驱动(一)——块设备概念介绍

    本文来源于: 1. http://blog.csdn.net/jianchi88/article/details/7212370 2. http://blog.chinaunix.net/uid-27 ...

随机推荐

  1. RSA签名

    RSA签名: /** * RSA签名 * @param content    待签名数据 * @param privateKey 商户私钥 * @return 签名值 */public static ...

  2. C# 模拟windows文件名称排序(使用windows自带dll)

    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] private static extern int StrCmpLogi ...

  3. APUE 学习笔记(八) 线程同步

    1. 进程的所有信息对该进程内的所有线程都是共享的 包括 可执行的程序文本.程序全局内存.堆内存以及文件描述符 线程包含了表示进程内执行环境必需的信息,包括线程ID.寄存器值.栈.调度优先级和策略.信 ...

  4. LOJ#2304. 「NOI2017」泳池

    $n \leq 1e9$底边长的泳池,好懒啊泥萌自己看题吧,$k \leq 1000$.答案对998244353取膜. 现在令$P$为安全,$Q$为危险的概率.刚好$K$是极其不好算的,于是来算$\l ...

  5. 使用iframe实现提交表单不刷新页面

    正常情况下,当你向服务器发送数据的时候,你的浏览器将会打开action页面,并且不会重回到当前页面.但是有的时候,我们因为各种各样的要求,而不希望浏览器在提交数据的时候去刷新当前的页面或者转向到新的页 ...

  6. 利用例子来理解spring的面向切面编程(使用@Aspect)

    上篇的例子,自动装配和自动检测Bean是使用注解的方式处理的,而面向切面编程是使用aop标签处理的,给我感觉就像中西医参合一样. 现在就来优化优化,全部使用注解的方式处理. 1.工程图:

  7. hadoop之hdfs及其工作原理

    hadoop之hdfs及其工作原理 (一)hdfs产生的背景 随着数据量的不断增大和增长速度的不断加快,一台机器上已经容纳不下,因此就需要放到更多的机器中,但这样做不方便维护和管理,因此需要一种文件系 ...

  8. maven命令行创建project

    创建普通java project: mvn archetype:generate -DgroupId=com.vincent -DartifactId=Java_Project -DpackageNa ...

  9. 一道简单DP题

    问题: 给定一个整数的数组,相邻的数不能同时选,求从该数组选取若干整数,使得他们的和最大,要求只能使用o(1)的空间复杂度.要求给出伪码. 解答: int maxSum(vector<int&g ...

  10. 关于#!/usr/bin/env python 的用法

    在linux的一些脚本里,需在开头一行指定脚本的解释程序,如: !/usr/bin/env python 再如: !/usr/bin/env perl 那么 env到底有什么用?何时用这个呢?脚本用e ...