theme | font | class | highlighter | colorSchema | info | transition | title | author | mdc | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
dracula |
|
text-center |
shiki |
dark |
minitalk :A minimalistic implementation of a small data exchange program using UNIX signals. |
slide-left |
minitalk |
passunca |
true |
A minimalistic implementation of a small data exchange program using UNIX signals
42 Student
&& Musician
Rank : 2
Grade : Learner
Github : @PedroZappa
::right::
A minimalistic implementation of a small data exchange program using UNIX signals.
<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>
Takes two command-line arguments:
Sends the message bit-by-bit to the server;
When a process receives a Signal:
Signal are useful tools for Interprocess Communication (IPC).
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. |
The program did an invalid operation and cannot continue execution.
Not All errors generate Signals!
Generally related to I/O or other processes.
Includes:
- Arrival of input.
- Expiration of timer.
- Termination of a child process.
Means the use of a library function like kill() whose purpose is to generate a signal.
Certain hardware errors are not 100% synchronous arriving a couple of instructions later.
::right::
When a signal type is unblocked it will be delivered immediately.
::right::
( except for SIGKILL & SIGSTOP, they cannot be handled or ignored 😨 )
<style> .note { @apply border-2 border-green rounded-3xl w-100 pa-5 } </style>
- Can be used to indicate a desired user-defined condition.
- Their default Signal Action is to terminate the process;
SIGUSR1 and SIGUSR2 are used to communicate between Server and Client.
::left::
::right::
- 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;
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;
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::
- 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;
/* 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);
}
/* 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::
- 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;
/* 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::
bitshift is used to iterate the incoming data from the Most Significant Bit (MSB) to the Least Significant Bit (LSB)
/* 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::
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.
/* 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::
- If bit is 0 :
- Else if bit is 1 :
- If pause_flag is set to 1, the client pauses waiting for the next signal to arrive;
Send SIGUSR1.
Send SIGUSR2.
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::
- 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::
- 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;
/* 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::
- 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;
When data the size of an int is received, and the Header Information is yet to be registered:
/* 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::
-
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;
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::
- 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;
Once 8 bits have been received, and the Header Information has already been received:
The server receives each char of the message until the NULL terminator is 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::
- 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;
When the byte received is the NULL terminator:
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::
- 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)
Standard Signals (The GNU C Library)
Miscellaneous Signals (The GNU C Library)
Signaling Another Process (The GNU C Library)
layout : section image: /img/tools-man.png backgroundSize: 85% class: '-top-8' transition: slide-left title : Tools man!
LANG="C"
SCRIPTS="bash"
BUILD="make"
DEBUG="gdb"
MEMCHECK="valgrind"
VERSION_CTL="git"
SlidesCarnival for slides inspiration;
tchow-so for the incredible intro picture;
by passunca
A minimalistic implementation of a small data exchange program using UNIX signals.