手动将 C 结构转换为包语法
如果你从 Perl 代码处理 C 二进制 API,通过 syscall
,ioctl
或 fcntl
函数,你需要知道如何以 C 兼容的方式构造内存。
例如,如果你曾经处理过一些期望一个 timespec
的函数,你会看到/usr/include/time.h
并找到:
struct timespec
{
__time_t tv_sec; /* Seconds. */
__syscall_slong_t tv_nsec; /* Nanoseconds. */
};
你和 cpp
一起跳舞,找到真正含义的东西:
cpp -E /usr/include/time.h -o /dev/stdout | grep __time_t
# typedef long int __time_t;
cpp -E /usr/include/time.h -o /dev/stdout | grep __syscall_slong_t
# typedef long int __syscall_slong_t
所以它是一个(签名的)int
echo 'void main(){ printf("%#lx\n", sizeof(__syscall_slong_t)); }' |
gcc -x c -include stdio.h -include time.h - -o /tmp/a.out && /tmp/a.out
# 0x8
它需要 8 个字节。所以 64bit 签了 int。而我正在使用 64 位处理器。 =)
Perldoc pack
说
q A signed quad (64-bit) value.
所以打包一个 timespec:
sub packtime {
my ( $config ) = @_;
return pack 'qq', @{$config}{qw( tv_sec tv_nsec )};
}
并打开一个 timespec:
sub unpacktime {
my ( $buf ) = @_;
my $out = {};
@{$out}{qw( tv_sec tv_nsec )} = unpack 'qq', $buf;
return $out;
}
现在你可以使用这些功能。
my $timespec = packtime({ tv_sec => 0, tv_nsec => 0 });
syscall( ..., $timespec ); # some syscall that reads timespec
later ...
syscall( ..., $timespec ); # some syscall that writes timespec
print Dumper( unpacktime( $timespec ));