semctl(2)

semctl(2) ๐ŸŽ€ โ€” System Calls Manual ๐ŸŽ€

okay so bestie, semctl(2) has entered the chat, and it is highkey the control room for System V semaphores. No cap, this is the System Calls Manual energy: you've got a semaphore set, you've got operations, and it's giving main character vibes. ๐Ÿ’…

๐Ÿ’– NAME ๐Ÿ’–

semctl โ€” System V semaphore control operations. Periodt. โœจ

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc). Slay. ๐Ÿซถ

โœจ SYNOPSIS โœจ

#include <sys/sem.h>

int semctl(int semid, int semnum, int op, ...);

๐ŸŒธ DESCRIPTION ๐ŸŒธ

okay so semctl() performs the control operation specified by op on the System V semaphore set identified by semid, or on the semnum-th semaphore of that set. (The semaphores in a set are numbered starting at 0.) ๐ŸŽ€

This function has three or four arguments, depending on op. When there are four, the fourth has the type union semun. The calling program must define this union as follows โ€” yes, you, bestie, you define it:

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

The semid_ds data structure is defined in <sys/sem.h> as follows:

struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions */
    time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Creation time/time of last
                                  modification via semctl() */
    unsigned long   sem_nsems; /* # of semaphores in set */
};

The fields of the semid_ds structure are as follows:

  • sem_perm โ€” This is an ipc_perm structure (see below) that specifies the access permissions on the semaphore set. ๐Ÿ‘‘
  • sem_otime โ€” Time of last semop(2) system call. โฐ
  • sem_ctime โ€” Time of creation of semaphore set or time of last semctl() IPCSET, SETVAL, or SETALL operation. ๐Ÿ•ฐ๏ธ
  • sem_nsems โ€” Number of semaphores in the set. Each semaphore of the set is referenced by a nonnegative integer ranging from 0 to sem_nsems-1. ๐Ÿ”ข

The ipc_perm structure is defined as follows โ€” the fields settable using IPC_SET are the ones getting the main-character treatment:

struct ipc_perm {
    key_t          __key; /* Key supplied to semget(2) */
    uid_t          uid;   /* Effective UID of owner */
    gid_t          gid;   /* Effective GID of owner */
    uid_t          cuid;  /* Effective UID of creator */
    gid_t          cgid;  /* Effective GID of creator */
    unsigned short mode;  /* Permissions */
    unsigned short __seq; /* Sequence number */
};

The least significant 9 bits of the mode field of the ipc_perm structure define the access permissions for the shared memory segment. The permission bits are as follows:

0400   Read by user
0200   Write by user
0040   Read by group
0020   Write by group
0004   Read by others
0002   Write by others

In effect, "write" means "alter" for a semaphore set. Bits 0100, 0010, and 0001 (the execute bits) are unused by the system. ๐Ÿฆ‹

Valid values for op are literally these, bestie:

  • IPC_STAT โ€” Copy information from the kernel data structure associated with semid into the semid_ds structure pointed to by arg.buf. The argument semnum is ignored. The calling process must have read permission on the semaphore set. ๐Ÿ‘‘
  • IPC_SET โ€” Write the values of some members of the semid_ds structure pointed to by arg.buf to the kernel data structure associated with this semaphore set, updating also its sem_ctime member.

The following members of the structure are updated: sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) sem_perm.mode.

