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