Professional Documents
Culture Documents
MULTITHREADED APPLICATION
BY LIRAN B.H | NOVEMBER 30, 2017 | 3 COMMENTS | LINUX
FacebookTwitterMore
Signals are very useful feature in linux to send notification from one process to another
and from the kernel to the process. Signals are sent in some error cases (accessing
wrong memory address, bus error, floating point error, …) and also to inform the user
application ( timer expired, child process finished, IO is ready, ….)
Signal handlers are per process, signal masks are per thread
On a multithreaded application –
action.sa_sigaction = handler;
if ( sigaction (SIGRTMIN + 3, &action, NULL) == -1)
{
perror("sigusr: sigaction");
_exit(1);
}
}
int main()
{
pthread_t t1 , t2, t3 ;
set_sig_handler();
pthread_exit(NULL);
return 0;
}
Compile and run the app, you will see periodic output for each thread:
thread1
thread2
thread3
thread1
thread2
thread3
...
The kernel choose one thread and run the signal handler in its context. In my case
thread 1 selected so the output for 10 times is:
signal
thread2
thread3
signal
thread2
thread3
...
This behaviour can be problematic in case the selected thread is an important task
(Note that if the signal is an exception (SIGSEGV, SIGFPE, SIGBUS, SIGILL, …) the
signal will be caught by the thread doing the exception)
We can’t choose the selected thread but we can do a little trick to hack the system to
choose the thread we want.
The trick is to block the signal on all threads except one thread – the one we want to
run the signal in:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
void mask_sig(void)
{
sigset_t mask;
sigemptyset(&mask);
action.sa_sigaction = handler;
if (sigaction(SIGRTMIN + 3, &action, NULL) == -1)
{
perror("sigusr: sigaction");
_exit(1);
}
}
int main()
{
pthread_t t1,t2,t3;
set_sig_handler();
pthread_create(&t1,NULL,threadfn1,NULL);
pthread_create(&t2,NULL,threadfn2,NULL);
pthread_create(&t3,NULL,threadfn3,NULL);
pthread_exit(NULL);
return 0;
}
We block the signal on threads 1,2 so the system will deliver the signal to thread 3
Run the app, send the signal with kill command. The output:
signal
thread1
thread2
signal
thread1
thread2
...
Another trick is to create a thread for signal handling that will be blocked using sigwait ,
waiting for signal
All the signals fields are stored per thread. Actually, there is no structure for the process
all the threads on the same process points to the same memory and files tables so the
kernel need to choose a thread to deliver the signal to:
struct task_struct
{
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
volatile long state;
...
...
...
/* Signal handlers: */
struct signal_struct *signal;
sigset_t blocked;
sigset_t real_blocked;
size_t sas_ss_size;
}
for(i=0;i<5;i++)
{
puts("signal");
sleep(2);
}
}
void set_sig_handler(void)
{
struct sigaction action;
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = handler;
if (sigaction( SIGRTMIN + 3, &action, NULL) == -1)
{
perror("sigusr: sigaction");
_exit(1);
}
}
int main()
{
pthread_t t1, t2, t3;
set_sig_handler();
pthread_create( &t1, NULL , threadfn1, NULL);
sleep(3);
pthread_kill(t1,SIGRTMIN+3);
sleep(15);
pthread_kill(t2,SIGRTMIN+3);
pthread_kill(t3,SIGRTMIN+3);
pthread_exit(NULL);
return 0;
}
We start with creating 3 threads, then we send a signal to thread 1, wait for the signal
handler to finish then send signals both to threads 2 and 3 , they will run the signal
handler at the same time so in this case we will see :
signal
signal
thread1
typedef struct
{
int si_signo;
int si_code;
union sigval si_value;
int si_errno;
pid_t si_pid;
uid_t si_uid;
void * si_addr;
int si_status;
int si_band;
} siginfo_t;
DESCRIPTION
si_signo
si_code
Signal code. This field is always set. Refer to Signal Codes for information on valid settings, and for
which of the remaining fields are valid for each code.
si_value
Signal value.
si_errno
si_pid
si_uid
si_status
si_band
st_mtime