块设备的读写框架

生成块设备

我们以虚拟文件的接口,来看这个框架;因为这是从从应用层到内核的必经之路;使用vfs_mknod来生成块设备文件,并初始化fops

mknod
    do_mknodat
        vfs_mknod
            shmem_mknod
                shmem_get_inode
                    init_special_inode
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
	inode->i_mode = mode;
	if (S_ISCHR(mode)) {
		inode->i_fop = &def_chr_fops;
		inode->i_rdev = rdev;
	} else if (S_ISBLK(mode)) {
		if (IS_ENABLED(CONFIG_BLOCK))
			inode->i_fop = &def_blk_fops;
		inode->i_rdev = rdev;
	} else if (S_ISFIFO(mode))
		inode->i_fop = &pipefifo_fops;
	else if (S_ISSOCK(mode))
		;	/* leave it no_open_fops */
	else
		printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
				  " inode %s:%lu\n", mode, inode->i_sb->s_id,
				  inode->i_ino);
}
EXPORT_SYMBOL(init_special_inode);

const struct file_operations def_blk_fops = {
	.open		= blkdev_open,
	.release	= blkdev_release,
	.llseek		= blkdev_llseek,
	.read_iter	= blkdev_read_iter,
	.write_iter	= blkdev_write_iter,
	.iopoll		= iocb_bio_iopoll,
	.mmap		= blkdev_mmap,
	.fsync		= blkdev_fsync,
	.unlocked_ioctl	= blkdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= compat_blkdev_ioctl,
#endif
	.splice_read	= filemap_splice_read,
	.splice_write	= iter_file_splice_write,
	.fallocate	= blkdev_fallocate,
};

打开块设备

open系统调用打开字符设备文件时,会调用do_dentry_open,里面的f->f_op = fops_get(inode->i_fop);就是把上面init_special_inode里的inode->i_fop = &def_blk_fops赋给f->f_op;其中的open为blkdev_open;会调用块设备驱动的open

open
    do_sys_open
        do_sys_openat2
            do_filp_open
                path_openat
                    vfs_open
                        do_dentry_open
static int do_dentry_open(struct file *f,
			  struct inode *inode,
			  int (*open)(struct inode *, struct file *))
{
	static const struct file_operations empty_fops = {};
	int error;
 
	path_get(&f->f_path);
	f->f_inode = inode;
	f->f_mapping = inode->i_mapping;
 
	/* Ensure that we skip any errors that predate opening of the file */
	f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
 
	if (unlikely(f->f_flags & O_PATH)) {
		f->f_mode = FMODE_PATH | FMODE_OPENED;
		f->f_op = &empty_fops;
		return 0;
	}
 
	/* Any file opened for execve()/uselib() has to be a regular file. */
	if (unlikely(f->f_flags & FMODE_EXEC && !S_ISREG(inode->i_mode))) {
		error = -EACCES;
		goto cleanup_file;
	}
 
	if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
		error = get_write_access(inode);
		if (unlikely(error))
			goto cleanup_file;
		error = __mnt_want_write(f->f_path.mnt);
		if (unlikely(error)) {
			put_write_access(inode);
			goto cleanup_file;
		}
		f->f_mode |= FMODE_WRITER;
	}
 
	/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
		f->f_mode |= FMODE_ATOMIC_POS;
 
	f->f_op = fops_get(inode->i_fop);
	if (WARN_ON(!f->f_op)) {
		error = -ENODEV;
		goto cleanup_all;
	}
 
	error = security_file_open(f);
	if (error)
		goto cleanup_all;
 
	error = break_lease(locks_inode(f), f->f_flags);
	if (error)
		goto cleanup_all;
 
	/* normally all 3 are set; ->open() can clear them if needed */
	f->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
	if (!open)
		open = f->f_op->open;
	if (open) {
		error = open(inode, f);
		if (error)
			goto cleanup_all;
	}
	f->f_mode |= FMODE_OPENED;
	if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
		i_readcount_inc(inode);
	if ((f->f_mode & FMODE_READ) &&
	     likely(f->f_op->read || f->f_op->read_iter))
		f->f_mode |= FMODE_CAN_READ;
	if ((f->f_mode & FMODE_WRITE) &&
	     likely(f->f_op->write || f->f_op->write_iter))
		f->f_mode |= FMODE_CAN_WRITE;
 
	f->f_write_hint = WRITE_LIFE_NOT_SET;
	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
 
	/* NB: we're sure to have correct a_ops only after f_op->open */
	if (f->f_flags & O_DIRECT) {
		if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)
			return -EINVAL;
	}
	/*
	 * XXX: Huge page cache doesn't support writing yet. Drop all page
	 * cache for this file before processing writes.
	 */
	if ((f->f_mode & FMODE_WRITE) && filemap_nr_thps(inode->i_mapping))
		truncate_pagecache(inode, 0);
 
	return 0;
 
