Linux用户空间将虚拟地址转化为物理地址 您所在的位置:网站首页 地址转换算法 Linux用户空间将虚拟地址转化为物理地址

Linux用户空间将虚拟地址转化为物理地址

2023-07-27 07:27| 来源: 网络整理| 查看: 265

目录

引言

地址绑定

MMU(内存管理单元)

基本硬件

进程如何从磁盘映射到内存

磁盘和内存的映射过程

MMU方案

开始编码

创建我们的缓冲区

/proc/[pid]/pagemap

写 /dev/mem

结论和完整代码清单

DPDK是如何操作的?

推荐文章

 

引言

在理解“了解Linux内核”中有关内存管理的章节中,我认为尝试编写将虚拟内存地址转换为物理地址的程序会很有趣。而且,我想在用户空间上使用它。再进一步,为什么不尝试获取缓冲区的物理地址,转到内存中的该位置,对其进行修改,然后使用虚拟地址查看更改。

尝试在用户空间中完成此任务存在一些问题:

虚拟内存背后的想法是提供连续内存的地址空间。进程的内存很可能存储在不连续的块中。没有保证page在系统的物理内存中。它可以在交换区或缓存中。可能没有实际地址!出于安全原因,即使进程的UID为0,进程也无权访问系统的原始内存。

我们可以采用两种方法来获取物理地址:

给内核添加一个系统调用,给定一个虚拟地址,它将返回物理地址。但是,修改内核违反了从用户空间执行所有操作的规则,因此我们必须将其排除在外。将page映射文件用于进程(内核2.6.25中已添加)以获取页面映射到的帧,然后使用该页面查找/dev/mem并修改那里的缓冲区。

使用这种方法,完全有可能在用户空间中将虚拟地址转换为物理地址。但是,要验证我们的翻译是正确的,需要阅读/dev/mem。这确实需要对内核进行一次小的修改(更改配置选项),但稍后会进行更多修改。

内存由大量word或array组成,每个word或array都有与之关联的地址。现在,CPU的工作是从基于内存的程序计数器中获取指令。现在,这些指令可能会导致加载或存储到特定的存储器地址。地址绑定是从一个地址空间映射到另一地址空间的过程。逻辑地址是CPU在执行过程中生成的地址,而物理地址是指存储单元(已加载到内存中的单元)中的位置。请注意,用户仅处理逻辑地址(虚拟地址)。逻辑地址尤其由MMU或地址转换单元进行转换。该过程的输出是适当的物理地址或代码/数据在RAM中的位置。

 

地址绑定

地址绑定可以通过三种不同的方式完成:

编译时间–如果您知道在编译期间进程将驻留在内存中,则会生成绝对地址,即在编译过程中将物理地址嵌入到程序的可执行文件中。将可执行文件作为进程加载到内存中的速度非常快。但是,如果生成的地址空间被其他进程所占用,则程序将崩溃,并且有必要重新编译程序以更改地址空间。加载时间–如果在编译时不知道进程将驻留在哪里,则将生成可重定位的地址。加载程序将可重定位地址转换为绝对地址。加载程序将主存储器中进程的基地址添加到所有逻辑地址中,以生成绝对地址。在这种情况下,如果进程的基址发生更改,则我们需要再次重新加载进程。执行时间-指令在内存中,正在由CPU处理。此时可以分配和/或释放其他内存。如果可以在执行期间将进程从一个内存移动到另一个内存(动态链接-在加载或运行时完成链接),则使用此方法。例如–压实。

 

MMU(内存管理单元)

虚拟地址和物理地址之间的运行时映射是通过称为MMU的硬件设备完成的。

在内存管理中,操作系统将处理进程并在磁盘和内存之间移动进程以执行。它跟踪可用和已使用的内存。

指令执行周期包括以下步骤:

从存储器中获取第一条指令,例如ADD A,B然后将这些指令解码,即A和B的加法然后在某个特定的存储位置进行进一步的加载或存储。 基本硬件

由于主存储器和寄存器内置在处理器中,CPU只能访问它们,因此每条指令都应写入直接访问存储 设备中。

如果从寄存器访问CPU指令,则由于寄存器内置在CPU中,因此可以在一个CPU时钟周期内完成。如果指令驻留在主存储器中,那么它将通过存储器总线进行访问,这将花费大量时间。因此,对此的补救措施是在CPU和主内存之间添加快速内存,即为事务添加缓存。现在,我们应该确保流程位于法定地址内。合法地址由基址寄存器(拥有最小的物理地址)和限制寄存器(范围的大小)组成。

例如:

基址寄存器= 300040 限制寄存器= 120900 那么合法地址=(300040 + 120900)= 420940(含)。 法定地址=基本寄存器+限制寄存器

 

进程如何从磁盘映射到内存 通常,进程以二进制可执行文件的形式驻留在磁盘中。因此,执行过程应驻留在主存储器中。根据使用的内存管理,进程从磁盘移动到内存。进程以就绪队列的形式在磁盘中等待以获取内存。

 

磁盘和内存的映射过程

正常过程是从输入队列中选择进程并将其加载到内存中。进程执行时,它将访问内存中的数据和指令,一旦完成,它将释放内存,现在内存可用于其他进程。

 

MMU方案 CPU ------- MMU ------内存

CPU将为例如346生成逻辑地址MMU将为例如:14000生成重定位寄存器(基址寄存器)内存中的物理地址位于例如:(346 + 14000 = 14346)

图来源:https://users.dimi.uniud.it/~antonio.dangelo/OpSys/materials/Operating_System_Concepts.pdf 本文由Vaishali Bhatia贡献。如果您喜欢GeeksforGeeks并且愿意做出贡献,那么您也可以使用contribution.geeksforgeeks.org撰写文章或将您的文章邮寄至[email protected]。查看您的文章出现在GeeksforGeeks主页上,并帮助其他Geeks。

 

开始编码 创建我们的缓冲区

除了通常的malloc()调用之外,创建缓冲区以查找地址的过程还需要另外一步。内核不保证虚拟地址空间中的地址实际映射到内存中的物理地址。它可以存储在交换空间中,某个地方或整个地方的缓存中。为了避免这种可能性,我们可以mlock()用来强制将页面保留在系统的物理内存中。幸运的是,这很简单。只需将mlock()指针传递到缓冲区和缓冲区的大小,它将处理其余的内容。看起来像这样:

void* create_buffer(void) { size_t buf_size = strlen(ORIG_BUFFER) + 1; // Allocate some memory to manipulate void *buffer = malloc(buf_size); if(buffer == NULL) { fprintf(stderr, "Failed to allocate memory for buffer\n"); exit(1); } // Lock the page in memory // Do this before writing data to the buffer so that any copy-on-write // mechanisms will give us our own page locked in memory if(mlock(buffer, buf_size) == -1) { fprintf(stderr, "Failed to lock page in memory: %s\n", strerror(errno)); exit(1); } // Add some data to the memory strncpy(buffer, ORIG_BUFFER, strlen(ORIG_BUFFER)); return buffer; }

请注意,锁定后我将数据复制到缓冲区中。这是因为如果缓冲区所在的页面与父进程共享,则OS可能会采用写时复制分页机制。为了强制操作系统提供我们自己的页面,我们将数据锁定后写入缓冲区。

 

/proc/[pid]/pagemap

页面映射为用户提供了访问空间,以访问内核如何管理进程页面。这是一个二进制文件,因此从中提取信息有些棘手。

从文档1中可以看到,每页有64位信息。我们对页帧号0-54位感兴趣。

我们如何从页面地图中获取给定页面的页面框架号?首先,我们需要确定要映射到页面地图中的偏移量。可以这样进行:

#define PAGEMAP_LENGTH 8 offset = (unsigned long)addr / getpagesize() * PAGEMAP_LENGTH

给定一个地址,我们将其除以页面大小,然后乘以8。为什么是8?每页有64位或8字节的信息。

然后,我们在文件中寻找该位置并读取前7个字节。为什么是7?我们对咬伤0-54感兴趣。总共有55位。因此,我们读取了前7个字节(56位)并清除了位55。位55是我们不关心的软脏标志。

unsigned long get_page_frame_number_of_address(void *addr) { // Open the pagemap file for the current process FILE *pagemap = fopen("/proc/self/pagemap", "rb"); // Seek to the page that the buffer is on unsigned long offset = (unsigned long)addr / getpagesize() * PAGEMAP_LENGTH; if(fseek(pagemap, (unsigned long)offset, SEEK_SET) != 0) { fprintf(stderr, "Failed to seek pagemap to proper location\n"); exit(1); } // The page frame number is in bits 0-54 so read the first 7 bytes and clear the 55th bit unsigned long page_frame_number = 0; fread(&page_frame_number, 1, PAGEMAP_LENGTH-1, pagemap); page_frame_number &= 0x7FFFFFFFFFFFFF; fclose(pagemap); return page_frame_number; }

现在我们有了页面帧号,我们可以轻松计算出缓冲区的物理地址,例如2:

physcial_addr = (page_frame_number


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有