Building the 42-School Minitalk Project: A Guide to UNIX Signal-Based Communication in C

Zakaria Elhajoui
6 min readNov 26, 2023

--

Welcome to this comprehensive guide on building the Minitalk project, a fantastic way from the 42 School curriculum that delves into the intricacies of UNIX signals and inter-process communication in C. This project is not just about coding; it’s an exploration into the depths of system programming, offering a unique opportunity to understand how processes communicate in a UNIX-like environment.

Introduction to Minitalk

Minitalk is a small data exchange program that leverages UNIX signals to facilitate communication between two processes: a client and a server. The core objective is to send a string from the client to the server, which then displays it. This seemingly simple task requires a deep understanding of signal handling, process management, and inter-process communication (IPC) in a UNIX environment.

Understanding the Project Requirements

The Minitalk project consists of two parts: the mandatory part and the bonus part. The mandatory part involves creating a client and a server that can communicate using UNIX signals. The bonus part includes additional features like acknowledgment signals and Unicode character support.

  1. Server: It should be started first and print its PID (Process ID). It waits for messages from the client and prints them upon receipt.
  2. Client: It takes two parameters — the server’s PID and the string to send. The client’s job is to send this string to the server using UNIX signals.

Mandatory Requirements:

  1. Client and Server: Create two executable files named client and server.
  2. Signal Handling: Use only SIGUSR1 and SIGUSR2 for communication.
  3. Error Handling: Your program should not quite unexpectedly and must handle errors gracefully.

Bonus Features (Optional):

  1. Acknowledgment Signal: The server sends a signal back to the client upon message receipt.
  2. Unicode Support: Handle Unicode characters in the communication.

Prerequisites for Developing the Minitalk Project

Before diving into the creation of the Minitalk program, it’s essential to ensure you have the right foundation and tools. This section will guide you through all the prerequisites needed to set the stage for a successful project execution.

1. Process Creation and Management

  • Processes: Both the client and the server are separate processes running on the operating system. A process is an instance of a computer program that is being executed.
  • Process ID (PID): Each process is assigned a unique identifier called a Process ID. The server process displays its PID so that the client process can send signals to it.

Resources:

2. UNIX Signals

  • Nature of Signals: UNIX signals are a form of software interrupt used to indicate to a process that a particular event has occurred.
  • Signal Handling: When a process receives a signal, the operating system interrupts the process’s normal flow of execution to deliver the signal. The process can define signal handlers, which are functions that specify what action should be taken when a specific signal is received.

Resources:

3. Inter-Process Communication (IPC)

  • Sending Signals: The client sends signals (SIGUSR1 and SIGUSR2) to the server. This is done by invoking system calls like kill() in the client’s code, specifying the server’s PID and the signal to send.
  • Receiving Signals: The server has signal handlers set up to catch these signals. When a signal is received, the corresponding handler function is executed.

Resources:

4. Data Encoding and Transmission

  • Encoding Data: The client encodes the string it needs to send into a series of signals. For example, it might encode the string into bits, with SIGUSR1 representing ‘0’ and SIGUSR2 representing ‘1’.
  • Transmission: The client sends these signals one by one to the server. The operating system delivers these signals to the server process.

Resources:

5. Data Reception and Decoding

  • Reception: The server, upon receiving each signal, uses its signal handlers to process them.
  • Decoding: The server decodes the series of signals back into the original string or data format.

6. Synchronization and Concurrency

  • Handling Multiple Clients: The server might receive signals from multiple clients. It needs to manage these concurrently, ensuring accurate and efficient communication.

7. Operating System’s Role

  • Scheduling: The OS schedules these processes, allocating CPU time for their execution.
  • Signal Delivery: The OS is responsible for delivering signals to the correct process.
  • Resource Management: The OS manages the resources that these processes use.

Step-by-Step Development Guide

Step 1: Structuring Your Code

A well-organized project structure is crucial. Here’s a recommended layout:

