EMBEDDED LINUX TUTORIAL
SIGNALS IN LINUX - LED TOGGLING UPON RECEIVING A SIGNAL
A Practical Approach
This tutorial is about Signals. We will explain Linux signals with an experiment showing LED toggling upon receiving a signal. We are going to use C Code for this example. In this tutorial you will need to use serial port and a LED connected to beaglebone board. We are using Beaglebone Black Board for this purpose. But any board on which you can blink LED on Linux can be used.  

EmbeddedCraft YouTube Channel Reference

We have created video tutorials explaining using serial port and using G P I O in Linux. Refer these links

Accessing GPIO and LED Blinking - LED Blinking Program in C for Beaglebone Black board


Beaglebone Black Board: Serial Port Interfacing

Connecting Beaglebone Black board with Serial Port

Embedded Linux  @ EmbeddedCraft YouTube Channel

What are Signals in Linux

Signal is very similar to interrupt. it is a method of sending notification to process. Linux Process can receive signals from multiple sources. For example, SIGBUS signal. child process can also send SIG CHLD signal to parent process.
Figure 1: Signal Handler
What will when signal is received by Linux Process

When signal is received, process will run signal handler. When execution of signal handler is complete, control will return back to process. Signal handler to be registered first.

For some signals, there are predefined activities, which a process will do upon receiving signal. When SIG KILL signal is received then the Process will terminate.

For some signals, core dump is also generated, for example SIG ABRT. In case of SIG ABRT signal, process is terminated and  code dump is generated. Signal can also let Process execution suspended or resumed.

How Signals should be used in C program

Signals are defined as integer numbers. Please note these numbers depend on Linux distribution. So, always refer "signum-generic.h" file available.
Figure 2: Signal Number
For example SIGINT is number 2. Signal name are start with SIG, and  these characters represent signal description.

Signal name is  starting with SIG prefix.

you should include “signal.h” in your project. Do not use “signum-generic.h” directly.

One more point, signals can be standard signal or real time signals.  Real time signals can be queued and Real time signals also have priority. We will discuss about Real time signals in another article.

Who can send Signals

Next question may be who can send signals to process. Answer is following …
Figure 3: Signal senders
Other Process – other process can send signal. For this use kill() API is used.

Hardware Exception -  In case of hardware failure like failure to access memory, Linux kernel also send signal to process. Example SIGBUS signal is generated to indicate memory access.

Terminal – We can also send signal from terminal. For Example pressing “Control+C” will send

SIGTERM to active process.  SIGTERM signal will terminate the process.

Software Events – Software events like timer expiry, child process terminates, will generate a signal.


Signal Mask

Signal can be masked or unmasked. When signal is masked, signal delivery is blocked.
When signal  is unmasked, than signal will be delivered immediately. When signal is masked, signal delivery is stopped. There are various system calls that allow a process to add and remove signals from its signal mask set.
Figure 4: Signal Mask
For masking and unmasking of signal, we have to create signal block. Signal block is sigset_t type structure.
Add signals in the block.

sigaddset API is used to add signal in the set.

sigprocmask is used to mask signals present the specified signal block.

LED Toggling and Signal Sending Program

We will have two applications (or process). One will be signal receiver and second will be signal sender.
Figure 5: Signal Sender and Signal Receiver Process
Signal Sender process will send SIGUSR1 signal. Signal Receiver process will receive SIGUSR1 and toggle LED.
Here is the LED connection on Beaglebone Black Board.
Figure 6: LED Connection on Beaglebone Black Board
C Code

Let us review code of Signal Sender Program. We will run signal_sender process as…
./signal_send -s <signal_number> -p <process_id>

Example, if we want to send signal number 10, to a process, whose process id is 1234.
./signal_send -s 10 -p 1234

It is using Kill API to send signal.
Download Source code from Github

 Signal Sender Process Code

 Explanation

int main (int argc, char *argv[]){

int opt=0,signum=0;

pid_t pid;

// parsing command line argument

while ((opt = getopt (argc, argv, "s:p:")) != -1) {

 switch(opt){

 case 's': signum= atoi(optarg);  break;

 case 'p':  pid= atoi(optarg);  break;

 default :  signum = SIGINT;  break;

} // end of switch

} // end of while

// sending signal

C Code

Let us review code of Signal Sender Program. We will run signal_sender process as…
./signal_send -s <signal_number> -p <process_id>


Example, if we want to send signal number 10, to a process, whose process id is 1234.

./signal_send -s 10 -p 1234

It is using Kill API to send signal.

if (kill(pid, signum) == -1)

         errExit("kill");

} // end of mai

