当前位置: 首页 > news >正文

Linux性能分析系统和虚拟文件系统缓存初始化

性能分析系统的初始化profile_init

void __init profile_init(void)
{if (!prof_on)return;/* only text is profiled */prof_len = (_etext - _stext) >> prof_shift;prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));
}

1. 函数功能概述

profile_init 函数负责初始化内核的代码性能分析系统,为内核文本段的执行频率统计分配内存缓冲区

2. 代码逐段分析

2.1. 函数定义

void __init profile_init(void)
{
  • void:函数没有返回值
  • __init:宏标记,表示该函数只在内核初始化阶段使用,初始化完成后内存会被释放

2.2. 性能分析开关检查

        if (!prof_on)return;
  • if (!prof_on):检查性能分析是否启用
  • prof_on:全局变量,控制性能分析的开关状态
  • return:如果性能分析未启用,直接返回,不进行后续初始化
  • 作用:允许在编译时或启动参数中禁用性能分析功能

2.3. 计算分析长度(仅分析文本段)

        /* only text is profiled */prof_len = (_etext - _stext) >> prof_shift;
  • 只对文本段(代码段)进行性能分析
  • prof_len:全局变量,存储性能分析缓冲区的长度
  • _etext - _stext:计算内核文本段的大小
    • _stext:内核文本段起始地址(代码开始)
    • _etext:内核文本段结束地址(代码结束)
  • >> prof_shift:右移操作,进行大小缩放
    • prof_shift:全局变量,控制采样的粒度(通常为0或小的整数值)

2.4. 分配性能分析缓冲区

        prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));
}
  • prof_buffer:全局指针,指向性能分析缓冲区
  • alloc_bootmem(prof_len*sizeof(atomic_t)):使用启动内存分配器分配内存
    • prof_len*sizeof(atomic_t):计算需要的总内存大小
    • atomic_t:原子整数类型,用于多CPU安全计数
  • 作用:为每个代码位置分配一个计数器,记录执行次数

3. 关键技术细节详解

3.1. 内核内存布局

内核内存映射典型布局:
0x00000000 +----------------+|                ||    文本段       | ← _stext (代码开始)|    (.text)     |+----------------+ ← _etext (代码结束)|                ||    数据段       ||    (.data)     |+----------------+|                ||    BSS段       ||    (.bss)      |+----------------+

3.2. 性能分析缓冲区结构

// prof_buffer 指向的内存布局
+---------+---------+---------+-----+---------+
| counter | counter | counter | ... | counter |atomic_t 数组
+---------+---------+---------+-----+---------+地址0     地址1     地址2    ...   地址prof_len-1// 每个计数器对应一段代码区域
计数器索引 = (指令地址 - _stext) >> prof_shift

3.3. 采样粒度控制

prof_shift 的作用:

// 示例:不同 prof_shift 值的效果
_stext = 0xC0000000, _etext = 0xC0100000 (16MB代码)prof_shift = 0:
prof_len = (0xC0100000 - 0xC0000000) >> 0 = 0x01000000 = 16,777,216 计数器prof_shift = 4:  
prof_len = 0x01000000 >> 4 = 0x00100000 = 1,048,576 计数器prof_shift = 8:
prof_len = 0x01000000 >> 8 = 0x00010000 = 65,536 计数器

作用:通过调整 prof_shift 可以平衡分析精度和内存开销。

初始 RAM 磁盘检查代码段

#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&initrd_start < min_low_pfn << PAGE_SHIFT) {printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - ""disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);initrd_start = 0;}
#endif

1. 代码逐段分析

1.1. 条件编译开始

#ifdef CONFIG_BLK_DEV_INITRD
  • #ifdef CONFIG_BLK_DEV_INITRD:条件编译指令
  • 只有在内核配置了 CONFIG_BLK_DEV_INITRD(支持初始 RAM 磁盘)时,才编译包含的代码
  • 作用:允许在编译时完全禁用 INITRD 相关功能,减少内核大小

