A multi-process simulation of an Italian post office built in C using POSIX IPC (shared memory, semaphores, System V message queues). Supports dynamic user addition, real-time scaling, comprehensive statistics (including CSV export), unit tests, and Docker. The choice of using english as for the documentation is given by person choice, if you find any issues let me know.
- Multi-process architecture with separate executables: director, ticket dispenser, operators, users, and
new_users - Real-time simulation with configurable minute-to-nanosecond scaling
- Dynamic user addition at runtime via the
new_userstool - POSIX IPC integration using shared memory (
/poste_stats,/poste_stations), semaphores, and System V message queues - Comprehensive statistics tracking daily, cumulative, per-service, operator utilization, pause counts, queue times
- Automatic CSV export of final statistics to
./tmp/final_stats*.csv - Unit testing framework for core modules (
test_time.c,test_shm_stats.c) - Docker support for containerized deployment
- Configuration management via files or environment variables
├── configs/
│ ├── config_timeout.conf # Sample timeout-trigger config
│ └── config_explode.conf # Sample explode-trigger config
├── include/
│ ├── config.h # Default parameters & g_config struct
│ ├── poste.h # Shared-memory data structures
│ ├── sharedmem.h # Shared-memory functions wrapper
│ ├── msg_queue.h # Message-Queues functions wrapper
│ ├── direttore.h # Used solely for testing purposes on the direttore.c file
│ ├── erogatore_ticket.h # Holds definitions used in the erogatore_ticket.c file
│ └── comunicazioni.h # IPC communication definitions
├── src/
│ ├── direttore.c # Director process, statistics & CSV export
│ ├── erogatore_ticket.c # Ticket dispenser logic
│ ├── operatore.c # Operator process logic
│ ├── utente.c # User process behavior
│ ├── new_users.c # Runtime user-addition client
│ └── systems/
│ ├── msg_queue.c # System V message-queue wrapper
│ ├── shared_mem.c # POSIX shared-memory helper
│ └── config.c # Configuration loader implementation
├── tests/
│ ├── smoke_test.sh # End-to-end smoke script
│ ├── test_time.c # Unit test for time computations
│ └── test_shm_stats.c # Unit test for shared-memory stats
├── msg/ # Message queue key files
├── tmp/ # CSV output directory (auto-created)
├── bin/ # Compiled executables (auto-created)
├── obj/ # Object files (auto-created)
├── makefile
├── Dockerfile
├── README.md
└── LICENSE
The simulation follows a producer-consumer pattern with multiple concurrent processes:
Director Process
├── spawns → Ticket Generator Process
├── spawns → N Operator Processes
├── spawns → M User Processes
├── manages → Shared Resources (Statistics, Stations)
└── handles → Dynamic User Addition (via message queue)
Process Communication Flow:
- Users request tickets from the ticket generator via message queues
- Users present tickets to available operators at worker stations
- Operators process services and update shared statistics
- Director monitors simulation, reports statistics, handles user addition requests
new_usersclient sends requests to director for adding users at runtime
- GCC compiler with C99 support
- POSIX-compatible system (Linux/Unix)
- Make utility
- System V IPC support (
ipcrm,ipcs)
The project uses strict compilation flags for code quality:
gcc -Wvla -Wall -Wextra -Werror -g -std=c99# Clean build
make clean
make all
# Clean IPCs and start simulation
make run
# Run with specific configurations
make run_explode
make run_timeout# Add 5 new users to running simulation
make add_users N=5
# Directly invoke the client
./bin/new_users --n-new-users 10# Run all tests
make test
# Run specific test modules
make unit
# Manual testing
./bin/test_time
./bin/test_shm_stats# Build container image
docker build -t poste-simulation .
# Run simulation in container
docker run --rm -it poste-simulationPass configuration files to override defaults:
./bin/direttore --config ./configs/config_timeout.confSet via configuration files or environment variables:
- SIM_DURATION: Simulation length in days (default: 5)
- NUM_OPERATORS: Number of operator processes (default: 10)
- NUM_USERS: Number of user processes (default: 5)
- NUM_WORKER_SEATS: Available service stations (default: 15)
- N_NANO_SECS: Time scaling factor - nanoseconds per simulated minute (default: 50,000,000)
- WORKER_SHIFT_OPEN: Opening hour (default: 8 AM)
- WORKER_SHIFT_CLOSE: Closing hour (default: 8 PM)
- EXPLODE_MAX: Max late users before explode termination (default: 30)
- MAX_N_REQUESTS: Max services per user per day (default: 10)
- NOF_PAUSE: Max operator early departures (default: 3)
The simulation implements the 6 required postal services:
| Service | Average Time (minutes) |
|---|---|
| Invio e ritiro pacchi | 10 |
| Invio e ritiro lettere e raccomandate | 8 |
| Prelievi e versamenti Bancoposta | 6 |
| Pagamento bollettini postali | 8 |
| Acquisto prodotti finanziari | 20 |
| Acquisto orologi e braccialetti | 20 |
Note: Service times are randomized within ±50% of the base value for realistic variation.
The director displays comprehensive statistics at the end of each simulated day:
- Daily metrics: served users, failed services, active operators, pauses taken
- Performance metrics: average wait times, service times (general and per-service)
- Operator utilization: ratio of active operators to available worker seats
- Service breakdown: individual statistics for each of the 6 postal services
At simulation end, all statistics are automatically exported to CSV format:
File location: ./tmp/final_stats.csv (or final_stats_1.csv, etc., to avoid overwrites)
CSV contents:
- Simulation Summary: exit mode (timeout/explode), configuration parameters
- Global Statistics: cumulative served/failed users, average wait/service times
- Per-Service Statistics: breakdown for each of the 6 postal services
- Extra Information: late users, total requests, detailed timing data
| Endpoint | Description | Synchronization |
|---|---|---|
/poste_stats |
Global and daily statistics, simulation state | stats_lock semaphore |
/poste_stations |
Worker seat status, operator assignments | stations_lock semaphore |
- Ticket requests: Users ↔ Ticket Generator
- Service processing: Users ↔ Operators
- Dynamic user addition:
new_usersclient ↔ Director
- stats_lock: Atomic statistics updates
- stations_lock: Worker seat management
- open_poste_event: Daily opening synchronization
- close_poste_event: Daily closing synchronization
- day_update_event: New day notifications
IPC Resource Cleanup
# Remove all IPCs if simulation crashes
ipcrm -a
# Check current IPC status
ipcsPermission Errors
# Ensure proper permissions
chmod +x bin/*Build Errors
# Clean rebuild
make clean
make allMessage Queue Issues
# Check if key files exist
ls -la msg/
# Ensure processes start in correct order
# (director should start before new_users client)The system automatically deallocates IPC resources on normal termination. After crashes or abnormal termination, use ipcrm -a to clean up orphaned resources.
Core Requirements:
- ✅ Multi-process architecture with separate executables
- ✅ All 6 required postal services with correct timing
- ✅ Director, operators, users, ticket dispenser processes
- ✅ Shared memory, semaphores, and message queue IPC
- ✅ Daily and cumulative statistics tracking
- ✅ Timeout and explode termination conditions
Advanced Requirements:
- ✅ Multiple service requests per user (N_REQUESTS)
- ✅ Dynamic user addition via
new_usersexecutable - ✅ CSV statistics export functionality
Technical Requirements:
- ✅ No busy waiting (proper semaphore synchronization)
- ✅ Modular code with separate executables
- ✅ Makefile-based build system
- ✅ Maximum concurrency between processes
- ✅ Proper IPC resource cleanup
- ✅ Strict compilation flags (
-Wvla -Wall -Wextra -Werror) - ✅ Multi-processor compatibility
Successfully tested on:
- Virtual Machine: 2 cores, 4GB RAM
- Load: 60+ concurrent users, 10 operators
- Performance: Stable operation with minimal slowdown
The simulation demonstrates excellent scalability and resource management under realistic post office workloads.
This project is licensed under the MIT License - see the LICENSE file for details.
Course: Sistemi Operativi (Operating Systems)
Institution: Università degli Studi di Torino
Language: C with POSIX IPC
Author: Andrea Bertarione (1097211)
A comprehensive simulation demonstrating advanced systems programming concepts including multi-process coordination, IPC mechanisms, shared memory management, and real-time system design.
- Use
make runfor normal development testing - Use
make run_explodeandmake run_timeoutto test termination conditions - Monitor IPC resources with
ipcsduring development - CSV files are timestamped to preserve multiple test runs
- Unit tests validate core time calculation and shared memory functions (I wasn't able to produce more tests in time, but atleast what i did should work)
For detailed implementation specifics, see the source code comments and the original project requirements in the repository.
📜 Original Project Requirements (click to expand)
Si intende simulare il funzionamento di un ufficio postale. A tal fine sono presenti i seguenti processi e risorse.
• Processo direttore Ufficio Posta, che gestisce la simulazione, e mantiene le statistiche su richieste e servizi erogati dall'ufficio. Genera gli sportelli, e gli operatori. • Processo erogatore ticket: eroga ticket per specifici servizi. In particolare, dovranno essere implementate le funzionalità per garantire almeno i servizi elencati in Tab. 1.
Il tempo indicato è da considerarsi il valore medio per erogare il servizio, e dovrà essere usato per generare un tempo casuale di erogazione nell'intorno ±50% del valore indicato.
| servizio | tempario (in minuti) |
|---|---|
| Invio e ritiro pacchi | 10 |
| Invio e ritiro lettere e raccomandate | 8 |
| Prelievi e versamenti Bancoposta | 6 |
| Pagamento bollettini postali | 8 |
| Acquisto prodotti finanziari | 20 |
| Acquisto orologi e braccialetti | 20 |
- L'utente richiede un ticket specifico per uno dei servizi elencati in Tab. 1, attende il proprio turno, riceve la prestazione richiesta e torna a casa.
- Esistono risorse di tipo sportello; ogni sportello è specializzato nel fornire un solo tipo di prestazione, che varia ogni giorno della simulazione (vedi sopra elenco dei possibili servizi). Gli sportelli aprono e chiudono secondo la disponibilità degli operatori.
- NOF_WORKERS processi di tipo operatore: hanno un orario di lavoro, effettuano pause casuali.
- NOF_USERS processi di tipo utente. Il processo utente decide se recarsi all'ufficio postale e sceglie il servizio da richiedere.
Il processo direttore è responsabile dell'avvio della simulazione, della creazione delle risorse di tipo sportello, dei processi operatore e utente, delle statistiche e della terminazione. Si noti bene che il processo direttore non si occupa dell'aggiornamento delle statistiche, ma solo della loro lettura, secondo quanto indicato. All'avvio, il processo direttore:
- crea un solo processo erogatore ticket.
- crea NOF_WORKER_SEATS risorse di tipo sportello.
- crea NOF_WORKERS processi di tipo operatore.
- crea NOF_USERS processi di tipo utente.
Successivamente il direttore avvia la simulazione, che avrà come durata SIM_DURATION giorni, dove ciascun minuto è simulato dal trascorrere di N_NANO_SECS nanosecondi. La simulazione deve cominciare solamente quando tutti i processi erogatore, operatore e utente sono stati creati e hanno terminato la fase di inizializzazione. Alla fine di ogni giornata, il processo direttore dovrà stampare le statistiche totali e quelle della giornata, che comprendono:
- il numero di utenti serviti totali nella simulazione
- il numero di utenti serviti in media al giorno
- il numero di servizi erogati totali nella simulazione
- il numero di servizi non erogati totali nella simulazione
- il numero di servizi erogati in media al giorno
- il numero di servizi non erogati in media al giorno
- il tempo medio di attesa degli utenti nella simulazione
- il tempo medio di attesa degli utenti nella giornata
- il tempo medio di erogazione dei servizi nella simulazione
- il tempo medio di erogazione dei servizi nella giornata
- le statistiche precedenti suddivise per tipologia di servizio
- il numero di operatori attivi durante la giornata;
- il numero di operatori attivi durante la simulazione;
- il numero medio di pause effettuate nella giornata e il totale di pause effettuate durante la simulazione;
- il rapporto fra operatori disponibili e sportelli esistenti, per ogni sportello per ogni giornata.
Su richiesta di un processo utente, il processo erogatore ticket si occupa di erogare il ticket relativo alla prestazione richiesta, secondo quanto indicato in Tabella 1.
Ogni giorno lo sportello è associato a un tipo di servizio dal direttore: ogni giorno ci possono essere più sportelli che offrono lo stesso servizio, oppure ci possono essere dei servizi non offerti da alcuno sportello. Ogni sportello può essere occupato da un singolo operatore; la politica di associazione operatore-sportello è definita dal progettista e deve essere applicata all'inizio di ogni giornata.
All'avvio, ogni processo operatore viene creato in modo che sia in grado di erogare uno dei servizi citati in Sezione 5. Tale mansione resta invariata per tutta la simulazione. All'inizio di ogni giornata lavorativa, l'operatore:
- Compete con gli altri operatori per la ricerca di uno sportello libero tra quelli disponibili nell'ufficio postale che si occupano del servizio che lui è in grado di svolgere
- Se ne trova uno, lo occupa e comincia il proprio lavoro che terminerà alla fine della giornata lavorativa
- Con un massimo di NOF_PAUSE volte per tutta la simulazione, l'operatore può decidere (secondo un criterio scelto dal programmatore) di interrompere il servizio della giornata anticipatamente. In questo caso:
- termina di servire il cliente che stava servendo;
- lascia libero lo sportello occupato;
- aggiorna le statistiche. Il processo operatore che al suo arrivo non trova uno sportello libero:
- resta in attesa che uno sportello si liberi (per una pausa di un altro operatore);
- torna a casa a fine giornata, e si ripresenta regolarmente il giorno dopo.
Ogni processo utente si reca presso l'ufficio postale saltuariamente per richiedere un servizio tra quelli disponibili. Più in dettaglio, ogni giorno ogni processo utente:
- decide se recarsi o meno all'ufficio postale, secondo una probabilità P_SERV differente per ogni utente e scelta singolarmente in fase di creazione dell'utente in un intervallo compreso tra i valori [P_SERV_MIN, P_SERV_MAX].
- In caso affermativo i. Stabilisce il servizio di cui vuole usufruire (secondo un criterio stabilito dall'utente); ii. Stabilisce un orario (secondo un criterio stabilito dall'utente); iii. Si reca all'ufficio postale; iv. Controlla se quel giorno l'ufficio postale può servire richieste per il tipo di servizio scelto; v. Se sì, ottiene un ticket per l'apposito servizio; vi. Attende il proprio turno e l'erogazione del servizio; vii. Torna a casa e attende il giorno successivo.
Se al termine della giornata l'utente si trova ancora in coda, abbandona l'ufficio rinunciando all'erogazione del servizio. Il numero di servizi non erogati è uno dei parametri da monitorare.
La simulazione termina in una delle seguenti circostanze:
- timeout: raggiungimento della durata impostata SIM_DURATION giorni
- explode: numero di utenti in attesa al termine della giornata maggiore del valore EXPLODE_THRESHOLD
Il gruppo di studenti deve produrre configurazioni (file config_timeout.conf e config_explode.conf) in grado di generare la terminazione nei casi sopra descritti. Al termine della simulazione, l'output del programma deve riportare anche la causa di terminazione e le statistiche finali.
In questa versione:
- un processo utente, quando decide di recarsi all'ufficio postale, genera una lista di al massimo N_REQUESTS richieste di servizi di vario tipo (il numero deve essere scelto in modo casuale per ogni utente, per ogni giorno). Quindi si reca all'ufficio postale dove richiederà in sequenza un ticket per ogni servizio nella lista (il ticket per il servizio i potrà essere richiesto solo quando il servizio i − 1 è stato completato).
- attraverso un nuovo eseguibile invocabile da linea di comando, deve essere possibile aggiungere alla simulazione altri N_NEW_USERS processi utente oltre a quelli inizialmente generati dal direttore;
- tutte le statistiche prodotte devono anche essere salvate in un file testo di tipo csv, in modo da poter essere utilizzate per una analisi futura
Tutti i parametri di configurazione sono letti a tempo di esecuzione, da file o da variabili di ambiente. Quindi, un cambiamento dei parametri non deve determinare una nuova compilazione dei sorgenti (non è consentito inserire i parametri uno alla volta da terminale una volta avviata la simulazione).
Il progetto (sia in versione "minimal" che "normal") deve
- evitare l'attesa attiva
- utilizzare almeno memoria condivisa, semafori e un meccanismo di comunicazione fra processi a scelta fra code di messaggi o pipe,
- essere realizzato sfruttando le tecniche di divisione in moduli del codice (per esempio, i vari processi devono essere lanciati da eseguibili diversi con execve(...)),
- essere compilato mediante l'utilizzo dell'utility make
- massimizzare il grado di concorrenza fra processi
- deallocare le risorse IPC che sono state allocate dai processi al termine del gioco
- essere compilato con almeno le seguenti opzioni di compilazione:
gcc -Wvla -Wextra -Werror- poter eseguire correttamente su una macchina (virtuale o fisica) che presenta parallelismo (due o più processori). Per i motivi introdotti a lezione, ricordarsi di definire la macro GNU_SOURCE o compilare il progetto con il flag
-D_GNU_SOURCE