Skip to content

Latest commit

 

History

History
2101 lines (1614 loc) · 43.7 KB

slides.md

File metadata and controls

2101 lines (1614 loc) · 43.7 KB
theme font class highlighter colorSchema info transition title author mdc
dracula
sans mono serif
Fira Code
Fira Code
Fira Code
text-center
shiki
dark
minitalk :A minimalistic implementation of a small data exchange program using UNIX signals. 
slide-left
minitalk
passunca
true

minitalk

A minimalistic implementation of a small data exchange program using UNIX signals 

42 logo


layout: two-cols class: '-top--15' transition: slide-up title: Hello Whirl

Hello Whirl!

I am Pedro Ribeiro

42 Student && Musician



Rank : 2

Grade : Learner



42 Intra : @passunca

Github : @PedroZappa

::right::

Pedro Ribeiro Photo


layout: section class: '-left--5' title: Project Requirements

1.

Project Requirements


layout: full

minitalk

A minimalistic implementation of a small data exchange program using UNIX signals.


Server

Receives a message.

Client

Sends a message.

<style> h1 { font-size: 3.5rem; padding-bottom: 2rem; } h2 { font-size: 2.5rem; padding-bottom: 1rem; } .note { @apply border-2 border-green rounded-3xl p-5 w-80 ml-15 } </style>

layout: full class: '-top--5' transition: slide-up title: Server Features

