Perl 中的 OS 页面对齐分配
Posted
技术标签:
【中文标题】Perl 中的 OS 页面对齐分配【英文标题】:OS Page aligned allocation in Perl 【发布时间】:2018-10-18 16:17:34 【问题描述】:驱动程序通过 ioctl 接口公开其 API。
ioctl 调用的参数是一个内存缓冲区,它的地址 必须与操作系统页面大小对齐。
例如,C 中的分配会调用 valloc(或 posix_memalign)
像这样简单的 Perl 缓冲区分配:
$buffer = "\0" x BUFFER_SIZE ;
还不够,因为很可能是标量的起始地址 不会与操作系统页面大小对齐。
有没有简单的方法来实现这一点?
注意:我将缓冲区转换为 C 地址,如下所示:
my $c_address = unpack('Q', pack('P', $buffer));
谢谢! 埃亚尔
【问题讨论】:
只要分配BUFFER_SIZE * 2
,就可以在缓冲区中找到一页内存。
你也许还可以使用(古老的)Mmap 模块
【参考方案1】:
有多种解决方案,但按照书本,您可以使用IO::AIO
模块,该模块具有IO::AIO::mmap
功能。基本上,你会做这样的事情(未经测试):
use IO::AIO
IO::AIO::mmap
my $buffer, BUFFER_SIZE, IO::AIO::PROT_READ | IO::AIO::PROT_WRITE,
IO::AIO::MAP_PRIVATE | IO::AIO::MAP_ANONYMOUS, undef
or die "mmap failure: $!";
$buffer
将在您 undef
或超出范围时自动取消映射,或者您可以使用 IO::AIO::munmap $buffer
。
您也可以通过调整一些更大的内存分配以其他方式自行完成,但您至少需要查询页面大小,因此如果没有模块的帮助,纯 perl 解决方案无法移植,和/或浪费内存。
【讨论】:
感谢您的回答。 IO::AIO 或 File::Map 或 Sys::Mmap 的 mmap 解决方案很好,当然是首选方式。但是在我的生产环境中,我不能(暂时)将模块添加到 perl 安装(本地编译)中,并且安装中不存在这些模块。所以我想对我来说唯一的方法是分配更多的内存并计算对齐的地址。【参考方案2】:这是一个不使用 mmap 解决问题的示例。
基本上,代码完成了 posix_memalign() 的工作。
# Required for 'syscall' below
#
require 'syscall.ph';
use strict;
use warnings;
# Linux / unix specific
#
my $PAGE_SIZE = `getconf PAGE_SIZE`;
# Arg = size of requested buffer
#
# return = 1. allocated buffer
# 2. C address of allocated buffer
# 3. Offset for aligned buffer
#
# Code is not portable and tested on x86_64 only.
#
sub valloc
my ($size, $ALIGN) = @_;
$ALIGN = $PAGE_SIZE
unless ($ALIGN);
my $buffer = "\0" x ($size + $ALIGN - 1);
my $address = unpack('Q', pack('p', $buffer));
my $aligned_address = (($address + $ALIGN - 1) & (-$ALIGN));
my $offset = $aligned_address - $address;
return ($buffer, $address, $offset);
#-------------------------------------------------------------
# Example to a function that accepts C address
#
sub cat
my ($path) = @_;
open (my $fh, '<', $path) || die "$path: $!\n";
my $size = -s $fh;
my ($buffer, $address, $offset) = valloc($size);
syscall(&SYS_read, fileno($fh), $address + $offset, $size);
close $fh;
return substr($buffer, $offset, $size);
#-------------------------------------------------------------
my $content = cat(__FILE__);
print $content;
【讨论】:
尚未运行您的脚本,但 getconf 没有在任何地方定义,而且它不是来自 syscall.ph。 另外,这不是 posix memalign 所做的——您的示例将内存过度分配了两倍,而 posix_memalign 没有(在实际实现中)这样做。当然,这在很多情况下都是可以接受的。 这不是两倍,只是页面的额外大小。它是(大小 + 对齐 - 1)。 cat() 函数示例需要 syscall.ph。不是 valloc 实现的一部分。 确实 - 我错误地认为对齐是问题中缓冲区的大小,但事实并非如此。以上是关于Perl 中的 OS 页面对齐分配的主要内容,如果未能解决你的问题,请参考以下文章
分配粒度和内存页面大小(x86处理器平台的分配粒度是64K,内存页是4K,所以section都是0x1000对齐,硬盘扇区大小是512字节,所以PE文件默认文件对齐是0x200)