linux块设备驱动之实例
2023-09-14 09:08:54 时间
前两篇blog已经基本熟悉了块设备的相关结构,这里来总结下ldd3中提到的一些块设备驱动例题。
1、注冊:向内核注冊个块设备驱动。事实上就是用主设备号告诉内核这个代表块设备驱动
sbull_major = register_blkdev(sbull_major, "sbull"); if (0 >= sbull_major){ printk(KERN_WARNING "sbull: unable to get major number!\n"); return -EBUSY; }
2、定义设备结构体:
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 time; // 用来模拟介质改变 };
3、初始化设备结构体:
memset(dev, 0, sizeof(struct sbull_dev)); dev->size = nsectors * hardsect_size; dev->data = vmalloc(dev->size); if (dev->data == NULL){ printk(KERN_NOTICE "vmalloc failure.\n"); return; } spin_lock_init(&dev->lock);//初始化自旋锁,为了下一步的队列分配
4、创建设备的请求队列:
dev->queue = blk_init_queue(sbull_request, &dev->lock);
5、分配、初始化及安装对应的gendisk结构:
dev->gd = alloc_disk(SBULL_MINORS); if (!dev->gd) { printk (KERN_NOTICE "alloc_disk failure.\n"); goto out_vfree; } dev->gd->major = sbull_major; dev->gd->first_minor = which*SBULL_MINORS; dev->gd->fops = &sbull_ops; dev->gd->queue = dev->queue; dev->gd->private_data= dev; snprintf(dev->gd->disk_name, 32, "sbull%c", which + 'a'); set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));//使用KERNEL_SECTOR_SIZE本地常量,进行内核512字节扇区到实际使用的扇区大小转换 add_disk(dev->gd);
SBULL_MINORS是每一个设备所支持的次设备号的数量。地一个设备名为 sbulla,第二个为sbullb.用户空间能够加入分区,第二个设备上的第三个分区可能是 /dev/sbullb3。
6、设置队列支持的扇区大小
通知内核设备所支持的扇区大小。硬件扇区大小作为一个參数放在队列中。而不是在gendisk中。当分配好队列后就要立即调用以下函数:
blk_queue_hardsect_size(dev->queue, hardset_size);
调用了上面的函数后。内核就会对我们的设备使用设定的硬件扇区大小。全部的I/O请求毒定位在硬件扇区的開始位置。而且每一个请求的大小都将是扇区大小的整数倍。
记住:内核总是觉得扇区大小是512字节,因此必须将全部的扇区数进行转换。
7、实现操作函数:
打开设备函数:
static int sbull_open(struct inode *inode, struct file *filp) { struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; del_timer_sync(&dev->timer);//移除定时器 filp->private_data = dev; spin_lock(&dev->lock); if (!dev->users) check_disk_change(inode->i_bdev);//检查驱动器中的介质是否改变 dev->users++;// 添加用户计数 spin_unlock(&dev->lock); return 0; }
关闭设备函数:
static int sbull_release(struct inode *inode, struct file *filp) { struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; spin_lock(&dev->lock); dev->users--; if (!dev->users){ dev->timer.expires = jiffies + INVALIDATE_DELAY;//设置定时器 add_timer(&dev->timer); } spin_unlock(&dev->lock); return 0; }
其它的函数也是一样实现,和字符设备驱动的类似。
这里就不写了,接下来看看核心部分,对于一个块设备驱动来说核心部分就是请求,差点儿全部的重心都在请求函数;
8、处理请求操作
dev->queue = blk_queue_init(&sbull_request, &dev->lock); static void sbull_request(request_queue_t *q) { struct request *req; while((req = elv_next_request(q)) != NULL){//获取队列中第一个未完毕的请求。没有则返回NULL。处理完后不删除该请求 struct sbull_dev *dev = req->rq_disk->private_data; if (! blk_fs_request(req)){// 推断是否是一个文件系统请求。即是不是块设备请求 printk(KERN_NOTICE "skip non-fs request.\n"); end_request(req, 0); continue; } //sbull_transfer()函数是真正的处理块设备请求函数 sbull_transfer(dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req)); end_request(req, 1); } } void end_request(struct request* req, int succeeded); // sector開始扇区的索引號,指的是512字节的扇区,假设是2048字节的扇区,则要sector/4再传递 // nsect 表示要传递多少个扇区。 buffer 数据缓存的地址指针。write 表示数据传递的方向。即:read/write; 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); }
转载地址: linux块设备驱动之实例
相关文章
- linux - python2.6.6 升级到python2.7.14
- 简单实例讲解linux的module模块编译步骤
- linux(centos8):jmeter5.3并发测试实例(参数在范围内随机取值)
- linux设置允许和禁止访问的IP host.allow 和 host.deny
- phpStudy for Linux (lnmp+lamp一键安装包)
- 【网址收藏】linux wget 命令常用参数用法详解(附实例说明)
- 【项目实战】Linux使用Nginx搭建静态资源服务器
- 【华为云技术分享】Linux内核补丁源码分析(1)
- 【Linux 内核 内存管理】memblock 分配器编程接口 ⑤ ( memblock_free 函数 | memblock_remove_range 函数 )
- 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )
- 【Android 逆向】Linux 文件分类 ( 普通文件 | 目录文件 | 链接文件 | 字符设备文件 | 管道文件 | 块设备文件 )
- Linux I2C设备驱动编写(三)-实例分析AM3359
- linux shell seq命令详解
- L29.linux命令每日一练 -- 第四章 文本处理三剑客 -- sed命令
- linux系统下grub.cfg详解和实例操作
- linux基本功系列之head命令实战
- linux安装selenium、chromedriver、Chrome浏览器、BrowserMob Proxy(代理)爬虫爬站环境安装及测试实例
- Linux常用命令——grep命令和通配符以及