Linux send signal to thread

How to Send a Signal to a Process Without Killing or Stopping It

announcement - icon

The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.

To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.

Connect your cluster and start monitoring your K8s costs right away:

1. Introduction

Signals are one of the main inter-process communication (IPC) methods in Linux. Some signals are for killing processes, while others are simply notifications.

In this tutorial, we explore ways to send a non-terminating signal to a process. First, we list and discuss interrupting and non-interrupting signals. Next, we describe a way to handle them. Finally, we turn to potentially safe signals and their possible pitfalls.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. Interrupting and Non-interrupting Signals

To get a complete list of every signal from a given system, we can use the kill command with its -l flag:

$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX

Officially, Linux provides most signals that SystemV, Berkeley Software Distribution (BSD), and POSIX support, but there are special cases:

  • SIGEMT is unsupported
  • SIGINFO is unsupported
  • SIGSYS is unsupported
  • SIGABRT is the same as SIGIOT
  • SIGIO, SIGPOLL, and SIGURG are the same
  • SIGBUS is SIGUNUSED

What happens when using these depends on the distribution and implementation, just like the #) numbers of many signals from the table may be different. For example, Debian terminates a process on an unhandled SIGSYS but doesn’t provide SIGUNUSED in the table from kill.

Let’s look at signals by the way we can handle them.

Читайте также:  Vnc через ssh linux

2.1. Ignorable Interrupting Signals

Now, let’s separate ignorable signals from the signal table, (part of) the default action of which is to stop or terminate the process:

+-------------+-------------------------------------+ | Signal Name | Function | +-------------+-------------------------------------+ | SIGHUP| terminal hangup | +-------------+-------------------------------------+ | SIGINT | interruption request | +-------------+-------------------------------------+ | SIGQUIT | terminate with core dump | +-------------+-------------------------------------+ | SIGILL | illegal instruction | +-------------+-------------------------------------+ | SIGTRAP | trace or breakpoint | +-------------+-------------------------------------+ | SIGABRT | abort | +-------------+-------------------------------------+ | SIGBUS | bad memory access or bus error | +-------------+-------------------------------------+ | SIGFPE | floating-point exception | +-------------+-------------------------------------+ | SIGUSR1 | custom user signal 1 | +-------------+-------------------------------------+ | SIGSEGV | invalid memory reference | +-------------+-------------------------------------+ | SIGUSR2 | custom user signal 2 | +-------------+-------------------------------------+ | SIGPIPE | write to pipe without readers | +-------------+-------------------------------------+ | SIGALRM | timer signal | +-------------+-------------------------------------+ | SIGTERM | like SIGQUIT without a core dump | +-------------+-------------------------------------+ | SIGSTKFLT | stack fault on coprocessor | +-------------+-------------------------------------+ | SIGTSTP | stop at terminal | +-------------+-------------------------------------+ | SIGTTIN | terminal input, background process | +-------------+-------------------------------------+ | SIGTTOU | terminal output, background process | +-------------+-------------------------------------+ | SIGURG | urgent condition on socket | +-------------+-------------------------------------+ | SIGXCPU | exceeded CPU time limit | +-------------+-------------------------------------+ | SIGXFSZ | exceeded file size limit | +-------------+-------------------------------------+ | SIGVTALRM | virtual alarm clock | +-------------+-------------------------------------+ | SIGPROF | profiling timer expired | +-------------+-------------------------------------+ | SIGIO | input or output possible | +-------------+-------------------------------------+ | SIGPWR | power failure | +-------------+-------------------------------------+ | SIGSYS | bad system call | +-------------+-------------------------------------+ | SIGRT* | real-time signals | +-------------+-------------------------------------+

Notably, although SIGUSR1 and SIGUSR2 are user-defined, their default action is still to kill the process. Still, all of the above can be ignored.

2.2. Non-ignorable Interrupting Signals

From the interrupting signals, there is a subset we’re unable to ignore:

Of course, even SIGKILL might not always work, but it’s the last resort.

2.3. Non-interrupting Signals

Finally, we can turn to signals that we can ignore but wouldn’t terminate the process if we don’t handle the following:

  • SIGCHLD – sent by the kernel when a child process completes
  • SIGCLD – synonym for SIGCHLD
  • SIGCONT – continue if stopped
  • SIGURG – urgent condition on socket
  • SIGWINCH – window changed
Читайте также:  Настройка времени через командную строку linux

Armed with this knowledge, we can check ways to send a signal without killing a process.

3. Trap to Prevent Termination

In Linux, we can use the trap command to intercept signals so that we can handle them:

