linux内核bio子系统学习记录一:

bio子系统学习一:

主要源码目录 :block/bio.c include/linux/bio.h
内核版本:4.19.1

这部分先总结下bio子系统的初始化部分,后续再总结bio的申请以及释放,还有其它api的使用等介绍。

bio的涉及到频繁的内存申请以及释放,包括bio结构体本身以及它比较重要的成员bi_io_vec*的内存申请和释放,所以初始化部分主要是相关的内存池的初始化,用于后面bio和bi_io_vec的内存申请和释放。

其中bio结构体和bio_vec结构体定义如下:

/** main unit of I/O for the block layer and lower layers (ie drivers and* stacking drivers)*/
struct bio {struct bio		*bi_next;	/* request queue link */struct gendisk		*bi_disk;unsigned int		bi_opf;		/* bottom bits req flags,* top bits REQ_OP. Use* accessors.*/unsigned short		bi_flags;	/* status, etc and bvec pool number */unsigned short		bi_ioprio;unsigned short		bi_write_hint;blk_status_t		bi_status;u8			bi_partno;/* Number of segments in this BIO after* physical address coalescing is performed.*/unsigned int		bi_phys_segments;/** To keep track of the max segment size, we account for the* sizes of the first and last mergeable segments in this bio.*/unsigned int		bi_seg_front_size;unsigned int		bi_seg_back_size;struct bvec_iter	bi_iter; //用来遍历bvec,标记bio的处理进度atomic_t		__bi_remaining;bio_end_io_t		*bi_end_io;void			*bi_private;
#ifdef CONFIG_BLK_CGROUP/** Optional ioc and css associated with this bio.  Put on bio* release.  Read comment on top of bio_associate_current().*/struct io_context	*bi_ioc;struct cgroup_subsys_state *bi_css;struct blkcg_gq		*bi_blkg;struct bio_issue	bi_issue;
#endifunion {
#if defined(CONFIG_BLK_DEV_INTEGRITY)struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif};unsigned short		bi_vcnt;	/* how many bio_vec's *//** Everything starting with bi_max_vecs will be preserved by bio_reset()*/unsigned short		bi_max_vecs;	/* max bvl_vecs we can hold */atomic_t		__bi_cnt;	/* pin count */struct bio_vec		*bi_io_vec;	/* the actual vec list */struct bio_set		*bi_pool;/** We can inline a number of vecs at the end of the bio, to avoid* double allocations for a small number of bio_vecs. This member* MUST obviously be kept at the very end of the bio.*/struct bio_vec		bi_inline_vecs[0];
};
/** was unsigned short, but we might as well be ready for > 64kB I/O pages*/
struct bio_vec {struct page	*bv_page;unsigned int	bv_len;unsigned int	bv_offset;
};

入口函数init_bio:

其中,bio_integrity_init函数和bioset_integrity_create函数先不看吧。

//这个是涉及到的结构体定义
struct bio_slab {struct kmem_cache *slab; //指向cache描述符,cache描述符描述了slab信息unsigned int slab_ref; //bio_slab结构体的引用计数unsigned int slab_size; //内存池中object大小char name[8]; //内存池名称,/proc/slabinfo可以看到
};static DEFINE_MUTEX(bio_slab_lock);
static struct bio_slab *bio_slabs;
static unsigned int bio_slab_nr, bio_slab_max;/*1:https://zhuanlan.zhihu.com/p/596543999?utm_id=02:https://blog.csdn.net/geshifei/article/details/119959905
*/
static int __init init_bio(void)
{/*全局数组*/bio_slab_max = 2;bio_slab_nr = 0;bio_slabs = kcalloc(bio_slab_max, sizeof(struct bio_slab), GFP_KERNEL);if (!bio_slabs)panic("bio: can't allocate bios\n");/*这部分先不看*/bio_integrity_init();//bvec_slabs数组管理bvec内存池biovec_init_slabs(); //分配bio_vec的高速内存池/*参数1:需要初始化的bio_set指针参数二:内存池中bio的最小数量参数三:struct bio结构体前附加数据块的大小,front_pad+sizeof(struct bio)就是内存池中object的大小。从fs_bio_set类型内存池中申请bio内存块,bio前无附加数据参数四:BIOSET_NEED_BVECS :创建一个单独的内存池用于分配iovecs,fs_bio_set需设置该标记BIOSET_NEED_RESCUER:创建一个workqueue,处理函数为bio_alloc_rescue,当内存不足无法从内存池中申请到bio时,该workqueue把处理stack device时(比如raid)暂存在list中的bio(参考后文“bio的提交”)提交处理,从而回收bio。对于非stack device场景,不需要这个工作队列。对fs_bio_set这个变量进行初始化*/if (bioset_init(&fs_bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS)) //2, 0 ,1panic("bio: can't allocate bios\n");/*这部分先不看*/if (bioset_integrity_create(&fs_bio_set, BIO_POOL_SIZE)) //2panic("bio: can't create integrity pool\n");return 0;
}
subsys_initcall(init_bio);

