永久内核映射

内存就像墙上的气球,32体系就好比是小孩,64位体系好比是大人。对于位置比较低的气球,抬抬脚就可以够到,这些气球相当于DMA内存(可用于设备直接内存访问);位置再高一点的气球,小孩伸手可以够到,这些气球相当于NORMAL内存;位置再高一点的气球,需要大人抱着才可以够到(相当于内存需要映射),这些气球相当于HIGH内存。对大人来说,所有的气球都可以够到,就相当于不存在HIGH区内存。

永久内核映射允许内核建立高端页框到内核地址空间的长期映射。主内核页表中一个专门的页表(其地址保存在pkmap_page_table变量中),记录用于永久内核映射的页框。对于32位体系(PAE未被激活的情况下),一个地址占4字节,一个页表可保存1024个页框,每个页框4K,因此可以映射4M的高端内存。

下图中,左边三个列表中的数据是一一对应的;pkmap_count列表中括号中的数字,是其计数。

图1

pkmap_count用于记录每个页表项的使用计数:

计数为0:对应页表项没有映射任何高端内存页框

计数为1:对应页表项映射了高端内存页框,页框没有被引用

计数大于1:相应页表项映射了1个高端内存页框,有n-1个地方引用了此页框

计数0和1的区别,可以看下flush_all_zero_pkmaps的实现。

永久内核映射的典型用法如下:

static int igb_check_lbtest_frame(struct igb_rx_buffer *rx_buffer,
				  unsigned int frame_size)
{
	unsigned char *data;
	bool match = true;

	frame_size >>= 1;

    // 如果page已经映射,返回对应的线性内存地址
    // 如果page没有映射,则进行映射,并返回对应的线性内存地址
	data = kmap(rx_buffer->page);

    // 对线性内存进程处理
	if (data[3] != 0xFF ||
	    data[frame_size + 10] != 0xBE ||
	    data[frame_size + 12] != 0xAF)
		match = false;

    // 取消映射
	kunmap(rx_buffer->page);

	return match;
}

 以气球讲解下kmap,就是:

对于小孩,可以够到的气球,自己来够,自己够不到的气球,让大人抱着来够;对于大人,可以够到所有的气球。

由于受硬件的限制,以x86为例,每个内存节点(用pg_data_t表示)的物理内存划分为3个管理区(用zone表示):

ZONE_DMA:低于(包含)16M的内存页框

ZONE_NORMAL: 32位体系-高于16M且低于896M的内存页框;64位体系-高于16M的内存页框

ZONE_HIGHMEM:32位体系-大于等于896M的内存页框;64位体系ZONE_HIGHMEM总是空的

一 kmap

kmap对高端内存和非高端内存,有不同的处理方式:非高端内存,cpu可以直接访问其物理内存,调用page_address,返回页框对应的线性地址;高端内存,cpu不能直接访问其物理内存,需要通过kmap_high进行映射后,cpu才可以访问。

void *kmap(struct page *page)
{
	might_sleep();
	if (!PageHighMem(page))
		return page_address( page);
	return kmap_high(page);
}

二 page_address

对于非高端内存,page_address大体实现逻辑如下:

__va((unsigned long)(page - mem_map) << 12)

其中page-mem_map用于计算页框的下标。

三 kmap_high

kmap_high用来实现映射的具体逻辑

void *kmap_high(struct page *page)
{
	unsigned long vaddr;

	/*
	 * For highmem pages, we can't trust "virtual" until
	 * after we have the lock.
	 */
	lock_kmap();
	vaddr = (unsigned long)page_address(page);
	if (!vaddr)
		// 映射页框
		vaddr = map_new_virtual(page);
    // 页框引用计数+1
	pkmap_count[PKMAP_NR(vaddr)]++;
	BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
	unlock_kmap();
	return (void*) vaddr;
}

 映射的具体逻辑,在map_new_virtual中


static inline unsigned long map_new_virtual(struct page *page)
{
	unsigned long vaddr;
	int count;
	unsigned int last_pkmap_nr;
	unsigned int color = get_pkmap_color(page);

start:
	count = get_pkmap_entries_count(color);
	/* Find an empty entry */
	for (;;) {
		last_pkmap_nr = get_next_pkmap_nr(color); // last_pkmap_nr上次使用页表项的索引
		if (no_more_pkmaps(last_pkmap_nr, color)) { // last_pkmap_nr 等于 0
			flush_all_zero_pkmaps();
			count = get_pkmap_entries_count(color);
		}

        // 第last_pkmap_nr个页表项的计数为0,即没有映射页框
		if (!pkmap_count[last_pkmap_nr])
			break;	/* Found a usable entry */
		if (--count)
			continue;

		// count减到0
		/*
		 * Sleep for somebody else to unmap their entries
		 */
		{
			DECLARE_WAITQUEUE(wait, current);
			wait_queue_head_t *pkmap_map_wait =
				get_pkmap_wait_queue_head(color);

			__set_current_state(TASK_UNINTERRUPTIBLE);
			add_wait_queue(pkmap_map_wait, &wait);
			unlock_kmap();
			schedule();
			remove_wait_queue(pkmap_map_wait, &wait);
			lock_kmap();

			/* Somebody else might have mapped it while we slept */
			if (page_address(page))
				return (unsigned long)page_address(page);

			/* Re-start */
			goto start;
		}
	}

	// 获取线性地址
	vaddr = PKMAP_ADDR(last_pkmap_nr);

    // 设置第last_pkmap_nr个页表项的页框
	set_pte_at(&init_mm, vaddr,
		   &(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));

    // 第last_pkmap_nr个页表项已映射了页框,页框未被引用,将计数置为1
	pkmap_count[last_pkmap_nr] = 1;
	set_page_address(page, (void *)vaddr);

	return vaddr;
}

四 igb_check_lbtest_frame中kmap的执行逻辑

现在回过头来,再看下igb_check_lbtest_frame中kmap的逻辑

1. 如果rx_buffer->page不在高端内存区,直接返回其线性地址

2. 如果rx_buffer->page在高端内存区,已建立映射,将pkmap_count(图1中左侧第2列)相应计数+1

3. 如果rx_buffer->page在高端内存区,尚未建立映射,在pkmap_count中查找一个空闲位置(计数为0),将页框(pkmap_count)添加到高端内存映射页表(图1右侧第2列)对应项中,pkmap_count中对应计数置为1,因为页框已被引用,pkmap_count计数再加1

五 永久内核映射线性地址

可以参考下图1中左侧列表

永久内核映射线性地址范围为[PKMAP_BASE, FIXADDR_START),即永久内核映射页表中第1项的线性地址为PKMAP_BASE,第2项的线性地址为PKMAP_BASE+4k,以此类推。

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

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

相关文章

AutoKeras(Python自动化机器学习)多模态数据和多任务

要点拓扑 AutoKeras 拓扑 要点 常规机器学习&#xff1a;scikit-learn示例探索性数据分析和数据预处理&#xff0c;线性回归&#xff0c;决策树图像分类ResNet模型示例&#xff0c;合成数据集DenseNet模型示例绘图线性回归和决策树模型使用Python工具seaborn、matplotlib、pan…

import tensorflow_hub报错

问题&#xff1a; 导入tensorflow_hub报ModuleNotFoundError: No module named ‘tensorflow.python.checkpoint’ 解决&#xff1a; tensorflow-estimator版本不对 和tensorflow&#xff08;2.6.0&#xff09;版本一致 。 pip install -U tensorflow-estimator2.6.0 验证&a…

一个收集了大量的C#/.NET/.NET Core项目宝库组织

项目宝库介绍 为.NET开发者提供一个寻找优秀C#/.NET/.NET Core项目和框架的入口&#xff0c;通过了解和对比更多的项目和框架来选择最适合我们自己学习、工作开发的一套项目或者框架。优秀的项目不应该被埋没&#xff0c;欢迎大家一起加入这个组织共同完善、发展.NET社区&…

线程和进程【并发和并行、线程上下文切换、线程的状态】

线程和进程【并发和并行、线程上下文切换、线程的状态】 什么是并发与并行&#xff1f;什么是线程上下文切换&#xff1f;线程状态&#xff1a;一个线程的一生 转自 极客时间 进程&#xff1a;是指内存中运行的一个应用程序&#xff0c;每个进程都有自己独立的内存空间&#x…

RapidMiner数据挖掘2 —— 初识RapidMiner

本节由一系列练习与问题组成&#xff0c;这些练习与问题有助于理解多个基本概念。它侧重于各种特定步骤&#xff0c;以进行直接的探索性数据分析。因此&#xff0c;其主要目标是测试一些检查初步数据特征的方法。大多数练习都是关于图表技术&#xff0c;通常用于数据挖掘。 为此…

51_蓝桥杯_蜂鸣器与继电器

一 电路 二 蜂鸣器与继电器工作原理 2.1蜂鸣器与继电器 2.2 十六进制与二进制 二进制 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 十六进制 0 1 2 3 4 5 6 7 8 9 A B C D E F 2.3非门 二 代码 …

C++初阶(十一) list

一、list的介绍及使用 1.1 list的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点…

第三节作业:基于 InternLM 和 LangChain 搭建你的知识库

