= 32,768
Max IPC objects
Invalid ID errors
Why Does the Identifier Algorithm Matter?
When a server restarts and recreates an IPC object with the same key, why does the new object get a different identifier? This is not a coincidence — the kernel uses a deliberate algorithm designed to make identifier reuse very unlikely in the short term.
Understanding this algorithm helps you understand: how identifiers are generated, why stale identifiers are detectable, how to quickly find an object from its identifier, and the limits on how many IPC objects can exist simultaneously.
For each IPC mechanism, the kernel maintains an ipc_ids structure. This is a global structure (one per mechanism) that keeps track of all instances of that mechanism.
seq = global counter incremented each time any object is created. entries[i] = pointer to semid_ds (or NULL if slot is free). max_id = highest currently-used index. in_use = count of active objects.When you call msgget(), semget(), or shmget(), here’s what the kernel does:
Scan the entries[] array for a structure whose
__key matches the provided key.Allocate a new data structure, find a free slot in entries[], then:
2a. Copy the key into
xxx_perm.__key2b. Copy current ipc_ids.seq into
xxx_perm.__seq, then increment ipc_ids.seqidentifier = index + xxx_perm.__seq * SEQ_MULTIPLIERWhere:
index= position of this object’s data structure in the entries[] arrayxxx_perm.__seq= sequence number saved at creation time (from ipc_ids.seq)SEQ_MULTIPLIER= 32,768 (defined as IPCMNI in the Linux kernel)
key = 0x4b079002
seq = 5
identifier = 2 + (5 × 32768)
= 2 + 163840
= 163,842
Object at entries[1]:
key = 0x4d0731db
seq = 9
identifier = 1 + (9 × 32768)
= 1 + 294912
= 294,913
index = identifier % SEQ_MULTIPLIERThis is used by the kernel to quickly look up the data structure for a given identifier.
When a new IPC object is created (even with the same key), it gets a different __seq value — because ipc_ids.seq is incremented for every object creation of that type. So the formula produces a different result.
The only way two objects at the same index could get the same identifier: 65,535 objects must be created in between (because seq wraps to 0 after INT_MAX / IPCMNI = 65,535). In practice, this almost never happens.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#define SEQ_MULTIPLIER 32768 /* IPCMNI from kernel source */
int main(void) {
int id1, id2, id3;
int index1, index2, index3;
int seq1, seq2, seq3;
/* Create three semaphore sets with unique keys */
id1 = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
id2 = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
id3 = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
if (id1 == -1 || id2 == -1 || id3 == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
printf("IDs: id1=%d id2=%d id3=%d\n", id1, id2, id3);
/* Reverse-engineer the index and sequence from the identifier */
index1 = id1 % SEQ_MULTIPLIER;
seq1 = id1 / SEQ_MULTIPLIER;
index2 = id2 % SEQ_MULTIPLIER;
seq2 = id2 / SEQ_MULTIPLIER;
index3 = id3 % SEQ_MULTIPLIER;
seq3 = id3 / SEQ_MULTIPLIER;
printf("\nDerived from formula (id = index + seq * 32768):\n");
printf("id1=%d → index=%d, seq=%d (verify: %d + %d*32768 = %d)\n",
id1, index1, seq1, index1, seq1, index1 + seq1 * SEQ_MULTIPLIER);
printf("id2=%d → index=%d, seq=%d (verify: %d + %d*32768 = %d)\n",
id2, index2, seq2, index2, seq2, index2 + seq2 * SEQ_MULTIPLIER);
printf("id3=%d → index=%d, seq=%d (verify: %d + %d*32768 = %d)\n",
id3, index3, seq3, index3, seq3, index3 + seq3 * SEQ_MULTIPLIER);
/* Cleanup */
semctl(id1, 0, IPC_RMID);
semctl(id2, 0, IPC_RMID);
semctl(id3, 0, IPC_RMID);
return 0;
}
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
int id, fake_id;
struct sembuf op = { .sem_num=0, .sem_op=1, .sem_flg=0 };
/* Create a semaphore */
id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
if (id == -1) { perror("semget"); exit(EXIT_FAILURE); }
printf("Created semaphore ID = %d\n", id);
/* Case 1: Use a completely invalid (never-existed) identifier */
fake_id = 999999999;
op.sem_num = 0; op.sem_op = 1;
if (semop(fake_id, &op, 1) == -1) {
if (errno == EINVAL)
printf("EINVAL: ID %d has no corresponding entry (never existed)\n", fake_id);
else
perror("semop");
}
/* Case 2: Delete the semaphore, then try to use the old ID */
semctl(id, 0, IPC_RMID);
printf("Semaphore %d deleted.\n", id);
if (semop(id, &op, 1) == -1) {
if (errno == EINVAL)
printf("EINVAL: slot for ID %d is now empty\n", id);
else if (errno == EIDRM)
printf("EIDRM: ID %d was removed (slot reused with different seq)\n", id);
else
perror("semop");
}
return 0;
}
EINVAL— the entries[] slot is completely empty (the index portion of the ID points to an unused slot)EIDRM— the slot is occupied, but the sequence number doesn’t match the ID (old object was deleted and slot reused for a different object)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
/* Read current system limits for semaphores */
int main(void) {
FILE *f;
char line[256];
printf("=== System V IPC Limits ===\n\n");
/* SEMMNI: max number of semaphore identifiers (sets) */
f = fopen("/proc/sys/kernel/sem", "r");
if (f) {
int semmsl, semmns, semopm, semmni;
if (fscanf(f, "%d %d %d %d", &semmsl, &semmns, &semopm, &semmni) == 4) {
printf("SEMMSL (max semaphores per set): %d\n", semmsl);
printf("SEMMNS (max semaphores system-wide): %d\n", semmns);
printf("SEMOPM (max ops per semop() call): %d\n", semopm);
printf("SEMMNI (max semaphore sets): %d\n", semmni);
}
fclose(f);
}
/* MSGMNI: max message queue identifiers */
f = fopen("/proc/sys/kernel/msgmni", "r");
if (f) {
int v; fscanf(f, "%d", &v);
printf("MSGMNI (max message queues): %d\n", v);
fclose(f);
}
/* SHMMNI: max shared memory identifiers */
f = fopen("/proc/sys/kernel/shmmni", "r");
if (f) {
int v; fscanf(f, "%d", &v);
printf("SHMMNI (max shared memory segments): %d\n", v);
fclose(f);
}
printf("\nSEQ_MULTIPLIER (IPCMNI): 32768\n");
printf("seq wraps at: INT_MAX / IPCMNI = 2147483647 / 32768 = 65535\n");
printf("Identifier reuse: possible after 65535 creations at same slot\n");
return 0;
}
🎯 Interview Questions — Identifier Algorithm
identifier = index + (xxx_perm.__seq × SEQ_MULTIPLIER), where index is the position in the entries[] array, __seq is the sequence number saved at creation time, and SEQ_MULTIPLIER is 32,768 (IPCMNI).index = identifier % SEQ_MULTIPLIER. This reverse formula is used by the kernel to quickly look up the associated data structure for a given identifier.