手動將 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 ));