$ trap 'echo "SIGINT"' SIGINT $ kill -SIGINT $$ SIGINT $

In this case, we set a handler for the SIGINT signal in which we just echo the signal name. After that, we send the signal by passing kill the shell process ID (PID) $$.

Furthermore, using this method, we can set and invoke handlers for any and all available signals (here, 64):

$ kill -SIGCONT $$ $ trap 'echo "TRAP"' $(seq 1 64) $ kill -SIGCONT $$ TRAP $

In addition, we can even prevent the default action for all signals except SIGKILL and SIGSTOP:

$ trap 'echo "TRAP"' $(seq 1 64) $ kill -SIGILL $$ TRAP $ kill -SIGUSR1 $$ TRAP $ kill -SIGKILL $$ Killed

In this case, we invoke several signals that usually terminate a process, but only SIGKILL actually manages to do that, as we trap the rest.

4. Safe Signals

Although receiving a specific signal without having a trap or handler might not make much sense, we can still leverage signals that don’t stop or terminate our process by default:

In theory, SIGCONT is harmless, as it only performs its task if a process is halted. Otherwise, it should do nothing.

However, this method doesn’t come without its pitfalls:

$ kill -SIGINT $$ $ kill -SIGTERM $$ $

Normally, SIGINT and SIGTERM terminate processes if they aren’t handled. Yet, sending them to the shell doesn’t kill it in this case. The reason for that is because some applications may have internal handlers for the signals Linux IPC mechanism.

Still, this may mislead us into thinking SIGINT and SIGTERM are non-terminating in general, which they aren’t:

$ sleep 10 & [1] 666 $ kill -SIGTERM 666 $ [1]+ Terminated sleep 10

In fact, this goes both ways – we can choose a seemingly safe signal, which can still terminate the process that receives it. Thus, we should thread carefully when trapping or sending any signal.

Still, we can employ this method with the correct signals when debugging with tools like gdb or just debugging Bash scripts in general.

5. Summary

In this article, we explored ways to send a signal that doesn’t stop or terminate a process.

In conclusion, as long as the signal is safe and there is a proper handler, we can send signals without interrupting a process.

Читайте также:  Монтирование cifs astra linux

Источник

Can I send signals to different threads

Say I have a Thread A, which wants to send signals to Threads B, C and D. Can I do something like this.

SendSignalTo( ThreadB, SIGUSR1 ); SendSignalTo( ThreadC, SIGUSR1 ); SendSignalTo( ThreadD, SIGUSR1 ); 
void SIGUSR1_Handler_for_ThreadB(. ) void SIGUSR1_Handler_for_ThreadC(. ) void SIGUSR1_Handler_for_ThreadD(. )

Signals aren’t really that great. On Linux, you could use eventfds for communication and have each thread run an epoll loop on its respective fd. This is also great for load balancing, as you can have multiple threads wait for the same eventfd, and precisely one is guaranteed to wake up.

2 Answers 2

You can send a signal to a specific thread using pthread_kill() , or if you don’t mind being GNU-specific, using pthread_sigqueue() (which can specify one int or void * the handler can access via info->si_value ).

There is only one signal handler per signal for the process. This means that a specific signal will always call the same handler function, no matter which thread it happens to be. If one thread sets a new signal handler, the signal handler will change for all threads.

However, the workaround is trivial: use a per-thread function pointer to define which function the signal handler should call. Just remember the signal handler limitations — you can only use async-signal safe functions in a signal handler.

/* Simplify by defining the signal handler function type, assume SA_SIGINFO */ typedef void (*signal_handler_t)(int, siginfo_t *, void *); /* Per-thread variable pointing to the desired function */ static __thread signal_handler_t thread_handler = NULL; /* Process-wide actual signal handler */ static void signal_handler(int signum, siginfo_t *info, void *context)

The atomic load ( __sync_fetch_and_or() ) allows you to trivially change the per-thread handler using a simple atomic store at any point in time, without even blocking the signal. Switching to function new_thread_handler is then

 signal_handler_t func; do < func = thread_handler; >while (!__sync_bool_compare_and_swap(&thread_handler, func, new_thread_handler)); 

The __sync_fetch_and_or() and the function switch can both be replaced by one C++11-style __atomic_ call, but I don’t have a GCC recent enough yet, so I’m still using the old-style __sync_ calls.

POSIX also supports real-time signals, SIGRTMIN+0 , SIGRTMIN+1 , . SIGRTMAX . They have the added benefit that more than one of them can be pending at the same time. They are much better suited for this kind of thing than the traditional signals.

Источник

Оцените статью
Adblock
detector