IPC Keys — IPC_PRIVATE & ftok()

 

IPC Keys — IPC_PRIVATE & ftok()
Chapter 45, Section 45.2 | How to Choose a Unique Key for IPC Objects
3
Key Methods
ftok()
File to Key
IPC_PRIVATE
Always unique
Key Terms
key_t IPC_PRIVATE ftok() i-node number proj value Key collision IPC_CREAT Unique identifier

Why Do We Need Keys?

When two unrelated processes want to share an IPC object (like a message queue), they need a way to agree on which object to use. They cannot pass an identifier directly (there’s no common channel yet). So they agree on a key beforehand — like agreeing on a door number before meeting at a building.

A key is an integer of type key_t. Both processes call the same get() function with the same key, and the kernel returns the same identifier to both.

The Problem: If you just pick any random integer as a key, you might accidentally pick the same key as another application on the system — and end up accessing their IPC object instead of creating your own.

Three Ways to Choose a Key
Three Key Generation Methods
Method 1
Random integer in header file
Simple but risky. You might pick the same value as another application. ❌ Not recommended.
Method 2
IPC_PRIVATE as key
Always creates a unique new object. Useful for parent-child processes. ✅ Recommended for related processes.
Method 3
ftok() function
Generates a likely-unique key from a file path + project number. ✅ Recommended for unrelated processes.

Method 2: IPC_PRIVATE — Guaranteed Unique New Object

When you specify IPC_PRIVATE as the key to a get call, the kernel always creates a brand new IPC object with a unique identifier. You don’t need IPC_CREAT or IPC_EXCL — they are implied.

The main use case for IPC_PRIVATE is in parent-child process communication: the parent creates the IPC object before calling fork(), and the child inherits the identifier (since it’s just an integer in the process’s data).

For unrelated processes: IPC_PRIVATE also works, but you need a side channel (like writing the identifier to a file) to communicate the identifier to other processes.

Coding Example 1 — IPC_PRIVATE with Parent-Child

Classic use: parent creates the IPC object before fork(), child inherits the ID.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>

/* Simple message structure */
struct mymsg {
    long mtype;          /* must be > 0 */
    char mtext[100];
};

int main(void) {
    int msqid;
    struct mymsg msg;
    pid_t pid;

    /* IPC_PRIVATE always creates a new unique queue */
    msqid = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR);
    if (msqid == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }
    printf("[Parent] Created queue with ID = %d\n", msqid);

    pid = fork();
    if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); }

    if (pid == 0) {
        /* CHILD: sends a message */
        msg.mtype = 1;
        snprintf(msg.mtext, sizeof(msg.mtext), "Hello from child PID=%d", getpid());
        if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) {
            perror("msgsnd");
            exit(EXIT_FAILURE);
        }
        printf("[Child]  Sent: %s\n", msg.mtext);
        exit(EXIT_SUCCESS);
    } else {
        /* PARENT: waits for child, then receives message */
        wait(NULL);
        if (msgrcv(msqid, &msg, sizeof(msg.mtext), 0, 0) == -1) {
            perror("msgrcv");
            exit(EXIT_FAILURE);
        }
        printf("[Parent] Received: %s\n", msg.mtext);

        /* Clean up */
        if (msgctl(msqid, IPC_RMID, NULL) == -1) {
            perror("msgctl IPC_RMID");
        }
        printf("[Parent] Queue deleted.\n");
    }

    return 0;
}
Compile & Run: gcc -o ipc_private ipc_private.c && ./ipc_private
The queue identifier is automatically shared between parent and child through the fork() — no key coordination needed.

Method 3: ftok() — File to Key

ftok() generates a key by combining information from an existing file with a project number you provide. The function signature is:

#include <sys/ipc.h>

key_t ftok(char *pathname, int proj);
/* Returns an integer key on success, or -1 on error */

How does it generate the key? On Linux, ftok() combines three pieces of information:

How ftok() Builds a 32-bit Key on Linux
bits 31–24
Least significant 8 bits of proj
+
bits 23–16
Least significant 8 bits of minor device number
+
bits 15–0
Least significant 16 bits of i-node number
=
result
32-bit key_t
Device + i-node info is obtained by calling stat() on the pathname internally.
Important rules for ftok():

  • Only the least significant 8 bits of proj are used. Avoid using 0 as proj — it is not portable.
  • The pathname must exist and be accessible by stat() — otherwise ftok() returns -1.
  • ftok() uses the i-node number, not the filename. So two different names pointing to the same file (hard links) give the same key.
  • Do NOT delete and re-create the file during the application’s life — the new file will have a different i-node and produce a different key.

Coding Example 2 — Using ftok() to Generate a Key
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

#define KEY_FILE   "/tmp/myapp_ipc_keyfile"   /* agreed-upon file */
#define PROJ_ID    'A'                          /* agreed-upon project char */

