手动将 C 结构转换为包语法

如果你从 Perl 代码处理 C 二进制 API,通过 syscallioctlfcntl 函数,你需要知道如何以 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 ));