Kill system call will send specified signal to specified process. 

Signal is SIGUSER1, which is number 10.

Signal receiver Process Code

Signal receiver has to register signal handler.  For that we have to use sigaction structure. sigempty set API, will unmask all signals.  sigaction.sa_sigaction field will get the name of signal handler.  And we are going to make the signal set empty, this will unmask all signals.  Sigaction API is registering my sig action with my signal.  it means when sig user 1 signal is received  then the handler function will execute.
Now let us review signal receiver code.

Download Source code from Github

Signal Receiver Process Code

 Explanation

int main (void) {

struct sigaction sa_my,sa_int; int f=0;

// registering MYSIGNAL

sa_my.sa_flags = SA_SIGINFO;  

// signal handler will be called with three arguments

sa_my.sa_sigaction = handler;

sigemptyset(&sa_my.sa_mask);           1

// clearing all signal

// registering signal handler for SIGUSR1

sigaction( SIGUSR1, &sa_my, NULL);    2  

// registering SIGINT

sa_int.sa_flags = SA_SIGINFO;

sa_int.sa_sigaction = handler;

sigemptyset(&sa_int.sa_mask);

// registering signal handler

sigaction(SIGINT, &sa_int, NULL)         3 

We are registering signal handler “hander” for two signal.
One is  SIGUSR1 and second is SIGINT.
Sigaction system call is used to register signal handler with signal.
We are calling this two times.
2  to register SIGUSR1  and
3  to register SIGINT.

1 And we are clearing mask field for Sigaction  structure. It means we are not masking any signal for this process.

// set direction out for GPIO 60

printf("\nSignal Receiver: set direction !!!");

f=open("/sys/class/gpio/gpio60/direction", O_RDWR);

write(f,"out",3);                                     4

close(f);

f=open("/sys/class/gpio/gpio60/value", O_WRONLY);

while(int_exit ==0 )                               5

// we will be in loop till int_exit is zero

{

pause(); // block until a signal is caught

if(int_sig_received==1)

{

   if (led_toggle == 0)

   write(f,"0",1);  // LED off

   else

   write(f,"1",1); // LED on;

  int_sig_received=0;

} }  // end of while

Close(f); } // end of main

This section of code is for LED toggling.

 

We are using GPIO60, because LED is connected at GPIO1_28 port

 

4 First we have to set direction of GPIO 60 to output.

 

5 Int_exit, variable is going to change in signal handler.

 

 

Remaining code is straight forward. Here we are doing LED toggling.

Signal Handler code

 

static void handler(int sig, siginfo_t *si, void *uc){

if (sig == SIGUSR1)                                               6

{

int_sig_received=1;

switch (led_toggle){

case 0: led_toggle =1; break;

case 1: led_toggle =0;   break;

default: led_toggle =0;

}

else if  (sig == SIGINT){                                  7

int_sig_received=1; int_exit=1;  }

else

printf("\n Unknown Signal !!! \n");

} // end of handler function

Signal handler will be called when the signal is received by signal receiver process.

6 Signal number will be in sig variable.

 

 

 

 

When signal is SIGINT, then we will terminate the process.

7  in case of SIGINT, we are setting sig_received and int_exit flag to 1.

5 this will break loop in our main program.

Reference

Signal name and Signal Numbes
SIGUSR1 10
SIGUSR2 12
SIGCHLD 17
SIGSTOP 19

Refer “include/bits/signum.h”  and “include/bits/signum-generic.h” file.

List of API

Kill – send signal to process. Usage “int kill(pid_t pid, int sig);”
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
struct sigaction  {
__sigaction_handler;   /* Signal handler.  */
__sigset_t sa_mask;    /* Additional set of signals to be blocked.  */
int sa_flags;                  /* Special flags.  */
void (*sa_restorer) (void); /* Restore handler.  */
};
Sigaction structure defined in  “include/bits/sigaction.h” file

IMPORTANT ARTICLES ON EMBEDDED LINUX, YOU MAY LIKE 

Embedded Linux Development

Embedded Linux: An Introduction

Minicom: Serial Terminal In Linux

Embedded Linux And Desktop Linux: Difference At A Glance

EmbeddedCraft : Published Date: 4-April-2021   |   Feedback: For any feedback please write to us at embeddedcraft_at_gmail_dot_com