semop(2)
๐ธ NAME ๐ธ
semop, semtimedop โ System V semaphore operations ๐
๐ LIBRARY ๐
Standard C library (libc, -lc)
โจ SYNOPSIS โจ
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *_Nullable timeout);
Feature Test Macro Requirements for glibc (peep feature_test_macros(7) for the tea):
semtimedop():_GNU_SOURCE
๐ DESCRIPTION ๐
okay so every single semaphore in a System V semaphore set is walking around with these little associated values, right:
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid; /* PID of process that last
modified the semaphore value */
semop() does operations on selected semaphores in the set that semid points at, bestie. Every one of the nsops elements in the array that sops points to is a struct describing an operation to run on one single semaphore. The members of that struct are of type struct sembuf, and they're these:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
The flags that get recognized in sem_flg are IPC_NOWAIT and SEM_UNDO. If an operation specifies SEM_UNDO, it gets automatically undone when the process terminates. no cap. ๐ซถ
The whole set of operations tucked inside sops gets performed in array order, and it's done atomically โ meaning it all happens as one complete unit, or literally none of it happens at all. periodt. The behavior of the system call when not everything can go down immediately depends on whether IPC_NOWAIT is chilling in the individual sem_flg fields, as noted below.
Each operation runs on the sem_num-th semaphore of the semaphore set, where the first semaphore of the set is numbered 0 (zero-indexed, she's a programmer girlie ๐
). There are three types of operation, and you can tell them apart by the value of sem_op.
- If
sem_opis a positive integer, the operation adds this value to the semaphore value (semval). On top of that, ifSEM_UNDOis specified for this operation, the system subtracts the valuesem_opfrom the semaphore adjustment (semadj) value for this semaphore. This operation can always proceed โ it never makes a thread wait. The calling process has to have alter permission on the semaphore set. - If
sem_opis zero, the process needs read permission on the semaphore set. This is the classic "wait-for-zero" setup: ifsemvalis zero, the operation can immediately proceed. Otherwise, ifIPC_NOWAITis specified insem_flg,semop()fails witherrnoset toEAGAIN(and none of the operations insopsgets performed โ nothing. ๐ซ). Otherwise,semzcnt(the count of threads waiting for this semaphore's value to become zero) gets incremented by one, and the thread goes to sleep until one of these things happens:semvalbecomes0, at which point the value ofsemzcntis decremented.- The semaphore set is removed:
semop()fails, witherrnoset toEIDRM. - The calling thread catches a signal: the value of
semzcntis decremented andsemop()fails, witherrnoset toEINTR.
- If
sem_opis less than zero, the process needs alter permission on the semaphore set. Ifsemvalis greater than or equal to the absolute value ofsem_op, the operation can proceed immediately: the absolute value ofsem_opgets subtracted fromsemval, and, ifSEM_UNDOis specified for this operation, the system adds the absolute value ofsem_opto the semaphore adjustment (semadj) value for this semaphore. If the absolute value ofsem_opis greater thansemval, andIPC_NOWAITis specified insem_flg,semop()fails, witherrnoset toEAGAIN(and none of the operations insopsgets performed). Otherwise,semncnt(the counter of threads waiting for this semaphore's value to increase) gets incremented by one and the thread naps until one of these drops:semvalbecomes greater than or equal to the absolute value ofsem_op: the operation now proceeds, just like described above.- The semaphore set is removed from the system:
semop()fails, witherrnoset toEIDRM. - The calling thread catches a signal: the value of
semncntis decremented andsemop()fails, witherrnoset toEINTR.
On successful completion, the sempid value for each semaphore specified in the array that sops points to gets set to the caller's process ID. Plus, sem_otime gets set to the current time. โจ
๐ฆ semtimedop() ๐ฆ
semtimedop() acts identically to semop() except that in those cases where the calling thread would go to sleep, the duration of that sleep gets capped by the amount of elapsed time specified by the timespec structure whose address is passed in the timeout argument. (Heads up: this sleep interval gets rounded up to the system clock granularity, and kernel scheduling delays mean the interval might overrun by a small amount. ๐) If the specified time limit has been reached, semtimedop() fails with errno set to EAGAIN (and none of the operations in sops gets performed). If the timeout argument is NULL, then semtimedop() behaves exactly like semop().
Note that ifsemtimedop()gets interrupted by a signal, causing the call to fail with the errorEINTR, the contents oftimeoutare left unchanged.
๐ RETURN VALUE ๐
On success, semop() and semtimedop() return 0. On failure, they return -1, and set errno to indicate the error. ๐ญ
๐ ERRORS ๐
E2BIGโ The argumentnsopsis greater thanSEMOPM, the maximum number of operations allowed per system call.EACCESโ The calling process doesn't have the permissions required to perform the specified semaphore operations, and doesn't have theCAP_IPC_OWNERcapability in the user namespace that governs its IPC namespace.EAGAINโ An operation couldn't proceed immediately and eitherIPC_NOWAITwas specified insem_flgor the time limit specified intimeoutexpired.EFAULTโ An address specified in either thesopsor thetimeoutargument isn't accessible.EFBIGโ For some operation the value ofsem_numis less than0or greater than or equal to the number of semaphores in the set.EIDRMโ The semaphore set was removed.EINTRโ While blocked in this system call, the thread caught a signal; seesignal(7).EINVALโ The semaphore set doesn't exist, orsemidis less than zero, ornsopshas a nonpositive value.ENOMEMโ Thesem_flgof some operation specifiedSEM_UNDOand the system doesn't have enough memory to allocate the undo structure.ERANGEโ For some operationsem_op+semvalis greater thanSEMVMX, the implementation dependent maximum value forsemval.
๐ STANDARDS ๐
POSIX.1-2024.
๐ HISTORY ๐
semop()- SVr4, POSIX.1-2001.
semtimedop()- SVr4, Linux 2.5.52 (backported into Linux 2.4.22), glibc 2.3.3.
๐ NOTES ๐
The sem_undo structures of a process aren't inherited by the child produced by fork(2), but they are inherited across an execve(2) system call. iykyk.
semop() is never automatically restarted after being interrupted by a signal handler, regardless of the setting of the SA_RESTART flag when establishing a signal handler.
A semaphore adjustment (semadj) value is a per-process, per-semaphore integer that is the negated sum of all operations performed on a semaphore specifying the SEM_UNDO flag. Each process has a list of semadj values โ one value for each semaphore on which it has operated using SEM_UNDO. When a process terminates, each of its per-semaphore semadj values gets added to the corresponding semaphore, which thus undoes the effect of that process's operations on the semaphore (but peep BUGS below, bestie ๐). When a semaphore's value is directly set using the SETVAL or SETALL request to semctl(2), the corresponding semadj values in all processes are cleared. The clone(2) CLONE_SYSVSEM flag allows more than one process to share a semadj list; see clone(2) for the deets.
The semval, sempid, semzcnt, and semnct values for a semaphore can all be retrieved using appropriate semctl(2) calls.
โญ Semaphore limits โญ
These limits on semaphore set resources affect the semop() call:
SEMOPMโ Maximum number of operations allowed for onesemop()call. Before Linux 3.19, the default value for this limit was32. Since Linux 3.19, the default value is500. On Linux, this limit can be read and modified via the third field of/proc/sys/kernel/sem.
Note: this limit should not be raised above1000, because of the risk of thatsemop()fails due to kernel memory fragmentation when allocating memory to copy thesopsarray.
SEMVMXโ Maximum allowable value forsemval: implementation dependent (32767).
The implementation has no intrinsic limits for the adjust on exit maximum value (SEMAEM), the system wide maximum number of undo structures (SEMMNU) and the per-process maximum number of undo entries system parameters.
๐ BUGS ๐
When a process terminates, its set of associated semadj structures gets used to undo the effect of all of the semaphore operations it performed with the SEM_UNDO flag. This raises a difficulty: if one (or more) of these semaphore adjustments would result in an attempt to decrease a semaphore's value below zero, what's an implementation supposed to do?? ๐ค One possible approach would be to block until all the semaphore adjustments could be performed. That's undesirable though, since it could force process termination to block for arbitrarily long periods. Another possibility is that such semaphore adjustments could be ignored altogether (somewhat analogously to failing when IPC_NOWAIT is specified for a semaphore operation). Linux adopts a third approach: decreasing the semaphore value as far as possible (i.e., to zero) and allowing process termination to proceed immediately. slay.
In Linux 2.6.x, x <= 10, there's a bug that in some circumstances prevents a thread that is waiting for a semaphore value to become zero from being woken up when the value does actually become zero. This bug is fixed in Linux 2.6.11. ๐
๐ EXAMPLES ๐
okay bestie, let's actually see this in action, because reading about semop() in the abstract is giving "studying for a test you're not taking" vibes. here's the whole point of the syscall: do multiple things to a semaphore set atomically, so nobody can slide in between your operations and ruin your whole life. ๐
so this little code segment uses semop() to atomically wait for the value of semaphore 0 to become zero, and then increment the semaphore value by one. literally one syscall, two ops, zero chances for another process to jump the queue. iconic behavior.
struct sembuf sops[2];
int semid;
/* Code to set semid omitted */
sops[0].sem_num = 0; /* Operate on semaphore 0 */
sops[0].sem_op = 0; /* Wait for value to equal 0 */
sops[0].sem_flg = 0;
sops[1].sem_num = 0; /* Operate on semaphore 0 */
sops[1].sem_op = 1; /* Increment value by one */
sops[1].sem_flg = 0;
if (semop(semid, sops, 2) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
see the way that works?? ๐ซถ we're passing a two-element array of struct sembuf, and the third argument says nope, there's <italic>two</italic> of them, not one. the first entry has sem_op of 0, which is the "wait until it equals zero" move โ the kernel will just park your process there until that's true. the second entry has sem_op of 1, bumping the value up by one. and because it's all one semop() call, no other process can squeeze anything in between the wait and the increment. that's the atomicity, bestie, that's the whole magic trick. โจ
ngl the perror("semop") + exit(EXIT_FAILURE) combo at the end is just chef's kiss error handling โ if semop() returns -1, say something, then dip. ๐ญ
and if you want even more examples (yes, more, because we're thorough around here), a further example of the use of semop() can be found in shmop(2). go read it. it's a whole vibe. ๐
๐ SEE ALSO ๐
the little family of friends you should go meet next, fr fr:
okay that's the whole page, you made it to the end, i'm so proud of you ๐ฅน๐ this was Linux man-pages 6.20, dated 2026-02-08 โ go forth and synchronize your processes responsibly, and remember: always check your return values. no cap. ๐๐ฆ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1993, Giorgio Ciucci <giorgio@crcc.it>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms