I/O · Fixed-length data

syscall#

Invoke a raw system call by its kernel number, passing the remaining arguments as int or as a pointer to a string buffer.

syscall is the escape hatch for operating-system calls the rest of the language does not wrap. The first argument is the syscall number — on Linux, the value of SYS_<name> from syscall.ph. Each remaining argument is passed either as an integer (if Perl already considers it numeric) or as a pointer to the scalar’s string buffer. It returns whatever the kernel returns. On failure it returns -1 and sets $!.

Synopsis#

syscall NUMBER, LIST

What you get back#

Whatever the kernel returned. Success values are syscall-specific — often a byte count, a file descriptor, or 0. Failure is -1 with $! set to the errno value.

Some syscalls (lseek, read on a pipe at EOF with errors, ptrace) can legitimately return -1 on success. To tell them apart from failures, clear $! before the call:

$! = 0;
my $r = syscall(SYS_lseek(), fileno($fh), 0, 1);
die "lseek: $!" if $r == -1 && $! != 0;

Global state it touches#

  • $! — set when the kernel returns -1. Not touched on success.

Argument passing rules#

Each argument in LIST is inspected at call time:

  • Numeric argument (a literal number, or a scalar that has been used in a numeric context) — passed as a C int.

  • Non-numeric argument — the scalar’s string buffer is passed by pointer. You are responsible for pre-extending the buffer to hold any data the kernel might write. A too-short buffer is a memory corruption bug, not a Perl error.

  • Read-only string (a literal, a __DATA__ string, anything Perl refuses to write through) — rejected, because syscall has to assume any string pointer might be written through.

  • Integer stored as a string (e.g. a value read from a file that was never used arithmetically) — may be passed as a pointer rather than an int. Force numeric interpretation by adding 0:

    syscall(SYS_write(), 0 + $fd, $buf, length $buf);
    

Up to 14 arguments are supported after the syscall number.

Examples#

Write a buffer to STDOUT using the raw write(2) syscall:

require 'syscall.ph';
my $s = "hi there\n";
syscall(SYS_write(), fileno(STDOUT), $s, length $s);

Pre-extend a buffer before a read:

require 'syscall.ph';
my $buf = "\0" x 4096;                # 4 KiB writable buffer
my $n = syscall(SYS_read(), fileno($fh), $buf, 4096);
die "read: $!" if $n == -1;
substr($buf, $n) = '';                # trim to actual bytes read

Call gettid(2) to get the kernel thread id (no libc wrapper on older glibc):

require 'syscall.ph';
my $tid = syscall(SYS_gettid());

Distinguish a legitimate -1 return from a real failure:

$! = 0;
my $off = syscall(SYS_lseek(), fileno($fh), 0, 1);   # SEEK_CUR
if ($off == -1 && $! != 0) {
    die "lseek failed: $!";
}

Force a stringified integer to be passed as int:

my $fd = read_config("fd");           # returns "3"
syscall(SYS_close(), 0 + $fd);        # without "0 +" it'd pass a pointer

Edge cases#

  • syscall.ph missing: require 'syscall.ph' fails unless h2ph has been run against the system headers. Without it the SYS_* constants are undefined and calls become hand-maintained magic numbers.

  • Read-only argument: passing a string literal or other read-only scalar croaks with Modification of a read-only value. Copy into a lexical first: my $s = "literal"; syscall(..., $s, ...).

  • Buffer too short: the kernel will happily write past the end of a scalar’s buffer if you lied about the length. This corrupts the Perl heap. Always pre-extend with $buf = "\0" x $n or vec($buf, $n-1, 8) = 0 before the call, and never pass length $buf larger than the actual capacity.

  • Argument count ceiling: 14 arguments after NUMBER. Syscalls that take more (rare) are not reachable via syscall.

  • SYS_pipe() on Linux: the raw syscall returns the read-end fd but gives you no handle on the write-end fd. Use pipe instead, which exposes both.

  • 64-bit values on 32-bit platforms: some syscalls take 64-bit arguments (offsets, sizes) that do not fit in a Perl int on 32-bit builds. There is no portable way to pass them through syscall — use a dedicated wrapper (Fcntl::lseek, sysseek, etc.) or a CPAN binding.

  • Unimplemented: on platforms without a syscall(2) entry point (or when built without support), syscall raises The syscall function is unimplemented.

  • Signal interruption: a syscall interrupted by a signal returns -1 with $! set to EINTR. syscall does not restart automatically — retry in user code if needed.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • syswrite — buffered-bypass write through a Perl filehandle; almost always what you want instead of syscall(SYS_write(), ...)

  • sysread — buffered-bypass read; pairs with syswrite and handles buffer management for you

  • sysopenopen(2) with the full flag vocabulary, without needing the raw syscall

  • pipe — the right way to create a pipe; avoids the SYS_pipe() write-end problem

  • pack — build the binary structs (struct timeval, struct stat, etc.) that syscalls expect in pointer arguments

  • unpack — decode the binary structs a syscall writes into your buffer