Linux
multithreading
signal handling
concurrent programming
POSIX signals

Signal handling with multiple threads in Linux

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Signal handling in Linux is a crucial aspect for managing asynchronous events in applications. Signals can interrupt a program's normal flow to handle asynchronous events such as hardware exceptions or system calls. When dealing with multithreaded applications, signal handling becomes more complex due to the concurrent nature of threads. In Linux, the behavior of signal handling in a multithreaded environment is governed by standard POSIX threads (pthreads) and various system-specific intricacies.

Basics of Signal Handling

Signal handling involves three primary actions in any Linux environment:

  1. Delivering a Signal: This is the mechanism by which the kernel sends a signal to a process.
  2. Catching a Signal: The process must have an appropriate handler if it needs to respond to a specific signal instead of default actions like termination.
  3. Blocking a Signal: Temporarily suppressing the signal so it doesn’t interrupt the process or thread.

Signals are identified with integer values and include both standard signals (e.g., SIGINT, SIGTERM) and real-time signals, which have a higher priority.

Signal Behavior in a Multithreaded Environment

When dealing with signals in multithreaded applications, there are specific considerations:

  1. Signal Masks: Each thread can have its own signal mask, determining which signals it can receive. The signal mask is one of the attributes that can be set before starting a thread.
  2. Thread-Level Signal Delivery: In a multithreaded program, a signal can be targeted to a specific thread or simply sent to the process. If targeted to the process, it’s delivered to one of the threads that do not have the signal blocked.
  3. Handlers Execution: Once a signal is delivered to a thread and it is unblocked, the signal handler set up for that signal is executed.

Example: Handling Signals in Multithreaded Applications

c
1#include <pthread.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6
7void signal_handler(int signum) {
8    printf("Received signal %d in thread %ld\n", signum, pthread_self());
9}
10
11void *thread_function(void *args) {
12    printf("Thread %ld is running\n", pthread_self());
13    while (1) {
14        sleep(1);
15    }
16    return NULL;
17}
18
19int main() {
20    pthread_t threads[3];
21    struct sigaction sa;
22
23    sa.sa_handler = signal_handler;
24    sigemptyset(&sa.sa_mask);
25    sa.sa_flags = 0;
26
27    // Set the signal handler for SIGUSR1
28    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
29        perror("sigaction");
30        exit(EXIT_FAILURE);
31    }
32
33    // Create multiple threads
34    for (int i = 0; i < 3; i++) {
35        if (pthread_create(&threads[i], NULL, thread_function, NULL) != 0) {
36            perror("pthread_create");
37            exit(EXIT_FAILURE);
38        }
39    }
40
41    // Send SIGUSR1 to all threads
42    sleep(2);
43    for (int i = 0; i < 3; i++) {
44        pthread_kill(threads[i], SIGUSR1);
45    }
46
47    // Wait for threads to finish
48    for (int i = 0; i < 3; i++) {
49        pthread_join(threads[i], NULL);
50    }
51
52    return 0;
53}

Key Points in Multithreaded Signal Handling

AspectDescription
Signal MaskEach thread can set and manage its own signal mask.
Default DeliverySignals sent to a process are typically delivered to any thread that does not block them.
Specific Thread Targetpthread_kill() can send signals to a specific thread.
Real-Time SignalsHave additional features over standard signals such as queuing.

Additional Considerations

Signal Stack

Especially important in multithreaded applications is the need for a dedicated signal stack, which can prevent stack overflow if the regular stack is deeply nested at signal delivery time. This can be set up with sigaltstack().

Asynchronous Signal Safety

It is crucial to adhere to signal safety, ensuring that handlers only execute async-signal-safe functions. Unsafe functions within a signal handler could lead to undefined behavior, especially in a multithreaded scenario.

Synchronization

Signal handling can introduce race conditions to multithreaded code. Mutexes and other synchronization mechanisms should cautiously coordinate shared resources inside signal handlers.

Real-time Signals

Real-time signals behave slightly differently as they can be queued. However, complexities arise with ordering, priorities, and appropriate handling in multithreaded systems.

Conclusion

Signal handling in multithreaded applications is essential and intricate due to the concurrent execution nature. Proper organization and management of signal masks, signal handlers, and thread coordination are fundamental to maintaining functionality and stability in these scenarios. By understanding and leveraging system and library functions, developers can effectively manage signal handling in Linux multithreaded applications.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.