如何在 Linux 内核模块代码中以编程方式获取页面大小

问题描述 投票:0回答:8

我正在开发 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 等)?
  • 其他?
memory-management linux-device-driver dma
8个回答
72
投票

尝试使用

getconf
实用程序,它可以让您轻松检索页面大小(以字节为单位)。

getconf PAGESIZE

示例输出,以字节为单位:

4096

21
投票

一种近似的方法是读取

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


15
投票

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

例如:

cd /proc/1
grep -i pagesize smaps

输出:

KernelPageSize:        4 kB
MMUPageSize:           4 kB

7
投票

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

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


6
投票

这就是我最终所做的:

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

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


0
投票

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

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


0
投票

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


0
投票

为了在 Linux 内核模块代码中拥有正确的页面大小(如果不需要 getconf PAGESIZE...?),您应该做的是从 meminfo 和 vmstat 中找到最接近的 n^2 指数。

有一种正确的方法可以实现此目的,即使用 bc 命令中的对数,如下所示:

  • 从 /proc/meminfo 中检索“映射”kB 值:
  • 使用 bc 的对数函数求以 2 为底的指数
  • 将返回值四舍五入到最接近的单位
_x=$(grep Mapped /proc/meminfo | head -n 1 | tr -s ' ' | cut -d ' ' -f 2);
_x=$(echo "l($_x)/l(2)" | bc -l | cut  -d '.' -f 1);
_x=$(printf "%.0f" "$_x");
echo "$_x";
18

对 /proc/vmstat 中的 nr_mapped 值执行相同的操作:

_y=$(grep nr_mapped /proc/vmstat | cut -d ' ' -f 2);
_y=$(echo "l($_y)/l(2)" | bc -l | cut  -d '.' -f 1);
_y=$(printf "%.0f" "$_y");
echo "$_y";
16

最后,由于您有两个基于 kB 的值的指数,您可以将它们的最终结果除以并乘以 1024,以获得以字节为单位的页面大小:

echo $((2**$_x/2**$_y*1024));
4096
© www.soinside.com 2019 - 2024. All rights reserved.