1.2. 主要条件检查

        if (initrd_start && !initrd_below_start_ok &&initrd_start < min_low_pfn << PAGE_SHIFT) {

这是一个复合条件检查,包含三个部分:

条件分解:

  • initrd_start:检查 INITRD 起始地址是否有效(非零)
  • !initrd_below_start_ok:检查是否允许 INITRD 在低内存区域之下
  • initrd_start < min_low_pfn << PAGE_SHIFT:检查 INITRD 是否位于有效内存范围之下

1.3. 地址计算解析

initrd_start < min_low_pfn << PAGE_SHIFT

计算过程:

  • min_low_pfn:最低可用物理页帧号
  • << PAGE_SHIFT:左移页大小位数(通常12位,因为PAGE_SIZE=4096)
  • min_low_pfn << PAGE_SHIFT:将页帧号转换为物理地址
  • 含义:检查 INITRD 起始地址是否低于第一个可用内存页的地址

1.4. 错误信息打印

                printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - ""disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
  • printk(KERN_CRIT ...):关键错误级别日志输出
  • 消息格式:"initrd overwritten (0x%08lx < 0x%08lx) - disabling it."
  • 参数:
    • initrd_start:INITRD 起始地址
    • min_low_pfn << PAGE_SHIFT:最低可用内存地址
  • 作用:告知用户 INITRD 被覆盖的具体地址信息

1.5. 禁用 INITRD

                initrd_start = 0;}
  • initrd_start = 0:将 INITRD 起始地址设为0
  • 禁用 INITRD,因为后续代码检查 initrd_start 是否为0来判断是否存在有效的 INITRD

代码段总结

  1. 安全性检查:确保 INITRD 不会与内核内存冲突
  2. 条件编译:只在需要时包含 INITRD 相关代码
  3. 错误检测:识别 INITRD 位置问题
  4. 优雅降级:通过禁用损坏的 INITRD 避免系统崩溃

虚拟文件系统缓存早期初始化vfs_caches_init_early

void __init vfs_caches_init_early(void)
{dcache_init_early();inode_init_early();
}
static void __init dcache_init_early(void)
{int loop;dentry_hashtable =alloc_large_system_hash("Dentry cache",sizeof(struct hlist_head),dhash_entries,13,0,&d_hash_shift,&d_hash_mask);for (loop = 0; loop < (1 << d_hash_shift); loop++)INIT_HLIST_HEAD(&dentry_hashtable[loop]);
}
void __init inode_init_early(void)
{int loop;inode_hashtable =alloc_large_system_hash("Inode-cache",sizeof(struct hlist_head),ihash_entries,14,0,&i_hash_shift,&i_hash_mask);for (loop = 0; loop < (1 << i_hash_shift); loop++)INIT_HLIST_HEAD(&inode_hashtable[loop]);
}

1. 函数功能概述

这些函数负责在系统启动早期初始化虚拟文件系统的目录项缓存(dentry cache)和索引节点缓存(inode cache)的哈希表结构,为文件系统操作提供基础数据结构支持

2. 代码逐段分析

2.1. 顶层初始化函数

void __init vfs_caches_init_early(void)
{dcache_init_early();inode_init_early();
}
  • void __init:无返回值,__init 表示只在初始化阶段使用
  • vfs_caches_init_early:虚拟文件系统缓存早期初始化
  • dcache_init_early():调用目录项缓存初始化函数
  • inode_init_early():调用索引节点缓存初始化函数
  • 作用:统一初始化 VFS 的两个核心缓存系统

2.2. 目录项缓存初始化函数

static void __init dcache_init_early(void)
{int loop;
  • static:只在当前文件内可见
  • __init:初始化阶段函数
  • int loop:循环计数器变量

2.3. 分配目录项哈希表

        dentry_hashtable =alloc_large_system_hash("Dentry cache",sizeof(struct hlist_head),dhash_entries,13,0,&d_hash_shift,&d_hash_mask);
  • "Dentry cache":哈希表名称,用于调试和显示
  • sizeof(struct hlist_head):每个桶的大小(链表头大小)
  • dhash_entries:期望的哈希表条目数(可配置参数)
  • 13:哈希表移位值(初始建议值,2¹³ = 8192 个桶)
  • 0:标志位,0表示无特殊标志
  • &d_hash_shift:返回实际的移位值
  • &d_hash_mask:返回哈希掩码值

2.4. 初始化目录项哈希桶

        for (loop = 0; loop < (1 << d_hash_shift); loop++)INIT_HLIST_HEAD(&dentry_hashtable[loop]);
}
  • loop = 0:从第一个桶开始
  • loop < (1 << d_hash_shift):循环条件,遍历所有桶
    • 1 << d_hash_shift:计算实际桶数量(2^d_hash_shift)
  • INIT_HLIST_HEAD(&dentry_hashtable[loop]):初始化每个哈希桶的链表头

2.5. 索引节点缓存初始化函数

