Linux-Flash驱动(2)-块设备驱动实例分析

2024-08-31 06:48

本文主要是介绍Linux-Flash驱动(2)-块设备驱动实例分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上一节课中,我们在内存中划分出512kB作为一个块设备,并对它实现读写的操作。现在我们来具体分析这段代码。

 

#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);elsememcpy(buffer, dev->data + offset, nbytes);
}/*
* 读写请求处理函数
*/
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);}}
}/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{int i;struct bio_vec *bvec;sector_t sector = bio->bi_sector;/* Do each segment independently. */bio_for_each_segment(bvec, bio, i) {char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);blk_transfer(dev, sector, bio_cur_bytes(bio)>>9 /* in sectors */, buffer, bio_data_dir(bio) == WRITE);sector += bio_cur_bytes(bio)>>9; /* in sectors */__bio_kunmap_atomic(bio, KM_USER0);}return 0; /* Always "succeed" */
}/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{struct bio *bio;int nsect = 0;__rq_for_each_bio(bio, req) {blk_xfer_bio(dev, bio);nsect += bio->bi_size/sect_size;}return nsect;
}/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = {
.owner            = THIS_MODULE,
};/*
* Set up our internal device.
*/
static void setup_device()
{//计算设备大小dev->size = nsectors*sect_size;dev->data = vmalloc(dev->size);if (dev->data == NULL) {printk (KERN_NOTICE "vmalloc failure.\n");return;}//把块设备放入请求队列中,blk_request用于指明处理这个请求的函数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;//分配gendisk结构dev->gd = alloc_disk(1);if (! dev->gd) {printk (KERN_NOTICE "alloc_disk failure\n");goto out_vfree;}/*初始化alloc_disk*/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)
{/** 注册块设备,申请主设备号*/major = register_blkdev(major, "blk");if (major <= 0) {printk(KERN_WARNING "blk: unable to get major number\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)
{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);nregister_blkdev(major, "blk");kfree(dev);
}module_init(blk_init);
module_exit(blk_exit);	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);elsememcpy(buffer, dev->data + offset, nbytes);
}/*
* 读写请求处理函数
*/
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);}}
}/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{int i;struct bio_vec *bvec;sector_t sector = bio->bi_sector;/* Do each segment independently. */bio_for_each_segment(bvec, bio, i) {char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);blk_transfer(dev, sector, bio_cur_bytes(bio)>>9 /* in sectors */, buffer, bio_data_dir(bio) == WRITE);sector += bio_cur_bytes(bio)>>9; /* in sectors */__bio_kunmap_atomic(bio, KM_USER0);}return 0; /* Always "succeed" */
}/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{struct bio *bio;int nsect = 0;__rq_for_each_bio(bio, req) {blk_xfer_bio(dev, bio);nsect += bio->bi_size/sect_size;}return nsect;
}/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = {
.owner            = THIS_MODULE,
};/*
* Set up our internal device.
*/
static void setup_device()
{//计算设备大小dev->size = nsectors*sect_size;dev->data = vmalloc(dev->size);if (dev->data == NULL) {printk (KERN_NOTICE "vmalloc failure.\n");return;}//把块设备放入请求队列中,blk_request用于指明处理这个请求的函数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;//分配gendisk结构dev->gd = alloc_disk(1);if (! dev->gd) {printk (KERN_NOTICE "alloc_disk failure\n");goto out_vfree;}/*初始化alloc_disk*/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)
{/** 注册块设备,申请主设备号*/major = register_blkdev(major, "blk");if (major <= 0) {printk(KERN_WARNING "blk: unable to get major number\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)
{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);nregister_blkdev(major, "blk");kfree(dev);
}module_init(blk_init);
module_exit(blk_exit);

 

 

 

这是一个模块的程序,先看看模块初始化里面的blk_init函数,里面做了这几件事:

1、注册一个快设备register_blkdev,如果没有分配到主设备号则打印错误信息

2、然后是申请一个结构,这个结构是用来保存这个块设备信息的,不是每个块设备都有

3、再安装这个设备setup_device,这个函数是自定义的

    3.1完成块设备大小的计算

    3.2把快设备放入请求队列中(IO调度层把请求排序后放入请求队列中,里面的参数blk_request是一个函数,用于指明使用哪个函数对这个请求进行处理)

    3.3指明设备的扇区大小

    3.4然后用alloc_disk函数分配一个gendisk结构(一个驱动可能对于几个块设备,用gendisk来区分)

    3.5紧接着需要对这个结构进行初始化,如下:

 

	/*初始化alloc_disk*/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));//扇区数

3.6注册这个块设备

 

第二个重要的函数是实现读写请求处理,读写请求通过blk_request函数来实现:

1、使用blk_fetch_request从队列中取出要处理的一个请求

2、使用blk_transfer实现对对应扇区的硬件操作,比如读和写,应该这里的块设备是内存模拟的,所以使用的是memcpy函数

3、使用__blk_end_request_cur判断请求队列是否还有请求要处理,如果有则继续处理,没有退出。
 

更多Linux资料及视频教程点击这里

这篇关于Linux-Flash驱动(2)-块设备驱动实例分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123196

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

MySQL的配置文件详解及实例代码

《MySQL的配置文件详解及实例代码》MySQL的配置文件是服务器运行的重要组成部分,用于设置服务器操作的各种参数,下面:本文主要介绍MySQL配置文件的相关资料,文中通过代码介绍的非常详细,需要... 目录前言一、配置文件结构1.[mysqld]2.[client]3.[mysql]4.[mysqldum

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全