Server Features


  • When executed, prints its PID;
  • Handles incoming SIGUSR1 & SIGUSR2 signals;
  • Receives a message bit-by-bit:
  • Acknowledges receiving a bit by sending a signal back to the client;
  • Prints the message once it has been fully received;
  • Handles multiple clients in a row.
  • <style> li { padding-bottom: 1rem; } </style>

    layout: full class: '-top--5' title: Client Features

    Client Features


    Takes two command-line arguments:

  • pid of the server;
  • message to be sent;

  • Sends the message bit-by-bit to the server;

  • Communicating using only SIGUSR1 & SIGUSR2 signals;

  • layout: section class: '-left--5' title: Client-Server Communication

    2.

    Client-Server Communication


    layout: center transition: slide-up title: Server-Client Diagram

    Server-Client Diagram

    Server-Client Diagram


    layout: section class: '-left--5' title: Client-Server Communication

    3.

    UNIX Signals


    layout: full transition: slide-down title: UNIX Signals

    But what is a Signal?

    A signal is a reporter of events.


    When a process receives a Signal:

  • The process is interrupted.
  • A default Signal Action is triggered.


  • Signal are useful tools for Interprocess Communication (IPC).

    <style> h1 { @apply text-5xl text-center } .note { @apply flex-col border-2 border-blue rounded-3xl w-140 pa-5 ml-35 mt-15 } </style>

    layout: full title: UNIX Signals Table

    Each Signal is defined by an Integer and a Macro

    Signal Macro Signal Number Default Action Description
    SIGHUP 1 Term Hang-up or death of the controlling process detected.
    SIGINT 2 Term Triggered when the user sends an interrupt by pressing <Ctrl-c>.
    SIGTRAP 5 Core Trace/breakpoint trap.
    SIGFPE 8 Core Floating Point Exception, an arithmetic Error.
    SIGKILL 9 Term Terminating signal, Cannot be caught or ignored.
    SIGSEGV 11 Core Segmentation fault, invalid memory address access.

    layout: full class: '-top--5' title: Signals VHS


    layout: full transition: slide-up title: Events generate Signals

    Events generate Signals



    Error Events

    The program did an invalid operation and cannot continue execution.



    Not All errors generate Signals!

    External Events

    Generally related to I/O or other processes.


    Includes:

    • Arrival of input.
    • Expiration of timer.
    • Termination of a child process.

    Explicit Events

    Means the use of a library function like kill() whose purpose is to generate a signal.

    <style> section { @apply border-2 border-green rounded-3xl p-5 } </style>

    layout: two-cols class: '-top--5' transition: slide-up title: Synchronous & Asynchronous Signals

    Synchronous Signals




  • Relate to specific events and are delivered during that event (unless blocked).
  • Most Errors and Explicit Requests generate Synchronous Signals.
  • Certain hardware errors are not 100% synchronous arriving a couple of instructions later.

    ::right::

    Asynchronous Signals




  • Generated by events outside the control of the receiving process.
  • Their arrival is unpredictable.
  • External Events and Explicit Requests applied to some other process generate Asynchronous Signals.
  • <style> .note { @apply border-2 border-green rounded-3xl p-5 w-100 } </style>

    layout: two-cols title: Delivering Signals



    Delivering Signals


  • When generated, a Signal becomes pending for a short period of time before it is delivered to a process.
  • However, if that signal type is being blocked, it may remain pending indefinitely.


  • When a signal type is unblocked it will be delivered immediately.


    ::right::




    For most signals a program either:

  • Ignores the Signal.
  • Executes a user-specified Signal Handler.
  • Executes the default Signal Action.


  • ( except for SIGKILL & SIGSTOP, they cannot be handled or ignored 😨 )



    <style> .note { @apply border-2 border-green rounded-3xl w-100 pa-5 } </style>

    layout: full transition: slide-up title: How About SIGUSR1 & SIGUSR2?


    SIGUSR1 & SIGUSR2

    • Can be used to indicate a desired user-defined condition.
    • Their default Signal Action is to terminate the process;
    <style> h1 { @apply text-5xl text-center } .note { @apply border-2 border-green rounded-3xl w-150 pa-5 ml-30 mt-20 } </style>

    layout: two-cols-header class: '-top-12' title: minitalk protocol

    minitalk Protocol

    SIGUSR1 and SIGUSR2 are used to communicate between Server and Client.


    ::left::

    For the Server:

  • SIGUSR1 means 0,
  • SIGUSR2 means 1.
  • ::right::

    For the Client:

  • SIGUSR1 signals that a bit was successfully received.
  • SIGUSR2 signals that the end of message was received, transmission is over.
  • <style> h1 { @apply text-5xl text-center } .note { @apply flex-col border-2 border-green rounded-3xl w-80 pa-5 ml-10 } </style>

    layout: section class: '-left--5' title: Implementation

    3.

    Implementation


    layout: quote class: '-top-12' transition: slide-up title: t_protocol data type

    t_protocol Data Type


    • Stores all the data the server needs to perform its operations.

    typedef struct s_protocol
    {
    	int  bits;     // Number of bits received
    	int  data;     // Received data
    	int  received; // Flag indicating if "header" data has been received
    	char *msg;     // Received message
    }	t_protocol;

    layout: two-cols class: 'pa-5' title: Server

    Server


    int	main(void)
    {
    	struct sigaction	sa;
    
    	sigemptyset(&sa.sa_mask);
    	sa.sa_sigaction = ft_server_sighandler;
    	sa.sa_flags = SA_SIGINFO | SA_RESTART;
    	ft_set_sigaction(&sa);
    	ft_print_pid();
    	while (1)
    		pause();
    }

    ::right::

    • Declares and initializes sa of type struct sigaction;
    • Setup Signal Handling:

    void	ft_set_sigaction(struct sigaction *sa)
    {
    	if (sigaction(SIGUSR1, sa, NULL) < 0)
    		ft_perror_exit(
    			"sigaction() failed to handle SIGUSR1");
    	if (sigaction(SIGUSR2, sa, NULL) < 0)
    		ft_perror_exit(
    			"sigaction() failed to handle SIGUSR2");
    }

    • Prints the PID to stdout;
    • Enters infinite loop, waiting for a signal to catch;

    layout: two-cols class: 'pa-5' transition: slide-down title: Client

    int	main(int argc, char **argv)
    {
    	struct sigaction	sa;
    
    	if (argc != 3)
    		ft_perror_exit(
    			"Usage: ./client [PID] [message]\n");
    	else if (kill(ft_atoi(argv[1]), 0) < 0)
    		ft_perror_exit("PID does not exist\n");
    	sigemptyset(&sa.sa_mask);
    	sa.sa_handler = ft_client_sighandler;
    	sa.sa_flags = SA_RESTART;
    	ft_set_sigaction(&sa);
    	ft_sep_color('0', '=', 28, GRN);
    	ft_printf("Sending to Server\n%sPID: %d%s\n",
    		   YEL, ft_atoi(argv[1]), NC);
    	ft_sep_color('0', '=', 28, GRN);
    	ft_send_msg(ft_atoi(argv[1]), argv[2]);
    	return (EXIT_SUCCESS);
    }

    ::right::

    Client


    • Checks if input arguments are valid;
    • Declare and initializes sa of type struct sigaction;
    • Setup Signal Handling;
    • Prints the PID of the server to stdout;
    • Sends the message to the server bit-by-bit;
    • Terminates the program successfully;

    Let's take a closer look at how the client's' signal handling is implemented:


    layout: default class: 'pr-50 pl-50' transition: slide-up title: Client into ft_send_msg()

    Sending the Message to the Server

    /* Client */
    int	main(int argc, char **argv)
    {
    	struct sigaction	sa;
    
    	if (argc != 3)
    		ft_perror_exit(
    			"Usage: ./client [PID] [message]\n");
    	else if (kill(ft_atoi(argv[1]), 0) < 0)
    		ft_perror_exit("PID does not exist\n");
    	sigemptyset(&sa.sa_mask);
    	sa.sa_handler = ft_client_sighandler;
    	sa.sa_flags = SA_RESTART;
    	ft_set_sigaction(&sa);
    	ft_sep_color('0', '=', 28, GRN);
    	ft_printf("Sending to Server\n%sPID: %d%s\n", YEL, ft_atoi(argv[1]), NC);
    	ft_sep_color('0', '=', 28, GRN);
    	ft_send_msg(ft_atoi(argv[1]), argv[2]);
    	return (EXIT_SUCCESS);
    }

    layout: two-cols class: 'pa-3' title: ft_send_msg()

    /* Client */
    static void	ft_send_msg(pid_t pid, char *msg)
    {
    	int		i;
    	int		msglen;
    
    	i = 0;
    	{
    		msglen = ft_strlen(msg);
    		ft_printf("%sOutbound msg's length = %d%s\n",
    			CYN, msglen, NC);
    		ft_send_int(pid, msglen);
    		ft_printf("\n%sSending Message%s\n", GRN, NC);
    		while (msg[i] != '\0')
    			ft_send_char(pid, msg[i++]);
    		ft_printf("\n");
    		ft_sep_color('0', '=', 28, GRN);
    		ft_printf("%sSending NULL Terminator\n", MAG, NC);
    		ft_sep_color('0', '=', 28, GRN);
    		ft_send_char(pid, '\0');
    	}
    }

    ::right::

    ft_send_msg()


    • Take the message length;
    • Print the message length to stdout;
    • Send message length bit-by-bit to the server;
    • Send message char by char to the server;
    • Send NULL Terminator;



    Let's take a closer look at how the data is sent:


    layout: two-cols class: 'pa-5' transition: slide-down title: Client sending Int

    Sending an Int

    /* Client */
    void	ft_send_int(pid_t pid, int num)
    {
    	int		bitshift;
    	char	bit;
    
    	bitshift = ((sizeof(int) * 8) - 1);
    	while (bitshift >= 0)
    	{
    		bit = (num >> bitshift) & 1;
    		ft_send_bit(pid, bit, 1);
    		--bitshift;
    	}
    }

    42
    00101010
    MSB ----- LSB

    ::right::



  • Declare & initialize bitshift with the size of the binary representation of an integer.
  • Loop while bitshift is larger than 0.

  • bitshift is used to iterate the incoming data from the Most Significant Bit (MSB) to the Least Significant Bit (LSB)


  • Isolate the current bit.
  • Send the bit to the server.
  • Decrement bitshift.
  • Repeat until the LSB bit has been sent.
  • <style> .note { @apply flex-col border-2 border-green rounded-3xl w-95 px-2 mx-5 } </style>

    layout: two-cols class: 'pa-5' title: Client sending char bits



    /* Client */
    void	ft_send_char(pid_t pid, char c)
    {
    	int		bitshift;
    	char	bit;
    
    	bitshift = ((sizeof(char) * 8) - 1);
    	while (bitshift >= 0)
    	{
    		bit = (c >> bitshift) & 1;
    		ft_send_bit(pid, bit, 1);
    		--bitshift;
    	}
    }

    ::right::

    Sending Chars


    Most of the logic is the same as what can be found in ft_send_int().


    • Except that bitshift is initialized with the size of the binary representation of a char.
    • Repeats until the LSB bit has been sent.
    <style> .note { @apply flex-col border-2 border-green rounded-3xl w-95 px-5 mx-5 } </style>

    layout: two-cols class: 'pa-5' transition: slide-up title: Sending bits



    /* Client */
    void	ft_send_bit(pid_t pid, char bit,
    				 char pause_flag)
    {
    	if (bit == 0)
    	{
    		if (kill(pid, SIGUSR1) < 0)
    			ft_perror_exit(
    				"kill() failed sending SIGUSR1\n");
    	}
    	else if (bit == 1)
    	{
    		if (kill(pid, SIGUSR2) < 0)
    			ft_perror_exit(
    				"kill() failed sending SIGUSR2\n");
    	}
    	if (pause_flag != 0)
    		pause();
    }

    ::right::

    Sending a bit to Server


    • If bit is 0 :
    • Send SIGUSR1.

    • Else if bit is 1 :
    • Send SIGUSR2.

    • If pause_flag is set to 1, the client pauses waiting for the next signal to arrive;

    layout: two-cols class: 'pa-3 -top-5' transition: slide-left title: ft_server_sighandler()



    static void	ft_server_sighandler(int sig,
    								 siginfo_t *info, void *context)
    {
    	static t_protocol	server;
    	static int			i;
    
    	usleep(PAUSE);
    	(void)context;
    	if (!server.bits)
    		server.data = 0;
    	...
    }

    ::right::

    ft_server_sighandler()



    • Follows a standard prototype;
    • server and i are static, initialized to 0;
    • Waits for PAUSE microseconds;
    • We cast context to void * to suppress compiler warnings;
    • If server.bits is 0, set/reset server.data to 0;


    layout: two-cols class: 'pa-3 -top-5' transition: slide-up title: ft_server_sighandler() Receiving Data




    static void	ft_server_sighandler(int sig,
    								 siginfo_t *info, void *context)
    {
    	...
    	if ((sig == SIGUSR2) && !server.received)
    		server.data |= 1 <<
    			(((sizeof(int) * 8) - 1) - server.bits);
    	else if ((sig == SIGUSR2) && server.received)
    		server.data |= 1 <<
    			(((sizeof(char) * 8) - 1) - server.bits);
    	++server.bits;
    	ft_strlen_received(&server);
    	ft_print_msg(&server, &i, info->si_pid);
    	ft_send_bit(info->si_pid, 0, 0);
    }

    ::right::

    Receiving Data



    • First the server expects an int as Header Information, the length of the message;
    • Every time a SIGUSR1 or SIGUSR2 is caught, server.bits is incremented;
    • After all the bits of the int have been received, the conditions to trigger ft_strlen_received() code block are reached;

    layout: two-cols class: 'pa-3 -top-5' transition: slide-down title: ft_strlen_received()


    /* Server */
    static void	ft_strlen_received(t_protocol *server)
    {
    	if ((server->bits == (sizeof(int) * 8)) &&
    		!server->received)
    	{
    		server->received = 1;
    		ft_printf("%sMessage Length : %s", YEL, NC);
    		ft_putnbr(server->data);
    		ft_printf("\n");
    		server->msg = ft_calloc((server->data + 1),
    						  sizeof(char));
    		if (!server->msg)
    			ft_perror_exit("ft_calloc() failed\n");
    		server->msg[server->data] = '\0';
    		server->bits = 0;
    	}
    }

    ::right::

    ft_strlen_received()

      When data the size of an int is received, and the Header Information is yet to be registered:

    • Sets header data received flag to true;
    • Prints the length of the message to the console;
    • Allocates memory for the message;
    • Protects against ft_calloc() failure.
    • The message is NULL terminated;
    • server->bits is reset to 0 to prepare the server to received the message bits;

    layout: two-cols class: 'pa-3 -top-5' title: ft_server_sighandler() Data Received


    /* Server */
    static void	ft_server_sighandler(int sig,
    								 siginfo_t *info, void *context)
    {
    	...
    	if ((sig == SIGUSR2) && !server.received)
    		server.data |= 1 <<
    			(((sizeof(int) * 8) - 1) - server.bits);
    	else if ((sig == SIGUSR2) && server.received)
    		server.data |= 1 <<
    			(((sizeof(char) * 8) - 1) - server.bits);
    	++server.bits;
    	ft_strlen_received(&server);
    	ft_print_msg(&server, &i, info->si_pid);
    	ft_send_bit(info->si_pid, 0, 0);
    }

    ::right::

    Receiving Data

    • First the server expects an int as Header Information, the length of the message;

    • Every time a SIGUSR1 or SIGUSR2 is caught, server.bits is incremented;

    • After all the bits of the int have been received, the conditions to trigger ft_strlen_received() code block are reached;

    • The server then proceeds to accumulate the incoming chars;

    When the message is fully received it is printed;


    layout: two-cols class: 'pa-3 -top-5' transition: slide-up title: ft_print_msg()


    static void	ft_print_msg(t_protocol *server,
    						 int *i, pid_t pid)
    {
    	if ((server->bits == 8) && server->received)
    	{
    		server->msg[*i] = server->data;
    		++(*i);
    		if (server->data == '\0')
    		{
    			ft_printf("[%sMessage bytes received!%s]\n",
    				MAG, NC);
    			ft_printf("Message:\n%s%s%s\n",
    				GRN, server->msg, NC);
    			ft_print_pid();
    			free(server->msg);
    			server->msg = NULL;
    			server->received = 0;
    			*i = 0;
    			ft_send_bit(pid, 1, 0);
    		}
    		server->bits = 0;
    	}
    }

    ::right::

    ft_print_msg()


      Once 8 bits have been received, and the Header Information has already been received:

    • The byte is stored in server->msg[i];
    • Then i is incremented so that when indexed server->msg[i] points to the next byte in memory;
    • The server receives each char of the message until the NULL terminator is received.

    <style> .note { @apply flex-col border-2 border-green rounded-3xl w-95 pa-5 mx-5 } </style>

    layout: two-cols class: 'pa-3 -top-5' transition: slide-right title: Message Received


    static void	ft_print_msg(t_protocol *server,
    						 int *i, pid_t pid)
    {
    	if ((server->bits == 8) && server->received)
    	{
    		server->msg[*i] = server->data;
    		++(*i);
    		if (server->data == '\0')
    		{
    			ft_printf("[%sMessage bytes received!%s]\n",
    				MAG, NC);
    			ft_printf("Message:\n%s%s%s\n",
    				GRN, server->msg, NC);
    			ft_print_pid();
    			free(server->msg);
    			server->msg = NULL;
    			server->received = 0;
    			*i = 0;
    			ft_send_bit(pid, 1, 0);
    		}
    		server->bits = 0;
    	}
    }

    ::right::

    Message Received


      When the byte received is the NULL terminator:


    • Prints the message to the console.
    • Re-prints the server pid.
    • Frees server->msg and sets it to NULL.
    • Header Information received flag is set to false;
    • i reset to 0;
    • SIGUSR2 is sent to the server, signaling the end of the transmission of data;

    layout: two-cols class: 'pa-3 -top-5' transition: slide-up title: ft_server_sighandler() Data Received




    static void	ft_server_sighandler(int sig,
    								 siginfo_t *info, void *context)
    {
    	...
    	if ((sig == SIGUSR2) && !server.received)
    		server.data |= 1 <<
    			(((sizeof(int) * 8) - 1) - server.bits);
    	else if ((sig == SIGUSR2) && server.received)
    		server.data |= 1 <<
    			(((sizeof(char) * 8) - 1) - server.bits);
    	++server.bits;
    	ft_strlen_received(&server);
    	ft_print_msg(&server, &i, info->si_pid);
    	ft_send_bit(info->si_pid, 0, 0);
    }

    ::right::

    Receiving Data


    • First the server expects an int as Header Information, the length of the message;
    • Every time a SIGUSR1 or SIGUSR2 is caught, server.bits is incremented;
    • After all the bits of the int have been received, the conditions to trigger ft_strlen_received() code block are reached;
    • The server then proceeds to accumulate the incoming bits; when the message is fully received it is printed;
    • The server sends a SIGUSR1 to the client;

    (bit received)


    layout: section class: '-left--5' transition: slide-up title: Demonstration

    4.

    Demonstration


    layout: fact transition: slide-right title: Demonstration


    layout : full transition: slide-up title : References

    References


    layout : section image: /img/tools-man.png backgroundSize: 85% class: '-top-8' transition: slide-left title : Tools man!


    42 logo

    Vulfmon - James Jamerson Used One Finger 👆

    layout : full transition: slide-down title : Tech Stack

    Tech Stack


    Project Tooling


    LANG="C"

    SCRIPTS="bash"

    BUILD="make"

    DEBUG="gdb"

    MEMCHECK="valgrind"

    VERSION_CTL="git"

    Dev Env


    SHELL="zsh"

    PROMPT="starship"

    EDITOR="neovim"

    MULTIPLXR="tmux"

    TERM="kitty"

    WM="i3wm"

    Presentation


    TOOL="slidev"

    SCREENREC="simplescreenrecorder"

    TERMREC="vhs"

    CONVERT="ffmpeg"

    <style> h2 { font-size: 1; } </style>

    layout : full transition: fade title : Credits


    Credits



    SlidesCarnival for slides inspiration;

    tchow-so for the incredible intro picture;


    layout : end class: 'height:100%' title : end

    This was minitalk!

    by passunca

    A minimalistic implementation of a small data exchange program using UNIX signals. 


    42 logo

    42 logo

    Thank you!