void __init inode_init_early(void)
{int loop;
  • void __init:无返回值,初始化阶段函数
  • inode_init_early:索引节点缓存早期初始化
  • int loop:循环计数器

2.6. 分配索引节点哈希表

        inode_hashtable =alloc_large_system_hash("Inode-cache",sizeof(struct hlist_head),ihash_entries,14,0,&i_hash_shift,&i_hash_mask);

参数对比:

  • "Inode-cache":索引节点缓存名称
  • sizeof(struct hlist_head):相同,链表头大小
  • ihash_entries:索引节点哈希表期望条目数
  • 14:更大的初始移位值(2¹⁴ = 16384 个桶)
  • &i_hash_shift:索引节点哈希移位值
  • &i_hash_mask:索引节点哈希掩码

2.7. 初始化索引节点哈希桶

        for (loop = 0; loop < (1 << i_hash_shift); loop++)INIT_HLIST_HEAD(&inode_hashtable[loop]);
}
  • 与目录项缓存相同的初始化逻辑
  • 遍历所有桶并初始化链表头

分配大型系统哈希表alloc_large_system_hash

void *__init alloc_large_system_hash(const char *tablename,unsigned long bucketsize,unsigned long numentries,int scale,int consider_highmem,unsigned int *_hash_shift,unsigned int *_hash_mask)
{unsigned long long max;unsigned long log2qty, size;void *table;/* allow the kernel cmdline to have a say */if (!numentries) {/* round applicable memory size up to nearest megabyte */numentries = consider_highmem ? nr_all_pages : nr_kernel_pages;numentries += (1UL << (20 - PAGE_SHIFT)) - 1;numentries >>= 20 - PAGE_SHIFT;numentries <<= 20 - PAGE_SHIFT;/* limit to 1 bucket per 2^scale bytes of low memory */if (scale > PAGE_SHIFT)numentries >>= (scale - PAGE_SHIFT);elsenumentries <<= (PAGE_SHIFT - scale);}/* rounded up to nearest power of 2 in size */numentries = 1UL << (long_log2(numentries) + 1);/* limit allocation size to 1/16 total memory */max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;do_div(max, bucketsize);if (numentries > max)numentries = max;log2qty = long_log2(numentries);do {size = bucketsize << log2qty;table = alloc_bootmem(size);} while (!table && size > PAGE_SIZE && --log2qty);if (!table)panic("Failed to allocate %s hash table\n", tablename);printk("%s hash table entries: %d (order: %d, %lu bytes)\n",tablename,(1U << log2qty),long_log2(size) - PAGE_SHIFT,size);if (_hash_shift)*_hash_shift = log2qty;if (_hash_mask)*_hash_mask = (1 << log2qty) - 1;return table;
}

1. 函数功能概述

alloc_large_system_hash 函数负责根据系统内存大小智能分配哈希表,自动调整哈希表大小以平衡性能和内存使用,为内核各种缓存系统提供合适大小的哈希表

2. 代码逐段分析

2.1. 函数定义和参数

void *__init alloc_large_system_hash(const char *tablename,unsigned long bucketsize,unsigned long numentries,int scale,int consider_highmem,unsigned int *_hash_shift,unsigned int *_hash_mask)
{
  • void *__init:返回分配的内存指针,__init 表示只在初始化阶段使用
  • const char *tablename:哈希表名称,用于调试输出
  • unsigned long bucketsize:每个哈希桶的大小(字节)
  • unsigned long numentries:期望的哈希表条目数
  • int scale:缩放因子,控制内存与哈希表大小的比例
  • int consider_highmem:是否考虑高端内存
  • unsigned int *_hash_shift:返回哈希移位值
  • unsigned int *_hash_mask:返回哈希掩码值

2.2. 变量声明

        unsigned long long max;unsigned long log2qty, size;void *table;
  • max:最大允许的哈希表大小
  • log2qty:以2为底的对数,表示哈希表大小
  • size:要分配的内存大小
  • table:指向分配的内存

2.3. 自动计算条目数(如果未指定)

        /* allow the kernel cmdline to have a say */if (!numentries) {/* round applicable memory size up to nearest megabyte */numentries = consider_highmem ? nr_all_pages : nr_kernel_pages;numentries += (1UL << (20 - PAGE_SHIFT)) - 1;numentries >>= 20 - PAGE_SHIFT;numentries <<= 20 - PAGE_SHIFT;
  • if (!numentries):如果没有指定条目数,自动计算
  • consider_highmem ? nr_all_pages : nr_kernel_pages:根据是否考虑高端内存选择页数
  • 将页数向上舍入到最近的MB边界

2.4. 应用缩放因子

                /* limit to 1 bucket per 2^scale bytes of low memory */if (scale > PAGE_SHIFT)numentries >>= (scale - PAGE_SHIFT);elsenumentries <<= (PAGE_SHIFT - scale);}
  • 根据缩放因子调整条目数
  • scale > PAGE_SHIFT:缩放因子大于页大小移位值,减少条目数
  • else:增加条目数
  • 作用:控制每 2^scale 字节内存对应1个哈希桶

2.5. 向上取整到2的幂

        /* rounded up to nearest power of 2 in size */numentries = 1UL << (long_log2(numentries) + 1);
  • long_log2(numentries):计算以2为底的对数
  • 1UL << (long_log2(numentries) + 1):向上取整到下一个2的幂
  • 目的:确保哈希表大小是2的幂,便于使用位操作进行哈希计算

2.6. 限制最大分配大小

        /* limit allocation size to 1/16 total memory */max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;do_div(max, bucketsize);if (numentries > max)numentries = max;
  • max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4:计算总内存的1/16
  • do_div(max, bucketsize):除以每个桶的大小,得到最大桶数
  • if (numentries > max):如果请求的条目数超过最大限制,使用最大值

2.7. 计算对数并尝试分配

        log2qty = long_log2(numentries);do {size = bucketsize << log2qty;table = alloc_bootmem(size);} while (!table && size > PAGE_SIZE && --log2qty);
  • log2qty = long_log2(numentries):计算最终的对数值,做为移位值
  • do-while 循环:尝试分配内存,如果失败则减小大小重试
  • size = bucketsize << log2qty:计算实际要分配的内存大小
  • table = alloc_bootmem(size):使用启动内存分配器分配内存
  • 循环条件:分配失败、大小大于一页、还可以继续减小

2.8. 分配失败处理

        if (!table)panic("Failed to allocate %s hash table\n", tablename);
  • 如果所有分配尝试都失败,触发内核恐慌
  • 显示具体的哈希表名称,便于调试

2.9. 打印分配信息

        printk("%s hash table entries: %d (order: %d, %lu bytes)\n",tablename,(1U << log2qty),long_log2(size) - PAGE_SHIFT,size);
  • 输出哈希表分配信息:
    • 表名称
    • 条目数量(2^log2qty)
    • 分配阶数(相对于页大小的对数)
    • 总字节数

2.10. 设置输出参数

        if (_hash_shift)*_hash_shift = log2qty;if (_hash_mask)*_hash_mask = (1 << log2qty) - 1;return table;
}
  • *_hash_shift = log2qty:设置哈希移位值
  • *_hash_mask = (1 << log2qty) - 1:计算并设置哈希掩码
  • return table:返回分配的内存指针

