OS Perl 中的页面对齐分配
OS Page aligned allocation in Perl
驱动程序通过 ioctl 接口公开其 API。
ioctl 调用的参数是一个内存缓冲区,它的地址
必须与 OS 页面大小对齐。
例如 C 中的分配将调用 valloc(或 posix_memalign)
缓冲区的简单 Perl 分配如下:
$buffer = "[=11=]" x BUFFER_SIZE ;
不够,因为很可能是标量的起始地址
不会与 OS 页面大小对齐。
有没有简单的方法可以做到这一点?
注意:我把缓冲区转换成C地址是这样的:
my $c_address = unpack('Q', pack('P', $buffer));
谢谢!
埃亚尔
有多种解决方案,但按照书上的说法,您可以使用 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: $!";
当您 undef
或它超出范围时,$buffer
将自动取消映射,或者您可以使用 IO::AIO::munmap $buffer
.
你也可以通过调整一些更大的内存分配来自己做,但你至少需要查询页面大小,所以如果没有模块的帮助,纯 perl 解决方案无法移植,and/or 浪费内存。
下面是一个不用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 = "[=10=]" 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;
驱动程序通过 ioctl 接口公开其 API。
ioctl 调用的参数是一个内存缓冲区,它的地址 必须与 OS 页面大小对齐。
例如 C 中的分配将调用 valloc(或 posix_memalign)
缓冲区的简单 Perl 分配如下:
$buffer = "[=11=]" x BUFFER_SIZE ;
不够,因为很可能是标量的起始地址 不会与 OS 页面大小对齐。
有没有简单的方法可以做到这一点?
注意:我把缓冲区转换成C地址是这样的:
my $c_address = unpack('Q', pack('P', $buffer));
谢谢! 埃亚尔
有多种解决方案,但按照书上的说法,您可以使用 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: $!";
当您 undef
或它超出范围时,$buffer
将自动取消映射,或者您可以使用 IO::AIO::munmap $buffer
.
你也可以通过调整一些更大的内存分配来自己做,但你至少需要查询页面大小,所以如果没有模块的帮助,纯 perl 解决方案无法移植,and/or 浪费内存。
下面是一个不用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 = "[=10=]" 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;