int main(void) {
    key_t key;
    int msqid;

    /* Step 1: Generate the key from the file + project ID */
    key = ftok(KEY_FILE, PROJ_ID);
    if (key == -1) {
        perror("ftok");
        fprintf(stderr, "Make sure %s exists!\n", KEY_FILE);
        exit(EXIT_FAILURE);
    }
    printf("Generated key = 0x%x (%d)\n", (unsigned int)key, key);

    /* Step 2: Use the key to create or open the message queue */
    msqid = msgget(key, IPC_CREAT | 0660);
    if (msqid == -1) {
        perror("msgget");
        exit(EXIT_FAILURE);
    }
    printf("Message queue ID = %d\n", msqid);

    /* Any other process that calls ftok(KEY_FILE, 'A')
       will get the same key, and thus the same msqid */

    /* Cleanup */
    msgctl(msqid, IPC_RMID, NULL);
    printf("Queue removed.\n");

    return 0;
}
Before running: touch /tmp/myapp_ipc_keyfile
Both server and client call ftok("/tmp/myapp_ipc_keyfile", 'A') — they get the same key and the same message queue ID.

Coding Example 3 — Multiple Keys from the Same File

An application might need several IPC objects of the same type (e.g., three semaphore sets). Use different proj values with the same file to generate distinct keys:

#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>

#define KEY_FILE "/tmp/myapp_keyfile"

int main(void) {
    key_t key1, key2, key3;
    int semid1, semid2, semid3;

    /* Three different proj values → three different keys */
    key1 = ftok(KEY_FILE, 'A');
    key2 = ftok(KEY_FILE, 'B');
    key3 = ftok(KEY_FILE, 'C');

    if (key1 == -1 || key2 == -1 || key3 == -1) {
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    printf("key1 = 0x%x\n", (unsigned int)key1);
    printf("key2 = 0x%x\n", (unsigned int)key2);
    printf("key3 = 0x%x\n", (unsigned int)key3);

    /* Create three separate semaphore sets */
    semid1 = semget(key1, 1, IPC_CREAT | 0600);
    semid2 = semget(key2, 1, IPC_CREAT | 0600);
    semid3 = semget(key3, 1, IPC_CREAT | 0600);

    if (semid1 == -1 || semid2 == -1 || semid3 == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    printf("Semaphore IDs: %d, %d, %d\n", semid1, semid2, semid3);

    /* Cleanup */
    semctl(semid1, 0, IPC_RMID); 
    semctl(semid2, 0, IPC_RMID); 
    semctl(semid3, 0, IPC_RMID);
    printf("All semaphores removed.\n");

    return 0;
}
Why proj values exist: A single application often needs multiple IPC objects of the same type. The proj argument (choose values 1–255, not 0) lets you derive multiple distinct keys from a single agreed-upon file.

Limitations of ftok()

ftok() is widely used but has a small limitation: there is a very small chance of key collisions. Two different files on different filesystems could yield the same key if:

  • The least significant bits of their i-node numbers happen to match, AND
  • The minor device numbers of their respective filesystems happen to match

In practice, this possibility is small enough that ftok() is a viable technique for most applications. The glibc implementation of ftok() has the same limitation as other UNIX implementations.

IPC_PRIVATE vs ftok() — When to Use Which
✅ Use IPC_PRIVATE when:
  • Parent and child processes communicate
  • You fork() before creating the IPC object
  • You can pass the ID through inheritance or a file
✅ Use ftok() when:
  • Completely unrelated processes need to share an IPC object
  • Both processes know a common pathname and proj value
  • You want a stable key that survives restarts

🎯 Interview Questions — IPC Keys, IPC_PRIVATE, ftok()

Q1. What data type is used to represent a System V IPC key?
A: key_t, which is an integer type defined in <sys/ipc.h>. On Linux it is a 32-bit value.
Q2. What happens when you use IPC_PRIVATE as the key to msgget()?
A: The kernel always creates a brand-new message queue with a guaranteed unique identifier, regardless of any existing queues. IPC_CREAT and IPC_EXCL flags are not needed.
Q3. What are the arguments to ftok() and what does each represent?
A: ftok(char *pathname, int proj). pathname is the path to an existing file whose i-node number is used. proj is a project number (only least significant 8 bits used) that allows generating multiple different keys from the same file. Returns a key_t.
Q4. Does ftok() use the filename or the i-node number to generate the key?
A: The i-node number. This means two different pathnames (hard links) pointing to the same file with the same proj value will return the same key. It also means you should not delete and re-create the file, as the new file will likely have a different i-node number and thus a different key.
Q5. Why should you never use 0 as the proj argument to ftok()?
A: SUSv3 leaves the behavior unspecified when proj is 0. On some UNIX implementations (like AIX 5.1), ftok() returns -1 for proj=0. For portability, use values 1–255.
Q6. How can you generate multiple distinct keys for an application that needs several IPC objects?
A: Call ftok() multiple times with the same pathname but different proj values (e.g., ‘A’, ‘B’, ‘C’). Each call produces a distinct key, allowing you to create separate IPC objects.
Q7. What is the risk of using a hardcoded random integer as an IPC key?
A: Another application on the same system might be using the same key value. Your get call would then open the other application’s IPC object instead of creating your own, potentially causing data corruption or security issues.
Q8. In a client-server application using IPC_PRIVATE, how does the client find out the IPC object’s identifier?
A: The server writes the identifier to a well-known file (e.g., a PID file or config file), and clients read it from there. Alternatively, the server could embed the ID in a response to the client’s initial connection.

Leave a Reply

Your email address will not be published. Required fields are marked *