3. 关键技术细节详解

3.1. 内存计算算法

// 自动计算条目数的详细过程
numentries = nr_kernel_pages;                    // 内核页数
numentries += (1 << (20 - PAGE_SHIFT)) - 1;      // 加 (1MB对应的页数 - 1)
numentries >>= 20 - PAGE_SHIFT;                  // 除以1MB对应的页数
numentries <<= 20 - PAGE_SHIFT;                  // 乘以1MB对应的页数
// 结果:向上舍入到最近的MB边界
http://www.dtcms.com/a/494667.html

相关文章:

  • 用python做网站和用php网站建设验收单意见怎么写
  • 德芙巧克力网站开发方案怎样宣传一个网站
  • 模式识别与机器学习课程笔记(4):线性判决函数
  • 无人机空中定位与一键返航原理详解
  • P12874 [蓝桥杯 2025 国 Python A] 巡逻||题解||图论
  • 律师在哪个网站做国家企业信用系统官网
  • mapbox基础,栅格图片切片并发布、加载
  • 深入 RFC 793:TCP 报文头部、MSS 协商与三次握手 / 四次挥手全解析
  • deconv(多项式除法)
  • unitree rl gym项目实践记录2:通过TensorBoard查看奖励曲线
  • 2.8、权限的终极目标:提权与持久化
  • 模式识别与机器学习课程笔记(11):深度学习
  • 网站流量站怎么做WordPress的登录页面
  • leetcode 191. 位1的个数 python
  • 河北住房与城乡建设部网站北京做网站企业
  • WordPress网站转APP插件家具设计
  • docker 学习dockerfile 构建 Nginx 镜像-部署 nginx 静态网
  • Prompt Engineering 核心知识:从基础模式到思维链,掌握大模型高效交互秘籍
  • Android中加载unity aar包实现方案
  • auxiliary英文单词学习
  • Elasticsearch:创建一个定制的 DeepSeek 嵌入推理端点
  • “自然搞懂”深度学习系列(基于Pytorch架构)——01初入茅庐
  • 51c~Pytorch~合集6
  • Java 对接印度股票数据源实现 http+ws实时数据
  • 建设网站分析报告陕西四通建设工程有限责任公司网站
  • 微信网站建设app公司WordPress邮箱注册慢
  • 【Qt】元对象系统:从实际开发中看QML/C++交互原理
  • 【MySQL】从零开始了解数据库开发 --- 数据表的索引
  • 设计模式篇之 策略模式 Strategy
  • 【HarmonyOS】并发线程间的通信