参考文档&#xff1a;https://github.com/InternLM/tutorial/tree/main/langchain 基础作业&#xff1a;复现课程知识库助手搭建过程 (截图) 1.环境配置 2.知识库搭建 &#xff08;1&#xff09;数据收集 收集由上海人工智能实验室开源的一系列大模型工具开源仓库作为语料库来…

004 - Hugo, 分类

004 - Hugo, 分类content文件夹 004 - Hugo, 分类 content文件夹 ├─.obsidian ├─categories │ ├─Python │ └─Test ├─page │ ├─about │ ├─archives │ ├─links │ └─search └─post├─chinese-test├─emoji-support├─Git教程├─Hugo分类├─…

STL:优先级队列的实现

STL中优先级队列本质上就是堆。在上一篇博客中讲到过&#xff1a;堆是一种完全二叉树&#xff0c;逻辑结构上看起来像树&#xff0c;但在物理结构中是存储在线性表中。与普通线性表不同的是&#xff0c;堆中数据大小是规律排列的&#xff1a;小堆中每个节点都大于它的父节点&am…

2024免费人像摄影后期处理工具Portraiture4.1

Portraiture作为一款智能磨皮插件&#xff0c;确实为Photoshop和Lightroom用户带来了极大的便利。通过其先进的人工智能算法&#xff0c;它能够自动识别并处理照片中的人物皮肤、头发和眉毛等部位&#xff0c;实现一键式的磨皮美化效果&#xff0c;极大地简化了后期处理的过程。…

QKD安全攻击防御方案分析和分级评估研究报告

今天分享的是行业报告&#xff1a;《QKD安全攻击防御方案分析和分级评估研究报告》 &#xff08;内容出品方&#xff1a;量子信息网络产业联盟&#xff09; 报告共计&#xff1a;180页 来源&#xff1a;《见鹿报告》 前言 量子通信是量子信息科学的重要分支&#xff0c;它…

人工智能学习与实训笔记(十四):Langchain之Agent

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 本篇目录 0、概要 1、Agent整体架构 2、langchain中agent实现 3、Agent业务实现逻辑 0、概要 Agent是干什么的&#xff1f; Agent的核心思想是使用语言模型&#xff08;LLM&#xff09;作为推理的大脑…

飞行路线(分层图+dijstra+堆优化)(加上题目选数复习)

飞行路线 这一题除了堆优化和dijstra算法和链式前向星除外还多考了一个考点就是&#xff0c;分层图&#xff0c;啥叫分层图呢&#xff1f;简而言之就是一个三维的图&#xff0c;按照其题意来说有几个可以免费的点就有几层&#xff0c;而且这个分层的权值为0&#xff08;这样就相…

嵌入式Qt 计算器界面设计

一.计算器界面设计 计算机界面程序分析&#xff1a; 需要用到的组件&#xff1a; 界面设计&#xff1a; 界面设计实现&#xff1a; 实验1&#xff1a;计算器界面设计 #include <QtGui/QApplication> #include <QWidget> //主窗口 #include <QLineEdit> //文…

由斐波那契数列探究递推与递归

斐波那契数列定义&#xff1a; 斐波那契数列大家都非常熟悉。它的定义是&#xff1a; 对于给定的整数 x &#xff0c;我们希望求出&#xff1a; f ( 1 ) f ( 2 ) … f ( x ) f(1)f(2)…f(x) f(1)f(2)…f(x) 的值。 有两种方法,分别是递推(迭代)与递归 具体解释如下图 备注…

Mysql知识点汇总

Mysql知识点汇总 1. Mysql基本场景的简单语句。2. Mysql的增删改查&#xff0c;统计表中的成绩最好的两个同学的名字&#xff0c;年级等。3&#xff1a;请使用多种方法查询每个学生的每门课分数>80的学生姓名4、order by&#xff0c;group by&#xff0c;子查询4.1、having和…

优化嵌入式系统电源管理以提高稳定性

&#xff08;本文为简单介绍&#xff0c;观点源于网络&#xff09; 在嵌入式系统的领域中&#xff0c;电源管理扮演着至关重要的角色&#xff0c;关乎系统稳定性与用户体验。如果电源管理做得不好&#xff0c;就可能导致系统不稳定、数据丢失&#xff0c;甚至硬件损坏。电源管…

springboot186人格障碍诊断系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

03 SS之返回JSON+UserDetail接口+基于数据库实现RBAC

1. 返回JSON 为什么要返回JSON 前后端分离成为企业应用开发中的主流&#xff0c;前后端分离通过json进行交互&#xff0c;登录成功和失败后不用页面跳转&#xff0c;而是给前端返回一段JSON提示, 前端根据JSON提示构建页面. 需求: 对于登录的各种状态 , 给前端返回JSON数据 …
最新文章