cleanup_all:
	if (WARN_ON_ONCE(error > 0))
		error = -EINVAL;
	fops_put(f->f_op);
	if (f->f_mode & FMODE_WRITER) {
		put_write_access(inode);
		__mnt_drop_write(f->f_path.mnt);
	}
cleanup_file:
	path_put(&f->f_path);
	f->f_path.mnt = NULL;
	f->f_path.dentry = NULL;
	f->f_inode = NULL;
	return error;
}

读块设备

不管有没有文件系统,应用层都是通过虚拟文件系统的接口来到内核的,通过open后;vfs_read里的file->f_op->read_iter就等于def_blk_fops的read_iter成员

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
	ssize_t ret;

	if (!(file->f_mode & FMODE_READ))
		return -EBADF;
	if (!(file->f_mode & FMODE_CAN_READ))
		return -EINVAL;
	if (unlikely(!access_ok(buf, count)))
		return -EFAULT;

	ret = rw_verify_area(READ, file, pos, count);
	if (ret)
		return ret;
	if (count > MAX_RW_COUNT)
		count =  MAX_RW_COUNT;

	if (file->f_op->read)
		ret = file->f_op->read(file, buf, count, pos);
	else if (file->f_op->read_iter)
		ret = new_sync_read(file, buf, count, pos);
	else
		ret = -EINVAL;
	if (ret > 0) {
		fsnotify_access(file);
		add_rchar(current, ret);
	}
	inc_syscr(current);
	return ret;
}

简化的read框图如下

写块设备

其中file->f_op->write_iter就等于def_blk_fops的write_iter成员

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
	ssize_t ret;

	if (!(file->f_mode & FMODE_WRITE))
		return -EBADF;
	if (!(file->f_mode & FMODE_CAN_WRITE))
		return -EINVAL;
	if (unlikely(!access_ok(buf, count)))
		return -EFAULT;

	ret = rw_verify_area(WRITE, file, pos, count);
	if (ret)
		return ret;
	if (count > MAX_RW_COUNT)
		count =  MAX_RW_COUNT;
	file_start_write(file);
	if (file->f_op->write)
		ret = file->f_op->write(file, buf, count, pos);
	else if (file->f_op->write_iter)
		ret = new_sync_write(file, buf, count, pos);
	else
		ret = -EINVAL;
	if (ret > 0) {
		fsnotify_modify(file);
		add_wchar(current, ret);
	}
	inc_syscw(current);
	file_end_write(file);
	return ret;
}

简化的write框图如下

 内核实例

比如插上sd卡,生成/dev/mmcblk0p1;然后usb的U盘功能的bind去打开这个块设备,就能在windows上枚举出一个U盘

drivers/usb/gadget/function/storage_common.c里面就会去,判断下/dev/mmcblk0p1这个设备的read/write相关的接口有没有

static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
        int                             ro;
        struct file                     *filp = NULL;
        int                             rc = -EINVAL;
        struct inode                    *inode = NULL;
        loff_t                          size;
        loff_t                          num_sectors;
        loff_t                          min_sectors;
        unsigned int                    blkbits;
        unsigned int                    blksize;

        /* R/W if we can, R/O if we must */
        ro = curlun->initially_ro;
        if (!ro) {
                filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
                if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
                        ro = 1;
        }
        if (ro)
                filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
        if (IS_ERR(filp)) {
                LINFO(curlun, "unable to open backing file: %s\n", filename);
                return PTR_ERR(filp);
        }

        if (!(filp->f_mode & FMODE_WRITE))
                ro = 1;

        inode = file_inode(filp);
        if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
                LINFO(curlun, "invalid file type: %s\n", filename);
                goto out;
        }

        /*
         * If we can't read the file, it's no good.
         * If we can't write the file, use it read-only.
         */
        if (!(filp->f_op->read || filp->f_op->read_iter)) {
                LINFO(curlun, "file not readable: %s\n", filename);
                goto out;
        }
        if (!(filp->f_op->write || filp->f_op->write_iter))
                ro = 1;

        size = i_size_read(inode->i_mapping->host);
        if (size < 0) {
                LINFO(curlun, "unable to find file size: %s\n", filename);
                rc = (int) size;
                goto out;
        }

        if (curlun->cdrom) {
                blksize = 2048;
                blkbits = 11;
        } else if (inode->i_bdev) {
                blksize = bdev_logical_block_size(inode->i_bdev);
                blkbits = blksize_bits(blksize);
        } else {
                blksize = 512;
                blkbits = 9;
        }

        num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
        min_sectors = 1;
        if (curlun->cdrom) {
                min_sectors = 0;
                if (num_sectors >= 256*60*75) {
                        num_sectors = 256*60*75 - 1;
                        LINFO(curlun, "file too big: %s\n", filename);
                        LINFO(curlun, "using only first %d blocks\n",
                                        (int) num_sectors);
                }
        }
        if (num_sectors < min_sectors) {
                LINFO(curlun, "file too small: %s\n", filename);
                rc = -ETOOSMALL;
                goto out;
        }

        if (fsg_lun_is_open(curlun))
                fsg_lun_close(curlun);

        curlun->blksize = blksize;
        curlun->blkbits = blkbits;
        curlun->ro = ro;
        curlun->filp = filp;
        curlun->file_length = size;
        curlun->num_sectors = num_sectors;
        LDBG(curlun, "open backing file: %s\n", filename);
        return 0;

