Externally Disabling Signals for a Linux Program

Externally disabling signals for a Linux program

The process signal mask is inherited across exec, so you can simply write a small wrapper program that blocks SIGINT and executes the target:

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

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

sigemptyset(&sigs);
sigaddset(&sigs, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, 0);

if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}

If you compile this program to noint, you would just execute ./noint ./y.

As ephemient notes in comments, the signal disposition is also inherited, so you can have the wrapper ignore the signal instead of blocking it:

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
struct sigaction sa = { 0 };

sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, 0);

if (argc > 1) {
execvp(argv[1], argv + 1);
perror("execv");
} else {
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
}
return 1;
}

(and of course for a belt-and-braces approach, you could do both).

Interrupting select, sleep and similar blocking functions inside a thread (pthreads). Signals in threads

Here is what I came up with. I don't know if there is another solution. I'll wait for suggestions.

To signal that work needs to be done SIGUSR1 is sent to the program. It sends SIGALERT to the threads to make them interrupt blocking code like sleep, select, etc. As a side effect blocking in main thread also gets interrupted, even though the signals are sent to the other threads only.

It can't be done with just one signal - like handle sigalert and then signal alert the two other threads using pthread_kill, because for it enters an endless loop.

#include <stdio.h>
#include <stdlib.h> // EXIT_FAILURE
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h> // for errno
#include <pthread.h>

#ifndef __cplusplus
#define true 1
#define false 0
#define nullptr NULL
typedef int bool;
#endif

// I copied this. I don't know why it's in a loop
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

pthread_t t1;
pthread_t t2;
pthread_t st;
pthread_t mainThreadId; // main thread

void *workerThread(void * param);
void unblockSignals();
void alertSignaHlandler(int sigNum);
void installSignalHandler(int sigNum, __sighandler_t handler);

void sigUsr1Handler(int sigNum) {
pthread_kill(t1, SIGALRM);
// pthread_kill(t2, SIGALRM); // let's say we only want t1 to interrupt
}

// on SIGTERM we want all threads to interrupt sleep so they can quit faster
void sigTermHandler(int sigNum) {
// setRunning(false); // also implement a global running vairable with a mutex, that other threads check to quit
pthread_kill(t1, SIGALRM);
pthread_kill(t2, SIGALRM);
// pthread_kill(mainThreadId, SIGALRM); // main thread will be interrupted by the signal anyway.
}

void alertSignaHlandler(int sigNum) {}

int main() {
void * ret1;
void * ret2;

mainThreadId = pthread_self();

printf("main starting with pid %d\n", getpid());

installSignalHandler(SIGALRM, alertSignaHlandler); // we need this to prevent program termination on alarm
installSignalHandler(SIGUSR1, sigUsr1Handler); // send this signal to interrupt other threads
installSignalHandler(SIGTERM, sigTermHandler);
installSignalHandler(SIGINT, sigTermHandler);

pthread_create(&t1, NULL, &workerThread, (void *)1);
pthread_create(&t2, NULL, &workerThread, (void *)2);

// simulate work in main thread. Only interrupt this on SIGTERM or SIGINT (ctrl-c)
time_t ts = time(NULL);
sleep(20);
ts = time(NULL) - ts;
printf("main(): sleep took %lds\n", ts);

pthread_join(t1, &ret1);
pthread_join(t2, &ret2);

printf("main thread reached end\n");

return 0;
}

void *workerThread(void * param){
long id = (long) param;

printf("workerThread %d starting...\n", id);

// in the real world this will be in a loop. somethign like this
// while(running) {
// // do something every minute
// doSomething();
// sleep(60); // this sleep we want to interrupt, because we don't want to wait 60 seconds to exit
// }

time_t ts = time(NULL);
sleep(60); // this can also be select, read or other timed blocking call.
ts = time(NULL) - ts;
printf("workerThread(%ld): sleep took %lds\n", id, ts);

return NULL;
}

void installSignalHandler(int sigNum, __sighandler_t handler){
struct sigaction newAction, oldAction;

memset(&newAction, '\0', sizeof(newAction));
newAction.sa_flags = 0;
//newAction.sa_flags = SA_RESETHAND;
newAction.sa_handler = handler;
sigaction(sigNum, &newAction, &oldAction);
}

Persistent signal handling

NOTE: this is largely guesswork.

I'm pretty sure that calling pthread_exit in the main thread is a bad idea. If the main thread has quit, then the OS may try to send subsequent signals to some other thread.

I recommend that instead of using pthread_exit in the main thread, you just pthread_join() all the other threads, then exit normally.

But it's also important to ensure that the other threads do not get the signals. Normally this is done with sigprocmask (or maybe more correctly pthread_sigmask, which is the same under Linux) to mask the signal out in the worker threads. This ensures that the signal is never delivered to them.

Note that to avoid race conditions, you should use pthread_sigmask in the main thread just before creating a child thread, then set the signal mask back again in the main thread afterwards. This ensures that there is no window, however small, during which a child thread can possibly get unwanted signals.

If no threads are blocking a signal, and one thread is in `sigwait()`, is there any guarantee that the blocked thread will receive the next signal?

At least for Linux: None of the two possiblities is guaranteed, as:

From man 7 signal:

The signal disposition is a per-process attribute: in a multithreaded application, the disposition of a particular signal is the same for all threads.

So as more than one thread does not block SIGINT those may received it.

Howto send signals to a program started in Debug mode in KDevelop

One possible Solution is:

  • Right Click on the CMake Project -> Debug as... -> Native Application.
  • Change to the "gdb"-Tab inside KDevelop.
  • Hit the "Pause"-Icon on the right corner to enable the input field of the "gdb"-Tab
  • Type signal <Signal>, e.g. signal SIGTERM
  • The program continues and it catches the signal sent.

How to disable GCC warnings for a few lines of code

It appears this can be done. I'm unable to determine the version of GCC that it was added, but it was sometime before June 2010.

Here's an example:

#pragma GCC diagnostic error "-Wuninitialized"
foo(a); /* error is given for this one */

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
foo(b); /* no diagnostic for this one */
#pragma GCC diagnostic pop

foo(c); /* error is given for this one */
#pragma GCC diagnostic pop

foo(d); /* depends on command line options */

In general, how expensive is calling an external program?

Like I said in my comment above, perhaps it would be best if you simply tried it and did some benchmarking. I'd expect this to depend primarily on the OS you're using.

That being said, starting a new process generally is many orders of magnitude slower than calling a subroutine (I'm tempted to say something like "at least a million times slower", but I couldn't back up such a claim with any measurements).

Possible reasons why starting a process is much slower:

  • Disk I/O (the OS has to load the process image file into memory) — this is going to be a big factor because I/O is many orders of magnitude slower than a simple CPU jump/call instruction.

    To give you a rough idea of the orders of magnitude involved, let me quote this 2011 blog article (which is about memory access vs HDD access, not CPU jump instruction vs HDD access):

    "Disk latency is around 13ms, but it depends on the quality and rotational speed of the hard drive. RAM latency is around 83 nanoseconds. How big is the difference? If RAM was an F-18 Hornet with a max speed of 1,190 mph (more than 1.5x the speed of sound), disk access speed is a banana slug with a top speed of 0.007 mph."

    You do the math.

  • allocations of memory & other kernel data structures

  • laying out the process image in memory & performing relocations
  • creation of a new OS thread
  • context switches

etc.

Apparently, all of the above points mean that your OS is likely to perform lots of internal subroutine calls to start a new process, so doing just one subroutine call yourself instead of having the OS do hundreds of these is bound to be comparatively super-cheap.



Related Topics



Leave a reply



Submit