semop#
Perform one or more System V semaphore operations atomically.
semop is the workhorse of SysV semaphore use: it takes a semaphore set
identifier (from semget) and a packed list of operations, then
asks the kernel to apply them all or none. Each operation names a single
semaphore within the set, an amount to add to it (typically -1 to
wait, +1 to signal), and a flag word. The kernel blocks the caller
until every requested operation can proceed, then commits them as one
unit. This is what makes semop usable as a mutual-exclusion or
counting-resource primitive between unrelated processes.
Synopsis#
semop $semid, $opstring
What you get back#
True on success, false on error with $! set. The truth value is
the only result — semop communicates outcome by the side effect on
the semaphore set, not through its return. Check it on every call:
semop($semid, $ops)
or die "semop failed: $!";
A false return means no operation in OPSTRING was applied; SysV
semantics guarantee all-or-nothing.
Global state it touches#
Sets $! on failure, with the usual errno values from semop(2):
EAGAIN when IPC_NOWAIT was set and the operation would block,
EIDRM when the semaphore set was removed while waiting, EINTR when
a signal interrupted the wait, EINVAL for a bad semaphore number or
identifier, EACCES for a permission mismatch against the set’s mode.
The operation structure#
OPSTRING is a concatenation of fixed-width records. Each record is
three native-width short integers and is produced with pack:
pack("s!3", $semnum, $semop, $semflag)
$semnum— the index of the target semaphore within the set (0throughnsems-1, wherensemsis the size passed tosemget).$semop— the amount to add to the semaphore’s value. Negative values wait until the semaphore is at least|$semop|before subtracting; positive values add unconditionally and wake waiters; zero waits until the semaphore reaches zero.$semflag— bitwise OR ofIPC_NOWAIT(fail withEAGAINinstead of blocking) andSEM_UNDO(have the kernel reverse the operation if the process exits without undoing it — essential for lock-like uses where a crash must not leave the semaphore held).
The length of OPSTRING implies the number of operations: the kernel
processes length($opstring) / sizeof(struct sembuf) records. Passing
two records packed end-to-end asks for two operations applied
atomically.
The s! in the template is the native short, which matches the
sembuf layout on Linux. Plain s is network-order signed 16-bit and
will silently mis-pack on any platform where sizeof(short) != 2 or
where endianness differs from network order.
Examples#
Wait on (decrement by 1) semaphore 0 of the set in $semid:
my $op = pack("s!3", 0, -1, 0);
semop($semid, $op)
or die "wait on semaphore failed: $!";
Signal (increment by 1) the same semaphore:
my $op = pack("s!3", 0, 1, 0);
semop($semid, $op)
or die "signal failed: $!";
Try to acquire without blocking — fail fast if someone else holds it:
use POSIX qw(EAGAIN);
my $op = pack("s!3", 0, -1, IPC_NOWAIT);
unless (semop($semid, $op)) {
die "semop: $!" unless $! == EAGAIN;
# contended; caller decides what to do
}
Acquire semaphore 0 with automatic release on process exit — the pattern for crash-safe mutual exclusion:
my $op = pack("s!3", 0, -1, SEM_UNDO);
semop($semid, $op) or die "lock: $!";
# critical section
my $release = pack("s!3", 0, 1, SEM_UNDO);
semop($semid, $release) or die "unlock: $!";
Two operations applied atomically — wait for semaphore 0 and semaphore 1 together, releasing neither if either would block:
my $ops = pack("s!3", 0, -1, 0)
. pack("s!3", 1, -1, 0);
semop($semid, $ops) or die "paired wait: $!";
Wait for semaphore 0 to reach zero — the “barrier” form, where
$semop is zero rather than negative:
my $op = pack("s!3", 0, 0, 0);
semop($semid, $op) or die "barrier: $!";
Edge cases#
Short
OPSTRING: the length must be an exact multiple of thesembufrecord size. A truncated or padded string givesEINVAL. Always build the string frompackcalls, never by hand.Native width matters:
s!3nots3. A script written with plainsworks on a 16-bit-short host by accident and breaks elsewhere.Zero-length
OPSTRING: asks for zero operations. The kernel accepts this and returns success without side effects — rarely what you want. Guard the call withreturn unless length $ops.Signal interruption: a blocked
semopreturning with$!set toEINTRis not a bug — retry unless you installed a handler that wants to abort. Perl’s default$SIG{*}handling does not automatically restart the syscall.SEM_UNDOwith matched pairs: the kernel tracks the cumulative adjustment per semaphore per process. Acquiring twice withSEM_UNDOthen releasing once leaves an outstanding-1to be undone on exit. Pair every-1with a matching+1in normal flow;SEM_UNDOis insurance against crashes, not a substitute for releasing.Removed set while waiting: if another process calls
semctlwithIPC_RMID, every blockedsemopreturns false with$!set toEIDRM. The semaphore identifier is invalid from that point on.Permissions:
semopchecks alter-permission on the set. A process that cansemgeta read-only handle still cannotsemopit.Large operation counts: the kernel caps the number of operations per call at
SEMOPM(typically 32). Building anOPSTRINGwith more records fails withE2BIG.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
semget— obtain the identifier you pass as the first argument; the two are always used togethersemctl— set or query semaphore values, and remove the set when you’re done with itpack— build eachsembufrecord;s!3is the template you wantmsgsnd— the message-queue counterpart when you need to pass data rather than signal availabilityshmget— the shared-memory counterpart; semaphores are typically used to guard access to a SysV shared-memory segmentIPC::SysV— exports theIPC_NOWAIT,SEM_UNDO, andIPC_RMIDconstants used in$semflagand insemctlcommandsIPC::Semaphore— object wrapper oversemget/semop/semctlwhen a procedural interface feels too bare