如何以编程方式获取 linux 内核页面大小

Posted

技术标签:

【中文标题】如何以编程方式获取 linux 内核页面大小【英文标题】:How to get linux kernel page size programmatically 【发布时间】:2011-02-03 15:28:05 【问题描述】:

我正在为 IA64 开发一个 Linux 模块。我当前的问题是驱动程序使用 PAGE_SIZE 和 PAGE_SHIFT 宏进行 dma 页面分配。我遇到的问题是编译驱动程序的机器不是运行驱动程序所需的机器。因此,如果编译机器上的 PAGE_SIZE 为 2^14K,而目标机器为 2^16K,则驱动程序失败。

我不想把这个问题变成关于在不是运行模块的机器上编译模块的“最佳实践”问题。我理解这方面的问题。我发现人们大多使用 getpagesize() 或 sysconf(_SC_PAGE_SIZE)。这两个选项不在 ia64 内核头文件中,所以我不能使用它们。还有其他方法可以获得运行时 PAGE_SIZE 吗?

我正在查看的选项:

正在读取 /proc 中的某些文件? 系统调用? 让我通过推理计算 PAGE_SIZE 的其他函数(例如 ORDER、getpageshift 等)? 其他?

【问题讨论】:

您是说PAGE_SIZE 可针对 IA64 架构进行配置,而不是固定的?我认为 PAGE_SIZE 对于给定的架构是固定的(例如,对于 x86 总是 4096)。 IA64 确实支持多种页面大小:informit.com/articles/article.aspx?p=29961&seqNum=3 【参考方案1】:

尝试使用getconf 实用程序,它可以让您轻松检索页面大小。

getconf PAGESIZE

【讨论】:

我认为这不能回答 OP 的问题,但这是有用的信息,因为 sysconf(_SC_PAGESIZE) 似乎在 linuxia64 上返回 4K(而 mprotect 在未与 16K 边界对齐的页面上失败)。 我的 x86-64 只返回 4k,但它有 4k、2M 和 1G 页面。【参考方案2】:

一种近似的方法是读取/proc/meminfo 并检查Mapped 的大小(我现在的52544 kB),然后检查/proc/vmstat 中的nr_mapped(我现在的131136)。最后PAGE_SIZE = Mapped/nr_mapped。有时这会给你一个准确的值(如我引用的当前示例),有时它是近似值但非常接近。 希望这会有所帮助!

【讨论】:

我没有找到 Mapped/nr_mapped 甚至接近 getconf PAGESIZE 提供的内容 @MCP 在我的机器上Mapped/nr_mapped = 379512KB/94878 = 4KBgetconf PAGESIZE 给出 4096。它们确实匹配。能分享一下你看到的吗?【参考方案3】:

查找页面大小的一种方法是从进程的 smap 中获取它。

例如:

cd /proc/1
grep -i pagesize smaps

KernelPageSize:        4 kB
MMUPageSize:           4 kB

【讨论】:

奇怪地使用了你的技巧,我在 ARM 上看到了 4 kB,但在运行内核的 arch/arm/include/asm/page.h 内部,它是 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT),显然它 is 1024 字节,当从我的驱动程序,正如定义 page.h 所建议的那样。什么给了?【参考方案4】:

如果您尝试构建内核模块,则至少需要为模块将运行的内核配置的内核头文件。这些将定义您需要的页面大小宏。如果您没有正确配置的头文件,内核将拒绝加载您的模块。

在一台机器上编译模块以在另一台机器上运行并没有错,即使它是不同的架构。您只需要针对正确的内核源代码进行构建。

【讨论】:

这不是内核源的问题。它是关于加载在机器上编译的模块,以运行到另一台机器(两者具有相同的内核版本但配置不同)。我不想针对新配置重新编译,因为 PAGE_SIZE 已更改。我希望从内核中获取该参数作为 API,而不是作为 MACRO(在编译时解决)。 PAGE_SIZE 是非常基本的,并且在构建模块本身时对于模块是必需的..【参考方案5】:

这就是我最终所做的:

重新设计我当前的模块以获取一个名为 page_shift 的新模块参数,并使用它来计算 PAGE_SIZE (PAGE_SIZE = 1 << PAGE_SHIFT) 创建了一个模块加载器包装器,它使用来自 libc 的 getconf API 获取当前系统 PAGE_SHIFT。此包装器获取当前系统页面移位并将其作为模块参数传递。

现在模块正在以不同的 PAGE_SIZE 加载到不同的架构上,没有任何问题。

【讨论】:

菜鸟问题。你能解释一下使用 getconf API 的包装器吗?我的意思是你在 popen() 函数中调用了 getconf 命令还是什么。多一点的光线将非常感激。【参考方案6】:

我担心这是不可能的,因为页面大小是内核的一部分。 如果您使用工具链来编译内核模块,则需要了解页面大小。

所以至少在当前的内核架构下是不可能的。

【讨论】:

【参考方案7】:

您可以只运行一个测试,只需对具有不同偏移量的文件进行 mmap 映射,然后查看哪个失败。虽然在内核模块中可能很烦人,但也许还有其他类似的测试可以使用。

【讨论】:

以上是关于如何以编程方式获取 linux 内核页面大小的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式获取当前页面

Laravel 以编程方式获取特定页面

我如何以编程方式确定 Github 目录页面中所有当前存在的目录?

Javascript - 如何以编程方式获取 Youtube 视频的网址? (自我回答)

如何从 Linux 内核模块中的逻辑地址获取物理地址?

浅谈Swift如何快速获取Web页面中图片资源的大小