Linux Signal Handling – 2

Reading Time: < 1 minute

In the previous post I showed you how we can handle signals using the sigaction system call or the wrapper function signal over the sigaction system call. In this post we’ll see how to actually design a signal handling mechanism.

This is just one way of designing the signal handlers. Your application may just require a simple enough code but it’s just better to know what else can be done.

A simple signal handling framework

We’ve seen that signal handlers aren’t a great place to write long sophisticated code since quite a lot of functions that you might want to use aren’t signal safe. For this reason let’s try to write a generic signal handling framework though which we can delegate the actual task to be performed on seeing the signal at a later time.

We’ve a couple of choices on how we want to handle the signal

  • Execute the handler in the same process. This would make the main process stuck and not process the incoming signals while we’re executing a signal handler.
  • Execute the handler in a separate process and pass back the result using either a socketpair or a shared memory region or a UNIX domain socket.

The second option is more promising due to the fact that the main application would be free to handle multiple signals while signal handlers are executed in a different process. We’ve would however code for both the options.

Writing our signal wrapping structure

We need to have the following in our wrapper

#include <signal.h>
#include <unistd.h>
struct app_signal;
 
/*Our signal handler typedef, to avoid writing long lines.*/
typedef void (*signal_handler)(struct app_signal*);
 
/*
 * Flags on how we want to execute the handler.
 */
#define APP_SIGNAL_EXEC_THIS                 (1<<0)
#define APP_SIGNAL_EXEC_FORK                 (1<<1)
 
/*Our signal wrapper*/
struct app_signal{
 
/*The signal number*/
int signal;
 
/*Flags for how we want to execute this handler*/
int flags;
 
/*File descriptor for communicating back to main process.*/
int fd;
 
/*Maximum number of saved siginfo. See later.*/
int max_signfo;
 
/*Number of saved siginfos*/
int nr_siginfo;
 
/*Our signal handler*/
signal_handler handler;
 
/*Blocked signal Mask*/
sigset_t mask;
 
/*Siginfo for storing the information from signal*/
siginfo_t siginfo;
 
/*For case where there maybe multiple signals arriving then we can handle*/
siginfo_t *saved_signinfo; //This would actually be an array.
 
/*pid of the handler process*/
pid_t pid;
};

NOTE: If you want to have multiple signal handlers to be registered for same signal, then you can make the above struct have a list. So that the signal handlers which are registered are executed in the order that they’ve been registered.

Provide an API to register and de-register signals

We add a couple of function prototypes to add and remove the registered signals. In this tutorial we’ll only consider that there’s a single signal handler to handle a particular signal. So our final header would something like below

 
#include <signal.h>
#include <unistd.h>
struct app_signal;
 
/*Our signal handler typedef, to avoid writing long lines.*/
typedef void (*signal_handler)(struct app_signal*);
 
/*
 * Flags on how we want to execute the handler.
 */
#define APP_SIGNAL_EXEC_THIS                 (1<<0)
#define APP_SIGNAL_EXEC_FORK                 (1<<1)
 
/*Our signal wrapper*/
struct app_signal{
 
/*The signal number*/
int signal;
 
/*Flags for how we want to execute this handler*/
int flags;
 
/*File descriptor for communicating back to main process.*/
int fd;
 
/*Maximum number of saved siginfo. See later.*/
int max_signfo;
 
/*Number of saved siginfos*/
int nr_siginfo;
 
/*Our signal handler*/
signal_handler handler;
 
/*Blocked signal Mask*/
sigset_t mask;
 
/*Siginfo for storing the information from signal*/
siginfo_t siginfo;
 
/*For case where there maybe multiple signals arriving then we can handle*/
siginfo_t *saved_signinfo; //This would actually be an array.
 
/*pid of the handler process*/
pid_t pid;
};
 
bool app_signal_pending(int signum);
bool app_signal_registered(int signum);
int  app_register_signal(int signum, struct app_signal *);
void app_unregister_signal(int signum);
struct app_signal* app_signal_first_handler(int signum);
int app_signal_subsys_init(void);
void app_signal_subsys_exec_pending(void);
void app_unregister_all(void);

Implementation of the app_signal handling mechanism

We can only have a single function that actually registers the signal handler with the kernel and it’s going to be a generic handler which will get used by all signals. The difference is what is get stored and how that information is relayed back to the main application.

Since the signal handler can’t actually call the installed handlers, we need another function which the main application would use in it’s tight loop to handle the signals seen so far. Remember though that we’ve to keep the same semantics for signal handler execution i.e. the same signal can’t be seen while the installed handler is being executed.

The complete code is present on the following GitHub 

https://github.com/pranjas/app_signal

 

Leave a Reply