out:
        fput(filp);
        return rc;
}

根据最上面的def_blk_fops;5.4.195的内核是没有read成员的,只有read_iter;所以就会打印“file not readable”;需做如下修改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513054.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SV学习笔记(三)

类和对象概述 类和对象 面向对象的编程语言更符号人对自然语言的理解&#xff08;属性property和功能function&#xff09;。 这个世界由无数的类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;构成的。 类是将相同的个体抽象出来的描述方式&#xff0c…

【Servlet】thymeleaf快速入门

文章目录 一、thymeleaf介绍二、入门案例 一、thymeleaf介绍 Thymeleaf&#xff1a;视图模板技术 在index.html页面上加载java内存中的fruitList数据&#xff0c;这个过程我们称之为渲染&#xff08;render&#xff09;。 thymeleaf是用来帮助我们做视图渲染的一个技术。 二…

Python学习从0到1 day20 第二阶段 面向对象 ③ 继承

循此苦旅&#xff0c;以达天际 —— 24.4.3 一、继承的基础语法 学习目标&#xff1a; ① 理解继承的概念 ② 掌握继承的使用方式 ③ 掌握pass关键字的作用 单继承 语法&#xff1a; class 类名(父类名): 类内容体 继承分为&#xff1a;单继承和多继承 继承表示&#xff1a;将从…

redis---HyperLogLog

HyperLogLog是一个基数统计的算法&#xff0c;如果集合中的每个元素都是唯一且不重复的&#xff0c;那么这个集合的基数就是集合中元素的个数 它的原理是使用随机算法来计算&#xff0c;通过牺牲一定的精确度&#xff0c;来换取更小的内存消耗&#xff0c;优点就是占用内存小。…

“帮助“Java成长的世界级大师不简单!

文章目录 初探编程&#xff1a;“天啊&#xff0c;真酷&#xff0c;程序真的能学习。”哺育Java成长&#xff0c;成为Java幕后英雄出书《Effective Java》斩获Jolt图书大奖 是谁&#xff1f;作品一出版就获得著名的Jolt图书大奖&#xff0c;每一版本豆瓣评分均超9.0&#xff01…

[已解决] slam_gmapping: undefined symbol: _ZN8GMapping14sampleGaussianEdm问题

之前用的好好的gampping建图功能包&#xff0c;今天突然不能用了&#xff0c;运行报错如下&#xff1a; /opt/ros/noetic/lib/gmapping/slam_gmapping: symbol lookup error: /opt/ros/noetic/lib/gmapping/slam_gmapping: undefined symbol: _ZN8GMapping14sampleGaussianEdm …

ShardingJdbc兼容达梦

ShardingJdbc兼容达梦 ​ 本章详细说ShardingJdbc和达梦数据库的扩展和配置问题&#xff0c;ShardingJdbc和DruidDataSource、Mybatis整合的兼容、冲突问题&#xff0c;以及这些问题的解决方案。&#xff0c;干货满满&#xff0c;全网独一份&#xff0c;建议收藏。本章不说Sha…

数码论坛系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)电子科技数码爱好者交流信息新闻畅聊讨论评价

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读300套最新项目持续更新中..... 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含ja…

680.验证回文串II-力扣

680.验证回文串II-力扣 给你一个字符串 s&#xff0c;最多可以从中删除一个字符。 请你判断 s 是否能成为回文字符串&#xff1a;如果能&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false。 示例1&#xff1a; 输入&#xff1a;s “aba” 输出&#xff1a;true示…

Python就业前景如何?薪资待遇怎么样?

前言 Python作为一种高级编程语言&#xff0c;已经在多个领域得到了广泛的应用&#xff0c;包括数据分析、人工智能、Web开发等。随着技术的不断发展和应用领域的不断扩展&#xff0c;Python的就业前景也越来越广阔。 首先&#xff0c;Python在数据分析领域的应用非常广泛。随…

mac | Windows 本地部署 Seata2.0.0,Nacos 作为配置中心、注册中心,MySQL 存储信息

1、本人环境介绍 系统 macOS sonama 14.1.1 MySQL 8.2.0 &#xff08;官方默认是5.7版本&#xff09; Seata 2.0.0 Nacos 2.2.3 2、下载&数据库初始化 默认你已经有 Nacos、MySQL&#xff0c;如果没有 Nacos 请参考我的文章 &#xff1a; Docker 部署 Nacos&#xff08;单机…

滴滴盈利,司机“受伤”

近日&#xff0c;滴滴对外披露了2023年Q4及全年业绩。 财报数据显示&#xff0c;2023年Q4&#xff0c;滴滴实现营收494亿元&#xff0c;同比增长55.4%&#xff0c;净利润达11亿元&#xff1b;2023年全年滴滴实现营收共计1924亿元&#xff0c;同比增长36.6%&#xff0c;净利润达…

springboot对接minio的webhook全过程

前言 近日需要将minio的apache2.0版本给用起来&#xff0c;顺便要完善一下原有的文件上传管理系统&#xff0c;其中很重要的一点是&#xff0c;在原有客户端直传的基础上&#xff0c;再添加 minio 的上传回调给服务端做后续处理。 本文重点在于&#xff0c;介绍整个minio与spr…

MySQL生产环境常见故障及解决方案汇总

MySQL生产环境常见故障及解决方案汇总 1. MySQL主从同步异常故障1.1. 情景说明1.2. 排查过程1.3. 数据同步2. MySQL慢查询故障1. MySQL主从同步异常故障 1.1. 情景说明 MySQL主库网卡需要更换IP地址,并将原IP地址配置为MySQL集群的VIP地址,上层应用程序其实不需要更改连接My…

VUE——生命周期

概念&#xff1a; mounted:挂载 new Vue({el: "#x",data: {},methods: {},mounted() {}, }) 系统会自己调用&#xff0c;不需要我们调用。 案例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><…

JavaScript(五)---【DOM】

零.前言 JavaScript(一)---【js的两种导入方式、全局作用域、函数作用域、块作用域】-CSDN博客 JavaScript(二)---【js数组、js对象、this指针】-CSDN博客 JavaScript(三)---【this指针&#xff0c;函数定义、Call、Apply、函数绑定、闭包】-CSDN博客 JavaScript(四)---【执…

在 Windows 中安装部署并启动连接 MongoDB 7.x(命令行方式启动、配置文件方式启动、将启动命令安装为系统服务实现开机自启)

MongoDB 的下载 下载地址&#xff1a;https://www.mongodb.com/try/download/community 这里需要对 MongoDB 的版本号说明一下&#xff1a; MongoDB 版本号的命名规则是 x.y.z&#xff0c;当其中的 y 是奇数时表示当前的版本为开发版&#xff0c;当其中的 y 是偶数时表示当前的…

“多组数组”题的注意事项,天杀的“鲁棒性”

【题目描述】 输入一些整数&#xff0c;求出它们的最小值、最大值和平均值&#xff08;保留3位小数&#xff09;。输入保证这些数都是不超过1000的整数。 输入包含多组数据&#xff0c;每组数据第一行是整数个数n&#xff0c;第二行是n个整数。n&#xff1d;0为输入结束标记&…

日志服务 HarmonyOS NEXT 日志采集最佳实践

作者&#xff1a;高玉龙&#xff08;元泊&#xff09; 背景信息 随着数字化新时代的全面展开以及 5G 与物联网&#xff08;IoT&#xff09;技术的迅速普及&#xff0c;操作系统正面临前所未有的变革需求。在这个背景下&#xff0c;华为公司自主研发的鸿蒙操作系统&#xff08…

经典文献阅读之--LOG-LIO(高效局部几何信息估计的激光雷达惯性里程计)

0. 简介 局部几何信息即法线和点分布在基于激光雷达的同时定位与地图构建&#xff08;SLAM&#xff09;中是至关重要&#xff0c;因为它为数据关联提供了约束&#xff0c;进一步确定了优化方向&#xff0c;最终影响姿态的准确性。然而即使在使用KD树或体素图的辅助下&#xff…