System V IPC#

System V IPC gives two unrelated processes a shared resource the kernel owns: a semaphore set for coordination, or a shared memory segment for passing bytes with no copying through a pipe. Each resource is identified by an integer key and survives until a process explicitly removes it.

The convenience modules IPC::SysV, IPC::Semaphore, and IPC::SharedMem are not available in PetaPerl, so the recipes here use the raw semget / shmget built-in family with the flag values written as plain numbers. Those built-ins are the layer the modules wrap; using them directly costs only a few named constants you supply yourself.

The flag constants you need#

A handful of small integers control how these calls behave. They are the standard Linux values:

use constant {
    IPC_PRIVATE => 0,        # "give me a fresh, private key"
    IPC_CREAT   => 0o1000,   # create the resource if absent
    IPC_EXCL    => 0o2000,   # fail if it already exists
    IPC_RMID    => 0,        # control command: remove the resource
};

The low nine bits of the flag word are ordinary permission bits, the same 0600 / 0644 you would pass to chmod. IPC_CREAT | 0600 means “create it, owner read/write”.

A shared-memory hand-off#

Shared memory is the fastest channel: one process writes bytes into a segment, another reads them straight out, with no kernel copy in between. This program creates a segment, writes a payload, forks, and the child reads the same bytes back:

use constant {
    IPC_PRIVATE => 0,
    IPC_CREAT   => 0o1000,
    IPC_RMID    => 0,
};

my $size = 1024;

my $id = shmget(IPC_PRIVATE, $size, IPC_CREAT | 0600);
defined $id or die "shmget: $!";

my $payload = "shared-payload";
shmwrite($id, $payload, 0, length $payload) or die "shmwrite: $!";

my $kid = fork() // die "fork: $!";
if ($kid == 0) {
    # Child reads the same segment the parent wrote.
    shmread($id, my $buf, 0, length $payload) or die "shmread: $!";
    print "child read: $buf\n";          # child read: shared-payload
    exit 0;
}

waitpid($kid, 0);

# The segment outlives the processes - remove it explicitly.
shmctl($id, IPC_RMID, 0) or warn "shmctl: $!";

Two points decide whether this code leaks:

  • shmread / shmwrite take an explicit length. They do not stop at a NUL or a newline; you tell them exactly how many bytes to move, starting at an offset. Pass length $payload so reader and writer agree on the size.

  • IPC_RMID is not automatic. A shared segment persists in the kernel after every process attached to it has exited. You can see orphaned segments with ipcs -m at the shell. Always pair a shmget with a matching shmctl IPC_RMID.

A semaphore for coordination#

A semaphore set is a small array of counters the kernel guards. The classic use is a lock: a process decrements the counter to enter a critical section and increments it to leave. This recipe creates a one-element set, initialises it, and removes it:

use constant {
    IPC_PRIVATE => 0,
    IPC_CREAT   => 0o1000,
    IPC_RMID    => 0,
    SETVAL      => 16,       # semctl command: set one value
    GETVAL      => 12,       # semctl command: read one value
};

# A set with one semaphore.
my $sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
defined $sem or die "semget: $!";

# Initialise semaphore 0 to the value 1 (one token available).
semctl($sem, 0, SETVAL, 1) or die "semctl SETVAL: $!";

my $value = semctl($sem, 0, GETVAL, 0);
print "semaphore value: $value\n";       # semaphore value: 1

# Remove the set when finished.
semctl($sem, 0, IPC_RMID, 0) or warn "semctl IPC_RMID: $!";

The wait/signal operations themselves go through semop, which takes a packed string of (sem_num, sem_op, sem_flg) triples built with pack. Decrementing by one waits until a token is free; incrementing by one releases it. As with shared memory, a semaphore set the kernel owns must be removed with IPC_RMID or it lingers - visible under ipcs -s.

Next#

Shared memory and semaphores are the heavyweight channels. For the lighter, stream-oriented ones, see Subprocesses and co-processes and TCP sockets.