Skip to content

Day58 - POSIX Semaphore Synchronization and Shared Memory Queue

Overview

This chapter explores Linux userspace synchronization using:

  • POSIX named semaphore
  • Shared memory
  • Producer / consumer synchronization
  • Ring buffer queue
  • Process-shared pthread mutex

This is one of the most important synchronization architectures in:

  • RTOS
  • Linux middleware
  • IPC systems
  • Network packet queue
  • Embedded Linux daemon

POSIX Semaphore Basics

Named Semaphore APIs

Create / Open

sem_t *sem_open(const char *name,
                int oflag,
                mode_t mode,
                unsigned int value);

Wait

int sem_wait(sem_t *sem);

Non-blocking wait

int sem_trywait(sem_t *sem);

Timeout wait

int sem_timedwait(sem_t *sem,
                  const struct timespec *abs_timeout);

Post

int sem_post(sem_t *sem);

Close

int sem_close(sem_t *sem);

Remove semaphore object

int sem_unlink(const char *name);

Semaphore Worldview

Semaphore is NOT data transport

Semaphore only provides:

  • synchronization
  • resource counting
  • wait/wakeup

Actual data transport still requires:

  • shared memory
  • queue
  • socket
  • pipe

Counting Semaphore

Counting semaphore represents:

available resource amount

Example:

sem_post()
→ resource count +1

sem_wait()
→ resource count -1

Binary Semaphore

Binary semaphore is conceptually:

counting semaphore with max value = 1

It is commonly used for:

  • event notification
  • wakeup signal
  • task synchronization

Mutex vs Semaphore

Mutex

Used for:

critical section protection

Characteristics:

  • ownership exists
  • lock/unlock must match
  • protects shared data consistency

Semaphore

Used for:

resource synchronization

Characteristics:

  • no ownership
  • any thread/process may post
  • represents resource amount

Shared Memory + Semaphore

Architecture:

shared memory
    = data plane

semaphore
    = synchronization plane

Ring Buffer Queue

Shared Ring Buffer Structure

#define SHM_RING_SIZE 8

struct shm_sem_data {
    uint32_t seq;
    char message[128];
};

struct shm_ring_buffer {
    pthread_mutex_t lock;

    uint32_t head;
    uint32_t tail;
    uint32_t count;
    uint32_t next_seq;

    struct shm_sem_data items[SHM_RING_SIZE];
};

Producer / Consumer Synchronization

Synchronization Objects

filled semaphore

Represents:

readable item count

empty semaphore

Represents:

available empty slot count

pthread mutex

Protects:

  • ring metadata
  • head/tail update
  • queue consistency

Producer Flow

wait empty semaphore
↓
lock mutex
↓
write ring buffer
↓
unlock mutex
↓
post filled semaphore

Consumer Flow

wait filled semaphore
↓
lock mutex
↓
read ring buffer
↓
unlock mutex
↓
post empty semaphore

Important Synchronization Concepts

Queue state != Semaphore state

Queue state is managed by:

  • head
  • tail
  • count

Semaphore only tracks:

  • resource availability

Why Two Semaphores?

filled semaphore

Tracks:

how many items can be read

empty semaphore

Tracks:

how many slots can be written

Process-shared pthread mutex

Mutex inside shared memory must use:

PTHREAD_PROCESS_SHARED

Initialization:

pthread_mutexattr_setpshared(
    &attr,
    PTHREAD_PROCESS_SHARED
);

Linux Synchronization Philosophy

Linux separates synchronization primitives clearly:

Primitive Purpose
mutex critical section ownership
semaphore resource counting
eventfd event notification
futex fast userspace locking
condvar condition synchronization

Summary

This chapter implemented a classic bounded producer-consumer queue using:

  • POSIX semaphore
  • shared memory
  • ring buffer
  • pthread mutex

This architecture is widely used in:

  • embedded Linux middleware
  • IPC service
  • RTOS queue system
  • packet processing
  • multimedia buffering