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, becausesyscallhas 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.phmissing:require 'syscall.ph'fails unlessh2phhas been run against the system headers. Without it theSYS_*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 $norvec($buf, $n-1, 8) = 0before the call, and never passlength $buflarger than the actual capacity.Argument count ceiling: 14 arguments after
NUMBER. Syscalls that take more (rare) are not reachable viasyscall.SYS_pipe()on Linux: the raw syscall returns the read-end fd but gives you no handle on the write-end fd. Usepipeinstead, 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
inton 32-bit builds. There is no portable way to pass them throughsyscall— 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),syscallraisesThe syscall function is unimplemented.Signal interruption: a syscall interrupted by a signal returns
-1with$!set toEINTR.syscalldoes 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 ofsyscall(SYS_write(), ...)sysread— buffered-bypass read; pairs withsyswriteand handles buffer management for yousysopen—open(2)with the full flag vocabulary, without needing the raw syscallpipe— the right way to create a pipe; avoids theSYS_pipe()write-end problempack— build the binary structs (struct timeval,struct stat, etc.) that syscalls expect in pointer argumentsunpack— decode the binary structs a syscall writes into your buffer