这里先分析下bio_slabs 这个结构体,,看变量名字也比较容易分析,它是后续用于申请bio这个结构体时使用的,只是初始化时,它的数组下标只有两个,且其成员也没有初始化,在实际使用时,它的成员才会进行初始化,而且在必要时也会进行扩容,也就是它的数组下标不是2了。(这个见bio_find_or_create_slab函数),,所以在申请bio的时候根据算法的设计,它有可能是从任何一个下标的成员(slab)当中申请内存的。

biovec_init_slabs函数分析

这个函数,看名称也不难分析,是对后续在申请biovec相关内存时做的一些初始化。

#define BIO_INLINE_VECS 4
/*1://https://blog.csdn.net/sinat_32960911/article/details/1322377812:__read_mostly gcc编译选项 标记该变量会被频繁访问 提高系统性能struct biovec_slab {int nr_vecs;char *name;struct kmem_cache *slab;};
*/
#define BV(x, n) { .nr_vecs = x, .name = "biovec-"#n }
static struct biovec_slab bvec_slabs[BVEC_POOL_NR] __read_mostly = { BV(1, 1), BV(4, 4), BV(16, 16), BV(64, 64), BV(128, 128), BV(BIO_MAX_PAGES, max), //这个当作是256吧
};
#undef BV //取消该宏定义static void __init biovec_init_slabs(void)
{int i;for (i = 0; i < BVEC_POOL_NR; i++) { //6int size;struct biovec_slab *bvs = bvec_slabs + i; //指向结构体数组的首地址if (bvs->nr_vecs <= BIO_INLINE_VECS) { //4, 前面两个成员bvs->slab = NULL; continue;}/*这个size比较关键*/size = bvs->nr_vecs * sizeof(struct bio_vec);bvs->slab = kmem_cache_create(bvs->name, size, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);}
}

bioset_init函数分析

这里比较重要的是调用的bio_find_or_create_slab函数,其余的就是对fs_bio_set这个结构体的一些成员变量进行初始化。

struct bio_set {struct kmem_cache *bio_slab;unsigned int front_pad;mempool_t bio_pool;mempool_t bvec_pool;
#if defined(CONFIG_BLK_DEV_INTEGRITY)mempool_t bio_integrity_pool;mempool_t bvec_integrity_pool;
#endif//Deadlock avoidance for stacking block drivers: see comments in bio_alloc_bioset() for detailsspinlock_t		rescue_lock;struct bio_list		rescue_list;struct work_struct	rescue_work;struct workqueue_struct	*rescue_workqueue;
};/** fs_bio_set is the bio_set containing bio and iovec memory pools used by* IO code that does not need private memory pools.*/
struct bio_set fs_bio_set;
EXPORT_SYMBOL(fs_bio_set);int bioset_init(struct bio_set *bs,unsigned int pool_size, unsigned int front_pad, int flags)
{//sizeof(bio)的多余部分unsigned int back_pad = BIO_INLINE_VECS * sizeof(struct bio_vec); //4 * sizeof(struct bio_vec)bs->front_pad = front_pad; //0spin_lock_init(&bs->rescue_lock); //自旋锁初始化bio_list_init(&bs->rescue_list); //bio_list 初始化INIT_WORK(&bs->rescue_work, bio_alloc_rescue); //工作队列初始化/*返回的这个值,也记录在这个bio_slabs全局数组里了看函数名称,bio当中寻找slab或者创建一个slab*/bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad); //0+back_padif (!bs->bio_slab)return -ENOMEM;/*bio_pool的初始化,后面分配内存使用mempool_allocpool_size内存池至少保留两个元素mempool_init(pool, min_nr, mempool_alloc_slab, mempool_free_slab, (void *) kc);后续申请内存走的是这个路线 -> mempool_alloc_slab -> kmem_cache_alloc*/if (mempool_init_slab_pool(&bs->bio_pool, pool_size, bs->bio_slab))//2goto bad;//1 & (1 << 0)if ((flags & BIOSET_NEED_BVECS) && biovec_init_pool(&bs->bvec_pool, pool_size)) //2goto bad;if (!(flags & BIOSET_NEED_RESCUER)) // 1 & (1 << 1)   -> 0001 & 0010return 0;bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0); //申请一个工作队列if (!bs->rescue_workqueue)goto bad;return 0;
bad:bioset_exit(bs);return -ENOMEM;
}
EXPORT_SYMBOL(bioset_init);

bio_find_or_create_slab函数分析

static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)
{unsigned int sz = sizeof(struct bio) + extra_size; struct kmem_cache *slab = NULL;struct bio_slab *bslab, *new_bio_slabs;unsigned int new_bio_slab_max;unsigned int i, entry = -1;mutex_lock(&bio_slab_lock);/*遍历bio_slabs数组,若某个bio_slab->slab_size等于sz,那么就选择这个bio_slab.*/i = 0;while (i < bio_slab_nr) { //这个一开始是0bslab = &bio_slabs[i];//若bio_slabs有空闲未用的bio_slab,选择该bio_slabif (!bslab->slab && entry == -1)entry = i;else if (bslab->slab_size == sz) {slab = bslab->slab;bslab->slab_ref++;break;}i++;}//找到bio_slab了if (slab)goto out_unlock;/*经过上面两步依然未找到可用的bio_slab,通过krealloc将bio_slabs数组长度扩大一倍,并选择其中一个空闲的bio_slab。*/if (bio_slab_nr == bio_slab_max && entry == -1) {new_bio_slab_max = bio_slab_max << 1; //相当于乘以2new_bio_slabs = krealloc(bio_slabs, new_bio_slab_max * sizeof(struct bio_slab), GFP_KERNEL);if (!new_bio_slabs)goto out_unlock;bio_slab_max = new_bio_slab_max;bio_slabs = new_bio_slabs;}if (entry == -1)entry = bio_slab_nr++;bslab = &bio_slabs[entry];snprintf(bslab->name, sizeof(bslab->name), "bio-%d", entry);slab = kmem_cache_create(bslab->name, sz, ARCH_KMALLOC_MINALIGN, SLAB_HWCACHE_ALIGN, NULL);if (!slab)goto out_unlock;bslab->slab = slab;bslab->slab_ref = 1;bslab->slab_size = sz;
out_unlock:mutex_unlock(&bio_slab_lock);return slab;
}

其初始化过程基本就是这样了,如果对内存池用的不多也不要紧,可以看看相关源码,也不多。

总结:初始化部分,如果光光看这部分,会比较晦涩,后面在涉及到bio的申请和释放时,再回头来看初始化部分,就清晰许多了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(学习笔记)Xposed模块编写(一)

前提&#xff1a;需要已经安装Xposed Installer 1. 新建一个AS项目 并把MainActvity和activity_main.xml这两个文件删掉&#xff0c;然后在AndriodManifest.xml中去掉这个Activity的声明 2. 在settings.gralde文件中加上阿里云的仓库地址&#xff0c;否则Xposed依赖无法下载 m…

【llm使用】ChatGLM3-6B Transformers部署调用

文章目录 环境准备模型下载代码准备部署 说明&#xff1a;本文转自国内开源组织datawhale的repo&#xff1a; self-llm 环境准备 在autodl平台中租一个3090等24G显存的显卡机器&#xff0c;如下图所示镜像选择PyTorch–>2.0.0–>3.8(ubuntu20.04)–>11.8 接下来打…

windows 系统读取 Linux Ext 分区硬盘

windows 系统读取 Linux Ext 分区硬盘 起因是需要处理一些在硬盘中的数据&#xff0c;硬盘插到我的电脑上后无法读盘&#xff0c;在 Windows 存储管理器中查看显示未分配&#xff0c;需要格式化&#xff0c;但是在 Ubuntu 系统的电脑中可以正常识别&#xff0c;试验了几次后发…

.NET8构建统计Extreme Optimization Numerical Libraries

为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持&#xff0c;使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合&#xff0c;为技术和统计…

关于清空ant.design 中表单内容的方法

关于清空ant.design 中表单内容的方法 其实就两个方法具体怎么清除一个一个试试就知道了 表单有两个可能的属性&#xff1a; formformRef 可以用他们绑定两个用法在代码部分定义&#xff1a; form useRef()form Form.useForm() 清空的方法&#xff1a; form.current?…

【Linux】第二十六站:软硬链接

文章目录 一、软链接二、硬链接三、ln命令四、该如何理解硬链接&#xff1f;五、如何理解软链接六、为什么要用软硬链接1.软链接的应用场景2.硬链接的应用场景 一、软链接 如下所示&#xff0c;我们创建一个文件以后&#xff0c;然后执行下面的指令 ln -s file.txt soft-link…

C++模拟实现unordered_map和unordered_set

目录 1.了解哈希表 1.哈希表 1.他的实现原理就是&#xff1a; ​编辑 2.写单个数据的类型&#xff08;这边先模拟map的kv类型&#xff0c;后面会再一起改&#xff0c;这边先一步步的先简单实现他&#xff09; 3.封装整个类&#xff1a; 4.哈希表中存储string 2.哈…

前后端分离部署https

引用&#xff1a;https://blog.csdn.net/weixin_35676679/article/details/127841598 前后端部署&#xff0c;&#xff0c;一般用的是nginx和java&#xff0c;&#xff0c;&#xff0c; 下载SSL证书&#xff1a; java配置https 将证书配置到springboot中 server:port: 544…

线程...

文章目录 1.Linux中线程该如何理解2.重新定义线程 和 进程3.重谈地址空间 --- 第四讲4.Linux线程周边的概念 线程:是进程内的一个执行分支。线程的执行粒度,要比进程要细 很多教材喜欢这么说&#xff0c;这只是一个线程的特征之一&#xff0c;来解释线程。 1.Linux中线程该如何…

基于51单片机的交通灯_紧急开关+黄灯倒计时+可调时间

51单片机交通灯_紧急开关黄灯倒计时可调时间 开题报告系统硬件设计主控制器选择系统硬件结构图时钟及复位电路指示灯及倒计时模块 倒计时模块&#xff1a;程序软件主流程框架main函数 设计报告资料清单资料下载链接 基于51单片机交通灯_紧急开关黄灯倒计时可调时间 仿真图prote…

题目:神奇的进制

解题思路&#xff1a; 用电脑自带的计算器&#xff0c;切换到程序员模式。里面有进制转换功能。 由题目&#xff0c;要求严格递增且都为字母&#xff0c;还要大于2023&#xff0c;则数字16进制为ABC。

【STM32】电机驱动

一、电机分类 二、直流电机的分类 1.有刷电机 2.无刷电机 3.直流减速电机 三、H桥电路 正向旋转 驱动Q1和Q4 反向旋转 驱动Q2和Q3 四、MC3386电机驱动芯片 1.基本原理图 1&#xff09;前进/后退&#xff1a;IN1和IN2的电平顺序决定电机的正反转 2&#xff09;调节速度&#…

ARM Cortex-A、Cortex-M和Cortex-R简介

ARM Cortex-A&#xff0c;Cortex-M和Cortex-R是ARM处理器系列的不同产品线&#xff0c;针对不同的应用场景和要求进行了优化。以下是它们之间的主要区别&#xff1a; 1. 应用场景&#xff1a;Cortex-A系列处理器主要用于高性能计算设备&#xff0c;例如智能手机、平板电脑、个…

Redis对象

Redis根据基本数据结构构建了自己的一套对象系统。主要包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象 同时不同的对象都有属于自己的一些特定的redis指令集&#xff0c;而且每种对象也包括多种编码类型&#xff0c;和实现方式。 Redis对象结构 struct redisOb…

Linux 防火墙

目录 安全技术 防火墙的分类 按保护范围划分 按实现方式划分 按网络协议划分 应用层防火墙&#xff08;7层&#xff09; 防火墙的工作原理 linux防火墙的基本认识 防火墙工具介绍 1.iptables 2.firewalld 3.nftables 安全技术 —— 入侵检测系统&#xff08;Intru…

Python 文件错误 SyntaxError: Non-Ascii Character \xe2

此错误表明您正在代码中写入非 ASCII 字符。 在编译时&#xff0c;解释器感到困惑并抛出 SyntaxError: Non-ASCII character ‘\xe2’ 。 ASCII 字符使用与 UTF-8 的前 128 个字符相同的编码&#xff0c;因此 ASCII 文本与 UTF-8 兼容。 首先&#xff0c;您必须了解 ASCII 字符…

centos7中通过kubeadmin安装k8s集群

k8s部署官方提供了kind、minikube、kubeadmin等多种安装方式。 其中minikube安装在之前的文章中已经介绍过&#xff0c;部署比较简单。下面介绍通过kubeadmin部署k8s集群。 生产中提供了多种高可用方案&#xff1a; k8s官方文档 本文安装的是1.28.0版本。 建议去认真阅读一下…

使用coco数据集进行语义分割(1):数据预处理,制作ground truth

如何coco数据集进行目标检测的介绍已经有很多了&#xff0c;但是关于语义分割几乎没有。本文旨在说明如何处理 stuff_train2017.json stuff_val2017.json panoptic_train2017.json panoptic_val2017.json&#xff0c;将上面那些json中的dict转化为图片的label mask&am…

Docker下安装MySQL

如果在Docker下直接拉取MySQL并运行镜像&#xff0c;由于没有指定字符编码集&#xff0c;可能会存在插入中文出现乱码的情况&#xff0c;并且当容器删除后&#xff0c;容器里面存在的数据会丢失&#xff0c;所以在运行容器时应该使用数据卷进行挂载&#xff0c;按照如下步骤操作…

大模型中幂律缩放法则和涌现能力

幂律缩放法则是一种用于描述两个变量之间关系的数学模型。 根据幂律缩放法则&#xff0c;当一个变量的值变化时&#xff0c;另一个变量的值以指数方式变化。具体而言&#xff0c;幂律缩放法则可以表示为Y a * X^b&#xff0c;其中Y表示一个变量的值&#xff0c;X表示另一个变…