├── Makefile
├── include
│ └── minitalk.h
├── lib
│ ├── ft_printf
│ └── libft
└── src
├── client
│ └── client.c
└── server
└── server.c

Step 2: Writing the Server

The server’s job is to listen for signals from the client and decode them into a message.

Server Code Structure:

  1. Main Function: Start by coding your server. It should:
  • Print its PID when started.
  • Set up signal handlers for SIGUSR1 and SIGUSR2.
  • Decode the signals to reconstruct the message.
  1. Signal Handling: Use sigaction() to handle signals asynchronously.
  2. Message Decoding: Choose a protocol to encode characters (e.g., binary representation using SIGUSR1 for 0 and SIGUSR2 for 1).
#include "minitalk.h"

int main(void)
{
struct sigaction sa;

ft_printf("Server PID: %d\n", getpid());
sa.sa_sigaction = &ft_handle_signal;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
while (1)
;
return (0);
}
#include "minitalk.h"

void ft_handle_signal(int signum, siginfo_t *info, void *context)
{
static unsigned char character = 0;
static int bit_count = 0;
static pid_t client_pid = 0;

(void)context;
if (client_pid != info->si_pid)
{
bit_count = 0;
character = 0;
}
client_pid = info->si_pid;
character = character << 1;
if (signum == SIGUSR1)
character = character | 1;
bit_count++;
if (bit_count == 8)
{
write(1, &character, 1);
bit_count = 0;
character = 0;
}
kill(client_pid, SIGUSR2);
}

The client sends a string to the server in the form of signals.

Client Code Structure:

  1. Parameter Parsing: Parse the server’s PID and the string to be sent from the command line arguments.
  2. Signal Sending: Convert the string into signals (SIGUSR1 and SIGUSR2) You might encode each character into bits, and send them to the server using the kill() function.
  3. Synchronization: Ensure the client waits appropriately between sending signals to avoid overwhelming the server.
#include "minitalk.h"

int main(int argc, char **argv)
{
pid_t pid;

if (argc != 3)
{
ft_putstr_fd("Usage: ./client [PID] [string]\n", 2);
exit(EXIT_FAILURE);
}
pid = ft_atoi(argv[1]);
if (pid <= 0)
{
ft_putstr_fd("Invalid PID\n", 2);
exit(EXIT_FAILURE);
}
signal(SIGUSR2, ft_resp_handler);
ft_send_string(pid, argv[2]);
return (0);
}
#include "minitalk.h"

int g_confirm_flag = 0;

void ft_resp_handler(int signum)
{
g_confirm_flag = 1;
(void)signum;
}

void ft_send_bit(int pid, int bit)
{
int signal;

if (bit == 1)
signal = SIGUSR1;
else
signal = SIGUSR2;
if (kill(pid, signal) == -1)
{
ft_putstr_fd("Error", 2);
exit(EXIT_FAILURE);
}
while (!g_confirm_flag)
;
g_confirm_flag = 0;
}

void ft_send_char(int pid, unsigned char c)
{
int i;

i = 7;
while (i >= 0)
{
ft_send_bit(pid, (c >> i) & 1);
usleep(400);
i--;
}
}

void ft_send_string(int pid, const char *str)
{
while (*str)
ft_send_char(pid, *str++);
ft_send_char(pid, '\0');
}

Step 4: Makefile

Create a Makefile to automate compilation. It should have rules for compiling the client and server, cleaning up object files, and recompiling everything.

Step 5: Bonus Features (Optional)

Implement features like acknowledgment signals from the server or Unicode character support.

Conclusion

The Minitalk project is an excellent opportunity to delve into the intricacies of inter-process communication using UNIX signals. It’s not just about coding; it’s about understanding how processes communicate at a fundamental level. Take this project step by step, test extensively, and don’t forget to enjoy the process of learning and discovery!

Happy coding, and best of luck with your Minitalk project!

The source code for this project is available here:

--

--