SysV IPC

semctl#

Perform a control operation on a System V semaphore set.

semctl is the Perl-level binding for the semctl(2) system call. It reads or modifies the state of the semaphore set identified by ID — getting or setting a single semaphore’s value, fetching or installing the full value vector, reading the associated semid_ds metadata, or removing the set entirely. CMD selects the operation; SEMNUM selects which semaphore within the set (ignored for set-wide commands like IPC_STAT, IPC_RMID, GETALL, SETALL); ARG is the command’s input or output buffer.

semctl is a thin wrapper — the semantics, the command constants, and the shape of ARG all come straight from the C-level call. You will almost always want:

use IPC::SysV;

first, to pull in the IPC_STAT, IPC_RMID, GETVAL, SETVAL, GETALL, SETALL, GETPID, GETNCNT, GETZCNT constants.

Synopsis#

semctl $id, $semnum, $cmd, $arg
semctl $id, 0,       IPC_RMID, 0            # destroy set
semctl $id, $n,      SETVAL,   $value       # set one
semctl $id, 0,       SETALL,   $packed      # set all

What you get back#

Return follows the same convention as ioctl:

  • undef on error (with $! set).

  • The string "0 but true" when the underlying call returned 0 — this string is false numerically (so it prints 0 in numeric context) but true in boolean context, letting you distinguish success-with-zero from failure in one test.

  • The actual integer return value otherwise.

The idiomatic test is therefore:

my $rc = semctl($id, $n, GETVAL, 0);
defined $rc or die "semctl: $!";

Check defined, not truth — a legitimately zero semaphore value would fail a plain if ($rc).

For CMD values that read data into ARG (IPC_STAT, GETALL), ARG must be a modifiable lvalue (a plain variable, not a literal or expression); on success the buffer is overwritten in place with the returned bytes and you unpack it yourself.

Global state it touches#

Sets $! on failure. No other interpreter globals involved.

Examples#

Read a single semaphore’s current value:

use IPC::SysV qw(GETVAL);
my $value = semctl($id, $semnum, GETVAL, 0);
defined $value or die "GETVAL: $!";

Set a single semaphore:

use IPC::SysV qw(SETVAL);
semctl($id, $semnum, SETVAL, 1)
    or die "SETVAL: $!";

Read all semaphore values at once. The buffer has to be pre-allocated to the right size — nsems native shorts — before the call. pack with an unused count is the usual way:

use IPC::SysV qw(GETALL);
my $nsems = 4;
my $buf   = pack("s!*", (0) x $nsems);      # allocate room
semctl($id, 0, GETALL, $buf) or die $!;
my @vals = unpack("s!*", $buf);

Install a full set of values:

use IPC::SysV qw(SETALL);
my $buf = pack("s!*", 1, 0, 0, 1);
semctl($id, 0, SETALL, $buf)
    or die "SETALL: $!";

Fetch the semid_ds structure for the set. ARG is written in place with the packed struct; its layout is platform-specific, so unpack with the help of IPC::Semaphore rather than by hand unless you have to:

use IPC::SysV qw(IPC_STAT);
my $ds = "";
semctl($id, 0, IPC_STAT, $ds)
    or die "IPC_STAT: $!";

Destroy the semaphore set. SEMNUM and ARG are unused; pass 0:

use IPC::SysV qw(IPC_RMID);
semctl($id, 0, IPC_RMID, 0)
    or die "IPC_RMID: $!";

Edge cases#

  • ARG must be a real variable for read commands. GETALL and IPC_STAT write into ARG; passing a literal (semctl($id, 0, GETALL, "")) or a read-only expression is a runtime error. The variable’s existing contents are overwritten, not appended to.

  • ARG must be pre-sized. The kernel writes exactly nsems * sizeof(short) bytes for GETALL, or sizeof(struct semid_ds) for IPC_STAT. Passing a shorter buffer is undefined behaviour in the underlying C call; pack("s!*", (0) x $nsems) is the standard idiom for GETALL.

  • Native short, not portable short. Semaphore values are C short (signed, typically 16-bit). Use s! in pack / unpack — the ! picks the platform-native type. Plain s forces little-endian 16-bit and will produce wrong results on big-endian or LP64-short systems.

  • "0 but true" returns. GETVAL on a semaphore whose value is 0, GETPID/GETNCNT/GETZCNT returning zero, and other zero-valued successes all return the literal string "0 but true". Arithmetic contexts see 0; boolean contexts see true. Use defined to check for failure.

  • SEMNUM is ignored for set-wide commands. IPC_STAT, IPC_RMID, SETALL, GETALL act on the whole set. Pass 0 by convention; passing any other value is harmless but obscures intent.

  • After IPC_RMID the id is invalid. Every subsequent semctl or semop on $id fails with EINVAL / EIDRM. Do not cache the id past removal.

  • The set is kernel-global and persists across process exits. A set created by a crashed script remains until someone calls IPC_RMID (or the system reboots). Use ipcs -s / ipcrm -s from the shell to inspect or clean up leaked sets.

  • Permissions come from semget. The IPC_CREAT | mode passed at creation time controls who may issue which semctl commands. EACCES from semctl means the mode bits disallow the requested operation for the current effective uid.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • semget — create or look up the semaphore set whose id you then pass to semctl

  • semop — the actual wait / signal operations; semctl is for control and inspection, semop does the work

  • msgctl — the analogous control call for System V message queues; same return-value convention

  • shmctl — the analogous control call for System V shared memory; same return-value convention

  • pack — how to build the ARG buffer with s! for semaphore values and the semid_ds struct

  • IPC::Semaphore — object wrapper that hides semctl behind named methods; reach for it when a script does more than one or two calls