Architecture Overview
Minitalk implements a classic client-server architecture where:- Server: A persistent process that waits for incoming messages
- Client: A transient process that sends a message and terminates
- Addressing: Uses Process IDs (PIDs) to route signals between processes
Unlike network client-server applications that use IP addresses and ports, Minitalk uses PIDs for addressing. Each running process has a unique PID assigned by the operating system.
Server Implementation
Server Startup and PID Display
The server starts up and immediately displays its PID so clients know where to send messages. Fromserver.c:34-37:
getpid() system call returns the current process’s PID. This is the number clients will use to address this server.
Signal Handler Registration
After displaying its PID, the server registers handlers for incoming signals (server.c:38-39):
- “When SIGUSR1 arrives, call
handle_sigusr” - “When SIGUSR2 arrives, call
handle_sigusr”
Both signals use the same handler because the handler function receives the signal type as a parameter (
int sigsent) and can distinguish between them.The Pause Loop
The heart of the server is an infinite loop withpause() from server.c:40-43:
What does pause() do?
What does pause() do?
pause() suspends the process until a signal is received. This is extremely efficient because:- No CPU usage: The process doesn’t busy-wait or poll
- Instant wake-up: The OS immediately resumes the process when a signal arrives
- Automatic return: After the signal handler finishes,
pause()returns and is called again
Client Implementation
PID Validation
Before sending any signals, the client validates the provided PID to ensure it’s a valid number. Fromclient.c:77-85:
Usage and Argument Validation
The client enforces correct usage fromclient.c:75-76:
Sending the Message
After validation, the client callssend_message() from client.c:86-90:
send_message function handles:
- Converting the PID string to an integer with
ft_atoi() - Iterating through each character in the message
- Sending 8 signals per character (one per bit)
- Error handling if
kill()fails
Communication Flow
Here’s the complete step-by-step flow of a message transmission:Phase 1: Setup
Phase 2: Client Transmission
Phase 3: Error Handling
kill() return -1?
-
Invalid PID: The process doesn’t exist
-
Permission denied: You don’t own the target process
- Invalid signal: Signal number is wrong (shouldn’t happen with SIGUSR1/2)
Error handling flow:This ensures the client doesn’t silently fail if the server isn’t responding.
PID-Based Addressing
How PIDs Work
PID Properties and Limitations
PID Properties and Limitations
Properties:
- Unique: Each running process has a unique PID
- System-wide: PIDs are unique across the entire system
- Temporary: PIDs are reassigned after a process terminates
- Sequential: Usually assigned sequentially (but can wrap around)
- Local only: PIDs only work on the same machine (not across networks)
- No authentication: Any process owned by you can signal any other process you own
- Reusable: If server restarts, it gets a new PID
Multiple Clients
Can multiple clients send to the same server?Yes! Multiple clients can all send signals to the same server PID. However:For production use, you’d need to add:
- No queuing: If two clients send simultaneously, signals may be lost
- No isolation: The server can’t distinguish which client sent which signal
- Message interleaving: Messages from different clients will be mixed together
- Client identification in the protocol
- Message framing (start/end markers)
- Acknowledgment signals back to clients
Real-World Example
Let’s trace a complete session:Terminal 1: Start Server
Terminal 2: Send Message
- Validates
42837is a valid number ✅ - Calls
send_message(42837, "Hi") - Sends 16 signals total (8 for ‘H’, 8 for ‘i’)
- Each signal is separated by 700 microseconds
- Total time: ~16 × 0.7ms = 11.2ms
- Exits successfully
Back to Terminal 1: Server Output
- Was sleeping in
pause() - Received 16 signals
- After every 8 signals, printed one character
- Continues waiting for more messages
Key Architectural Decisions
Server is Persistent
Runs continuously, handling multiple messages from multiple clients over its lifetime
Client is Transient
Starts, sends one message, and terminates immediately
Unidirectional
Communication flows only from client to server (no acknowledgments in basic version)
PID-Based
Uses process IDs for addressing instead of network addresses
Summary
The Minitalk architecture demonstrates:- Server initialization: Get PID, register handlers, enter pause loop
- Client addressing: Validate and use PID to target the server
- Signal-based IPC: Use
kill()to send, signal handlers to receive - Efficient waiting:
pause()provides zero-CPU waiting - Error handling: Check
kill()return values for transmission errors
- Process identification (PIDs)
- Signal handling
- Inter-process communication
- Asynchronous event handling