The effective UID of the calling process must match the owner (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, or the caller must be privileged. The argument semnum is ignored. ๐Ÿ’…

  • IPC_RMID โ€” Immediately remove the semaphore set, awakening all processes blocked in semop(2) calls on the set (with an error return and errno set to EIDRM). The effective user ID of the calling process must match the creator or owner of the semaphore set, or the caller must be privileged. The argument semnum is ignored. ๐Ÿ—‘๏ธ
  • IPC_INFO (Linux-specific) โ€” Return information about system-wide semaphore limits and parameters in the structure pointed to by arg.__buf. This structure is of type seminfo, defined in <sys/sem.h> if the _GNU_SOURCE feature test macro is defined:
struct seminfo {
    int semmap;  /* Number of entries in semaphore
                    map; unused within kernel */
    int semmni;  /* Maximum number of semaphore sets */
    int semmns;  /* Maximum number of semaphores in all
                    semaphore sets */
    int semmnu;  /* System-wide maximum number of undo
                    structures; unused within kernel */
    int semmsl;  /* Maximum number of semaphores in a
                    set */
    int semopm;  /* Maximum number of operations for
                    semop(2) */
    int semume;  /* Maximum number of undo entries per
                    process; unused within kernel */
    int semusz;  /* Size of struct sem_undo */
    int semvmx;  /* Maximum semaphore value */
    int semaem;  /* Max. value that can be recorded for
                    semaphore adjustment (SEM_UNDO) */
};

The semmsl, semmns, semopm, and semmni settings can be changed via /proc/sys/kernel/sem; see proc(5) for details. ๐ŸŒ€

  • SEM_INFO (Linux-specific) โ€” Return a seminfo structure containing the same information as for IPC_INFO, except that the following fields are returned with information about system resources consumed by semaphores: the semusz field returns the number of semaphore sets that currently exist on the system; and the semaem field returns the total number of semaphores in all semaphore sets on the system. ๐ŸŒˆ
  • SEM_STAT (Linux-specific) โ€” Return a semid_ds structure as for IPC_STAT. However, the semid argument is not a semaphore identifier, but instead an index into the kernel's internal array that maintains information about all semaphore sets on the system. ๐Ÿ”ฅ
  • SEM_STAT_ANY (Linux-specific, since Linux 4.17) โ€” Return a semid_ds structure as for SEM_STAT. However, sem_perm.mode is not checked for read access for semid meaning that any user can employ this operation (just as any user may read /proc/sysvipc/sem to obtain the same information). ๐Ÿ‘‘
  • GETALL โ€” Return semval (i.e., the current value) for all semaphores of the set into arg.array. The argument semnum is ignored. The calling process must have read permission on the semaphore set. ๐Ÿ’–
  • GETNCNT โ€” Return the semncnt value for the semnum-th semaphore of the set (i.e., the number of processes waiting for the semaphore's value to increase). The calling process must have read permission on the semaphore set. ๐Ÿงš
  • GETPID โ€” Return the sempid value for the semnum-th semaphore of the set. This is the PID of the process that last performed an operation on that semaphore (but see VERSIONS). The calling process must have read permission on the semaphore set. ๐Ÿ“
  • GETVAL โ€” Return semval (i.e., the semaphore value) for the semnum-th semaphore of the set. The calling process must have read permission on the semaphore set. โญ
  • GETZCNT โ€” Return the semzcnt value for the semnum-th semaphore of the set (i.e., the number of processes waiting for the semaphore value to become 0). The calling process must have read permission on the semaphore set. ๐Ÿ’œ
  • SETALL โ€” Set the semval values for all semaphores of the set using arg.array, updating also the sem_ctime member of the semid_ds structure associated with the set. Undo entries (see semop(2)) are cleared for altered semaphores in all processes. If the changes to semaphore values would permit blocked semop(2) calls in other processes to proceed, then those processes are woken up. The argument semnum is ignored. The calling process must have alter (write) permission on the semaphore set. ๐Ÿ”ฅ
  • SETVAL โ€” Set the semaphore value (semval) to arg.val for the semnum-th semaphore of the set, updating also the sem_ctime member of the semid_ds structure associated with the set. Undo entries are cleared for altered semaphores in all processes. If the changes to semaphore values would permit blocked semop(2) calls in other processes to proceed, then those processes are woken up. The calling process must have alter permission on the semaphore set. ๐Ÿ’…

๐ŸŽ€ RETURN VALUE ๐ŸŽ€

On success, semctl() returns a nonnegative value depending on op as follows:

  • GETNCNT โ€” the value of semncnt. ๐Ÿงš
  • GETPID โ€” the value of sempid. ๐Ÿ“
  • GETVAL โ€” the value of semval. โญ
  • GETZCNT โ€” the value of semzcnt. ๐Ÿ’œ
  • IPC_INFO โ€” the index of the highest used entry in the kernel's internal array recording information about all semaphore sets. (This information can be used with repeated SEM_STAT or SEM_STAT_ANY operations to obtain information about all semaphore sets on the system.) ๐ŸŒ€
  • SEM_INFO โ€” as for IPC_INFO. ๐ŸŒˆ
  • SEM_STAT โ€” the identifier of the semaphore set whose index was given in semid. ๐Ÿ”ฅ
  • SEM_STAT_ANY โ€” as for SEM_STAT. ๐Ÿ‘‘

All other op values return 0 on success. โœจ

On failure, semctl() returns -1 and sets errno to indicate the error. ๐Ÿ˜ญ

๐Ÿ’€ ERRORS ๐Ÿ’€

okay so when semctl() is in its flop era, it sets errno and here's the entire receipts list, no gatekeeping:

  • EACCES โ€” the op you passed is one of GETALL, GETPID, GETVAL, GETNCNT, GETZCNT, IPC_STAT, SEM_STAT, SEM_STAT_ANY, SETALL, or SETVAL, and your calling process does not have the required permissions on the semaphore set, and also doesn't have the CAP_IPC_OWNER capability in the user namespace that governs its IPC namespace. like bestie, you're literally not on the list ๐Ÿ’…
  • EFAULT โ€” the address that arg.buf or arg.array is pointing at isn't accessible. it's giving "pointer to nowhere" ๐Ÿซ 
  • EIDRM โ€” the semaphore set was removed. gone, deleted, no longer with us ๐Ÿ˜ญ
  • EINVAL โ€” invalid value for op or semid. OR: for a SEM_STAT operation, the index value you put in semid pointed at an array slot that's currently unused. empty seat at the table fr.
  • EPERM โ€” the op is IPC_SET or IPC_RMID, but the effective user ID of the calling process isn't the creator (that's sem_perm.cuid) or the owner (that's sem_perm.uid) of the semaphore set, and the process doesn't have the CAP_SYS_ADMIN capability. not your semaphore, not your call, periodt ๐Ÿ™…โ€โ™€๏ธ
  • ERANGE โ€” the op is SETALL or SETVAL, and the value you're trying to set semval to (for some semaphore in the set) is less than 0 or greater than the implementation limit SEMVMX. stay in your lane, numbers ๐Ÿšซ

๐ŸŒˆ VERSIONS ๐ŸŒˆ

okay so tea time, history edition โ˜•:

POSIX.1 says the sem_nsems field of the semid_ds structure has the type unsigned short, and that's how it's defined on most other systems. it was also defined that way on Linux 2.2 and earlier โ€” but since Linux 2.4, that field has the type unsigned long. glow-up, honestly โœจ

๐Ÿฆ‹ The sempid value ๐Ÿฆ‹

POSIX.1 defines sempid as the "process ID of [the] last operation" on a semaphore, and it explicitly says this value gets set by a successful semop(2) call โ€” with the implication that literally no other interface touches the sempid value. like, hands off, that's semop(2)'s job ๐Ÿ”’

and while some implementations do conform to the behavior POSIX.1 specifies, others simply do not. (the fault here probably lies with POSIX.1 itself, inasmuch as it likely failed to capture the full range of existing implementation behaviors. not to be messy but ๐Ÿ‘€)

various other implementations also update sempid for the other operations that update the value of a semaphore: the SETVAL and SETALL operations, plus the semaphore adjustments performed on process termination as a consequence of the use of the SEM_UNDO flag (peep semop(2) for that one).

Linux also updates sempid for SETVAL operations and semaphore adjustments. BUT โ€” and this is the inconsistent part, highkey iconic behavior โ€” up to and including Linux 4.5, the kernel did not update sempid for SETALL operations. that got rectified in Linux 4.6. character development fr ๐ŸŽ€


๐Ÿ“ STANDARDS ๐Ÿ“

POSIX.1-2024. certified canon, no notes ๐Ÿ‘‘


๐Ÿ“œ HISTORY ๐Ÿ“œ

POSIX.1-2001, SVr4. the origin story ๐Ÿซถ

various fields in a struct semid_ds were typed as short under Linux 2.2 and have become long under Linux 2.4. to take advantage of this, a recompilation under glibc-2.1.91 or later should suffice. (the kernel distinguishes old and new calls by an IPC_64 flag in op.) so basically, just rebuild and you're slaying ๐Ÿ”จ

in some earlier versions of glibc, the semun union was defined in <sys/sem.h>, but POSIX.1 requires that the caller define this union. so on versions of glibc where this union is not defined, the macro _SEM_SEMUN_UNDEFINED is defined in <sys/sem.h> โ€” your little heads-up that "bestie you have to write it yourself" ๐Ÿ’…


๐Ÿ“ NOTES ๐Ÿ“

the IPC_INFO, SEM_STAT, and SEM_INFO operations are used by the ipcs(1) program to provide information on allocated resources. and in the future these may be modified or moved to a /proc filesystem interface. so like... watch this space, ig ๐Ÿ‘€

also, this system limit on semaphore sets affects a semctl() call:

  • SEMVMX โ€” maximum value for semval: implementation dependent (32767).

and for greater portability, it's best to always call semctl() with four arguments. four is the magic number, no cap ๐Ÿ“

real talk: if your code is out here calling semctl() with three arguments and then getting confused, this is your sign. pass all four. future you says thank you ๐Ÿ™ˆ

โœจ EXAMPLES โœจ

see shmop(2).

(yeah, that's it. go read shmop(2), it ate ๐Ÿฝ๏ธ)


๐Ÿ”— SEE ALSO ๐Ÿ”—

ipc(2), semget(2), semop(2), capabilities(7), sem_overview(7), sysvipc(7)


and that's a wrap on semctl(2), bestie!! ๐Ÿ’– this little masterpiece comes from Linux man-pages 6.20, dated 2026-02-08 โ€” so if you're reading this in some future era, hi, hope the semaphores are behaving ๐Ÿฆ‹

now go forth and manipulate those semaphore sets responsibly, and remember: check your return values, always pass four arguments, and never trust sempid without reading the VERSIONS section first ๐Ÿ˜Œ๐Ÿ’…โœจ

๐Ÿ“ฆ 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
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms