*bio) submit_bio会调用make_request_fn将bio封装成request插入到request_queue,默认会使用linux系统实现的blk_queue_bio。...的page,调用bio_endio通知读写完成, 从头到尾request_queue和request就没有用到 static int simp_blkdev_make_request(struct request_queue...代码参考 写一个块设备驱动.pdf 资料参考 《Linux内核设计与实现》 《Linux内核完全注释》 Linux.Generic.Block.Layer.pdf https://zhuanlan.zhihu.com...struct bio_vec *bvec; int i; void *dsk_mem; if ((bio->bi_sector bio->bi_size > SIMP_BLKDEV_BYTES...(unsigned long long)bio->bi_sector, bio->bi_size); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
Linux系统一次读取磁盘的大小是一个块,而不是一个扇区,块设备驱动由此得名。 二、块设备处理过程 1、linux 内核中,块设备将数据存储与固定的大小的块中,每个块都有自己的固定地址。...Linux内核中块设备和其他模块的关系如下。 ? 1、块设备的处理过程涉及Linux内核中的很多模块,下面简单描述之间的处理过过程。 ...; unsigned int bi_size; /* 为了明了最大的 segment 尺寸,我们考虑这个 bio 中第一个和最后一个 可合并的 segment 的尺寸 */ unsigned...内核定义如下: struct bio_vec { struct page *bv_page; /* 页指针 */ unsigned int bv_len; /* 传输的字节数 */ unsigned...(也可以不用注册设备,驱动一样可以工作,该函数和字符设备的register_chrdev()函数相对应,对于大多数的块设备,第一个工作就是相内核注册自己,但是在Linux2.6以后,register_blkdev
->bi_io_vec[0].bv_page = bh->b_page; bio->bi_io_vec[0].bv_len = bh->b_size; bio->bi_io_vec[0].bv_offset...= bh_offset(bh); bio->bi_vcnt = 1; bio->bi_idx = 0; bio->bi_size = bh->b_size; bio->bi_end_io...generic_make_request(struct bio *bio) { struct bio_list bio_list_on_stack; //如果当前的bio_list存在了,直接返回...} while (ret); } //看默认的request函数都干啥了 static int __make_request(struct request_queue *q, struct bio...request_queue *q) { if (unlikely(blk_queue_stopped(q))) return; //调用队列的处理函数, 当我们写驱动时,我们写的处理函数就在这里使用
是Linux内存管理机制中一个内存页或者内存页的一部分。 块 (Blocks): 由Linux制定对内核或文件系统等数据处理的基本单位。通常由1个或多个扇区组成。...(对Linux操作系统而言) 扇区(Sectors):块设备的基本单位。...,写入,这样会浪费很多时间在读/写硬盘上,所以内核提供了一个队列的机制,再没有关闭txt文件之前,会将读写请求进行优化,排序,合并等操作,从而提高访问硬盘的效率 (PS:内核中是通过elv_merge(...[0].bv_len = bh->b_size; //存放扇区的大小 bio->bi_io_vec[0].bv_offset = bh_offset(bh);...通过上面代码和注释,内核中的申请队列q最终都是交给驱动处理,由驱动来对扇区读写 8.接下来我们就看看drivers\block\xd.c的入口函数大概流程,是如何创建块设备驱动的 static DEFINE_SPINLOCK
是Linux内存管理机制中一个内存页或者内存页的一部分。 块 (Blocks): 由Linux制定对内核或文件系统等数据处理的基本单位。通常由1个或多个扇区组成。...[0].bv_len = bh->b_size; //存放扇区的大小 bio->bi_io_vec[0].bv_offset = bh_offset(bh);...最终q->make_request_fn()执行的是__make_request()函数 6、我们来看看__make_request()函数,对提交的申请队列q和bio做了什么 static int _.... ... //(1)将之前的申请队列q和传入的bio,通过排序,合并在本身的req队列中 el_ret = elv_merge(q, &req, bio); ... ......通过上面代码和注释,内核中的申请队列q最终都是交给驱动处理,由驱动来对扇区读写 9、接下来我们就看看drivers\block\xd.c的入口函数大概流程,是如何创建块设备驱动的 static DEFINE_SPINLOCK
对于flash存储设备而言,要存取数据,根据芯片的手册我们主要是封装一个写数据函数和读取函数,封装好了这两个函数才方便上层应用的调用。 对于flash而言常见的读写单位一般是页、扇区。...如果 major = 0,表示尝试分配未使用的主设备号,返回值就表示分配 成功的主设备号。 @name:新块设备的名称。 注意: 该名称必须保证在系统中是唯一的。...request_queue *q, struct bio *bio) { int dir; unsigned long long dsk_offset; struct bio_vec *...,iovec_mem,dir); kunmap(bvec->bv_page); dsk_offset += bvec->bv_len; } bio_endio(bio, 0);...看懂块设备框架,使用的模拟的内存。 2. 加入SD卡的驱动,配合块设备框架,完成完整的块设备驱动编写。
在Linux里面,用于描述硬盘里面要真实操作的位置与page cache的页映射关系的数据结构是bio。相信大家已经见到bio一万次了,但是就是和真实的案例对不上。...bio的定义如下(include/linux/blk_types.h): struct bio_vec { struct page *bv_page; unsigned...int bv_len; unsigned int bv_offset; }; struct bio { struct bio *bi_next...我们现在假设2种情况 第1种情况是page_cache_sync_readahead()要读的0~16KB数据,在硬盘里面正好是顺序排列的(是否顺序排列,要查文件系统,如ext3、ext4),Linux...第2种情况是page_cache_sync_readahead()要读的0~16KB数据,在硬盘里面正好是完全不连续的4块 (是否顺序排列,要查文件系统,如ext3、ext4),Linux会为这一次4页的读
Linux内核版本: 3.5 一、块设备介绍 块是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区...块 (Blocks):由Linux制定对内核或文件系统等数据处理的基本单位。通常,1个块由1个或多个扇区组成。(对Linux操作系统而言) 3. 段(Segments):由若干个相邻的块组成。...CFQ I/O scheduler为系统内所有的任务分配均匀的IO带宽,提供一个公平的工作环境,在多媒体环境中,能保证音视频及时从磁盘中读取数据,是当前内核默认的调度器 我们可以通过内核传参的方式指定使用的调度算法...函数参数介绍: @ gfp_mask : 内存分配的方式。 GFP_KERNEL和GFP_ATOMIC, GFP_ATOMIC: 用来从中断处理和进程上下文之外的其他代码中分配内存....函数指针的原型如下: typedef void (make_request_fn) (struct request_queue *q, struct bio *bio); 该函数指针在Blkdev.h
将其中的数据流抽出来分析,深入理解里面每一层使用的数据结构,会让我们对MMC的理解更为深刻。 存储基本知识 先了解一些存储的基本概念,以我的开发板为例,存储介质为容量8G的eMMC。...系统里输入fdisk -l可查看磁盘信息,红框的为8G eMMC的信息。 有两个用户分区,分别为p1和p2。 对于p1分区,起始逻辑块为20480,终止逻辑块为282623。...int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags) _submit_bh是fs/buffer.c的一个函数...在历史上,buffer_head被用来映射页面中的单个块,当然也作为I/O的单位通过文件系统和块层。...->bi_io_vec[0].bv_len = bh->b_size; bio->bi_io_vec[0].bv_offset = bh_offset(bh); bio->bi_iter.bi_size
内核希望一次只有一个make_request_fn是活动的,否则使用堆叠设备的堆栈可能是一个问题。因此,使用current->bio_list保存由make_request_fn函数提交的请求列表。...内核4.1以后的某个版本将card层合并到core层去了,所以这里暂且把card层也当成MMC的CORE核心层去看吧。...进入drivers/mmc/host, 可以通过以下方法确认使用的host是哪个。...对于SDHCI_AUTO_CMD23的处理,区别就是在于使用mmc_request里的sbc还是cmd,事实上并没什么区别,他们的类型都是struct mmc_command。...,大部分是位运算和写寄存器操作。
这些功能都需要存储设备固件的支持,如何在 Linux 主机上,使用 Linux 现有的机制,实现数据的分层存储?本文主要介绍了 Linux 平台上两种不同的实现分层存储的方案。...Storage Tiering 分层存储技术在企业级的存储设备中已经被广泛使用,如 IBM 的 Easy Tier, EMC 的 FAST 等,但这些功能都集成在存储设备内部,需要存储设备固件的支持。...方案结构 该方案的结构包括一个 Linux 设备驱动程序和若干用户态的控制程序,如图 3 所示。...该方案由于 Storage Tiering 所有的功能都在 Linux 内核实现,且需要维护虚拟设备到物理设备的地址映射表,以及保证数据一致性,所以实现难度和工作量比较大,但可扩展性和灵活性也相对较大。...当 VDEV 收到一个 bio,可以由 bi_sector 和 bi_size 找出所对应的 VDEV 的 Block 以及 Block 内的偏移量,通过查询映射表,找到各个 VDEV Block 所对应的物理设备以及
#编译后的驱动为sd_mod.ko 安装(初次安装直接安装即可, 后续更新, 需要先做一个内核切换) #rpm -ivh kernel-*.rpm # 改默认内核 # 查看内核列表 grubby -..., 设置为原来的, reboot后删除编译的, 然后更新编译的新核 # grub2-set-default 2 # reboot # 查看内核 grub2-editenv list # 查看内核索引...makfile(编译树采用递归下降): https://docs.kernel.org/kbuild/makefiles.html 内核配置文件kconfig(.config,控制模块打开和关闭): https...https://lore.kernel.org/r/20210122023317.687987-9-ming.lei@redhat.com ret = BLK_STS_RESOURCE -> 块:引入新的块状态代码类型目前我们在块层中使用标准的...blk_status_t (ab) 使用稀疏的 __bitwise 注释来允许稀疏类型检查,这样我们就可以很容易地捕捉到传递错误值的地方 scsi_prepare_cmd -> static blk_status_t
轮询:程序会周期性地轮询或检查 I/O 操作的状态,以确定它们是否已经完成。这可以通过使用系统调用如 select、poll、epoll(Linux系统)或异步事件处理来实现。..., 频繁的内核态和用户态的切换。...它是对 select 和 poll 的改进,提供了更好的性能和扩展性,特别适用于处理大规模并发连接, 如 Web 服务器、聊天服务器和网络游戏服务器等,因为它具有出色的性能和扩展性。...在 Linux 2.4 内核前主要是 select 和 poll,自 Linux 2.6 内核正式引入 epoll 以来,epoll 已经成为了目前实现高性能网络服务器的必备技术。...mmap 将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址(不管是用户空间还是内核空间都是虚拟地址,最终要通过地址映射映射到物理地址),使得这块物理内存对内核和对用户均可见,减少用户态和内核态之间的数据交换
-> 我们一次只希望一个 ->submit_bio 处于活动状态,否则堆栈设备的堆栈使用可能会出现问题。...-> void submit_bio_noacct(struct bio *bio) -> 为 I/O 重新提交 bio 到块设备层 bio 描述内存和设备上的位置。...块层的所有文件系统和其他上层用户应该使用 submit_bio() 代替, bio 在节流之前已经被检查过,所以在从节流队列中调度它之前不需要再次检查它。...,可以直接下发(对于较快的硬盘如nvme盘,进入调度层可能会浪费时间,跳过IO调度层有利于性能提升) (1)bounce过程 (2)bio的切分和合并 (3)IO请求和tag的分配 (4)plug/unplug...机制 (5)IO调度器 (4)其他 参考 Linux内核笔记: https://github.com/ssbandjl/linux/blob/v5.10/readme_linux_with_git_log
VFS提供的核心数据结构有四个,它们定义在内核源代码的include/linux/fs.h和include/linux/dcache.h中。...在file对应的操作方法file_operations里面定义了我们经常使用的read和write: struct file_operations { .........它的中文译名叫页高速缓存,是Linux内核使用的主要磁盘高速缓存,是一个纯内存的工作组件,其作用就是来给访问相对比较慢的磁盘来进行访问加速。...Linux内核使用搜索树来高效管理大量的页面。 如果你有特殊的需求想要绕开Page Cache,只要设置DIRECT_IO就可以了。...通用块层 通用块层是一个处理系统中所有块设备IO请求的内核模块。它定义了一个叫bio的数据结构来表示一次IO操作请求(include/linux/bio.h)。
“插桩”的方法抓取log,“插桩”也称为Tracepoint,Tracepoint是Linux内核预先定义的静态探测点,它分布于内核的各个子系统中,每种Tracepoint有一个name、一个enable...下面将使用该定义来描述TRACE_EVENT()宏的每个部分 除第一个参数外,所有参数都用另一个宏(TP_PROTO、TP_ARGS、TP_STRUCT_uentry、TP_fast_assign和TP_printk...(struct rq *rq, struct task_struct *prev, struct task_struct *next); 它用作添加到内核代码的跟踪点和回调函数的原型。...然后,可以使用TP_PROTO()和TP_ARGS()定义的参数的变量名来将适当的数据分配到该输入结构中 Print 最后一个参数定义如何使用printk()从TP_STRUCT_uentry结构中打印字段...tracepoint.h文件是必需的 #include linux/tracepoint.h> 现在可以使用trace_EVENT()宏定义所有跟踪事件。
由于文件系统涉及面较广,例如虚拟文件系统(VFS),页缓存,块缓存,数据同步等内容,不可能全部分析到位,这里只记录和read有关的两种使用方式。cached IO和direct IO。 1....设置系统状态或读取内核数据:因为系统调用是用户空间和内核的唯一通讯手段,所以用户设置系统状态,比如开/关某项内核服务(设置某个内核变量),或读取内核数据都必须通过系统调用。...比如 fork、clone、execve、exit等 那为什么一定要用系统调用来访问操作系统的内容呢,其实这可以看做对内核的保护,linux分为用户空间和内核空间,而用户空间是不允许访问内核空间的数据的...缓存的目标是任何基于页的对象,这包含各种类型的文件和各种类型的内存映射。为了满足普遍性要求,linux使用定义在linux/fs.h中的结构体address_space结构体描述页高速缓存中的页面。...int mpage_readpage(struct page *page, get_block_t get_block) { struct bio *bio = NULL; sector_t last_block_in_bio
作者简介 谢欢,大家可以叫我Jeff, 我目前就职于某国际知名linux发行版开源公司, 热衷于linux内核。...我平时把linux内核源码当小说一样阅读学习,也一直把能给linux社区贡献更多有质量的代码而努力。...今年10月中旬,我向Linux内核社区提交了一个关于tracing 的patchset. tracing 的 Maintainer steve 和kprobe的maintainer Masami都非常感兴趣...有老师带着学,我们可以事半功倍,搞懂了原理,再去使用工具,更加得心应手了 老师如果有精力,也可以出一些内核子系统的实现讲解,就像ftrace这种讲法就挺好 Jeff老师:我是准备之后讲解block子系统的...:我最近想到一个idea,不再局限于跟踪函数,跟踪struct page,跟踪struct bio,跟踪struct task_struct,任何对象都可以动态跟踪,正在准备提个大补丁到社区去,看能不能合并进去
I/O子系统概貌 VFS:内核提供不同实现文件系统的抽象,应用端一般请求到vfs,vfs在调用实际文件系统的posix语义函数,可以理解为vfs作为用户态和实际文件系统的之间的转换桥梁,为用户态提供对于底层磁盘文件系统无感知的文件系统服务层...struct hd_struct:对于分区的请求可以线性的映射到物理磁盘,这种分区的体现在内核中使用hd_struct表示。...struct block_device:在内核中磁盘和分区对应一个块设备,每个块设备有自己唯一的块设备编号和名称,根据块设备可以找到磁盘,这个通用的块设备抽象层是通过block_device呈现。...这里涉及到了两种I/O请求,一种是通用块设备层的I/O请求,这个在内核中是以struct bio呈现;另外一种设备驱动层的I/O请求,在内核中是以struct request呈现。...}; // 来自用户态的IO请求在内核的表现形式bio struct bio { // 指向属于同一个request的后一个bio struct bio *bi_next; // bio
领取专属 10元无门槛券
手把手带您无忧上云