Related
I have the following program where only one thread installs the signal handler. But when I tested the code by sending signal to each thread, all the threads execute the signal handler.
Does all the threads share the same signal handler. I was under the assumption that it would happen(threads share signal handler) only when the main process which spawns these threads install the signal handler.
And one more question is about the context in which the signal handler executes. Is it guaranteed that the signal sent to the particular thread will execute in the same thread context for the given scenario?
void handler(int signo, siginfo_t *info, void *extra)
{
printf("handler id %d and thread id %d\n",syscall( SYS_gettid ),pthread_self());
}
void signalHandler()
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, NULL);
//sigaction(SIGINT, &sa, NULL);
}
void *threadfn0(void *p)
{
signalHandler();
printf("thread0\n");
while ( 1 )
{
pause();
}
}
void *threadfn1(void *p)
{
while(1){
printf("thread1\n");
sleep(15);
}
return 0;
}
void *threadfn2(void *p)
{
while(1){
printf("thread2\n");
sleep(15);
}
return 0;
}
int main()
{
pthread_t t0,t1,t2;
pthread_create(&t0,NULL,threadfn0,NULL);
printf("T0 is %d\n",t0);
pthread_create(&t1,NULL,threadfn1,NULL);
printf("T1 is %d\n",t1);
pthread_create(&t2,NULL,threadfn2,NULL);
printf("T2 is %d\n",t2);
sleep(10);
pthread_kill(t2,SIGSEGV);
sleep(10);
pthread_kill(t1,SIGSEGV);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t0,NULL);
return 0;
}
output:
T0 is 1110239552
T1 is 1088309568
T2 is 1120729408
thread0
thread1
thread2
handler id 18878 and thread id 1120729408
thread2
thread1
handler id 18877 and thread id 1088309568
thread1
From the manpage for signal(7):
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 all threads share the same handlers, yes. If you use pthread_kill() to send a signal to a specific thread, that thread should execute the handler (Depending on the thread's signal mask set with pthread_sigmask(), of course).
Also note that you can't safely use printf() or other stdio functions in a signal handler. See the list of allowed functions in signal-safety(7).
All threads share the signal handler. You can use pthread_sigmask() to select, which threads have which signals blocked or unblocked and therefore can execute the handler. If several threads have the same signal unblocked, then any of them can execute the handler.
So cleaned up and fixed example looks something like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/syscall.h>
static void handler (int signo, siginfo_t *info, void *extra)
{
printf ("SIGNAL %u, handler id %lu and thread id %lu\n", signo, syscall (SYS_gettid), pthread_self ());
}
static void signalHandler (void)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction (SIGSEGV, &sa, NULL);
}
static void *threadfn0 (void *p)
{
signalHandler ();
sigset_t set;
sigfillset (&set);
pthread_sigmask (SIG_UNBLOCK, &set, NULL);
printf ("thread0\n");
while (1) {
pause ();
}
}
static void *threadfn1 (void *p)
{
while (1) {
printf ("thread1\n");
sleep (15);
}
return 0;
}
static void *threadfn2 (void *p)
{
while (1) {
printf ("thread2\n");
sleep (15);
}
return 0;
}
int main (int argc, char *argv[])
{
pthread_t t0, t1, t2;
// By default, block all signals in all threads and
// unblock them only in one thread after signal handler
// is set up, to avoid race conditions
sigset_t set;
sigfillset (&set);
pthread_sigmask (SIG_BLOCK, &set, NULL);
pthread_create (&t0, NULL, threadfn0, NULL);
printf ("T0 is %lu\n", t0);
pthread_create (&t1, NULL, threadfn1, NULL);
printf ("T1 is %lu\n", t1);
pthread_create (&t2, NULL, threadfn2, NULL);
printf ("T2 is %lu\n", t2);
pthread_kill (t2, SIGSEGV);
pthread_kill (t1, SIGSEGV);
pthread_kill (t0, SIGSEGV);
pthread_join (t2, NULL);
pthread_join (t1, NULL);
pthread_join (t0, NULL);
return 0;
}
I want to create a simple program that uses fork and creates a child process which with the use of pause is waiting. I want this child process to start after it gets a specific signal from father process. Code I've written:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t c = fork();
if (c == 0) {
pause();
printf("signal was given");
}
if (c > 0)
kill(c, SIGINT);
return 0;
}
I think kill gives a specific signal to a process with pid c(child) and I thought that pause just waits for a signal to unpause that process. However in this case running this program has no results. I have also tried adding a signal catching function to the child using signal(SIGINT, handler) and creating a handler function that prints the desired result but it is still not working. Any ideas?
If you send SIGINT, whose default disposition is to kill the process, to a process that neither blocks it nor handles it, the process will die.
If you want the signal to interrupt blocking calls like pause(), it needs to have a handler.
But simply installing a handler introduces race conditions:
if (c == 0 ){
//< if the signal arrives here the child dies
signal(SIGINT, handler);
//< if the signal arrives here then nothing happens except the handler is run
pause(); //< if the handler arrives here then pause gets interrupted
printf("signal was given\n");
exit(0);
}
To eliminate the race conditions, you need to
block the signal in the parent so that the child starts with the signal blocked
install the handler in the child
unblock the signal and pause() in one atomic step
To achieve 3. in one step, you need sigsuspend() instead of pause().
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
void handler(int Sig){}
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
signal(SIGINT, handler);
sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
sigsuspend(&oldmask);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
Alternatively, you can use sigwait() and drop the need for a handler altogether:
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
int sig; sigwait(&sigint,&sig);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
You have two issues:
The child process is getting a signal before it calls pause().
SIGINT by default would kill a process so printf will never be executed.
Try this:
void handler(int signum)
{
//nothing here
}
int main()
{
pid_t c = fork();
if (c == 0) {
signal(SIGINT, handler);
pause();
printf("signal was given");
}
if (c > 0) {
sleep(1); // <-- give the child process some time to pause()
kill(c, SIGINT);
}
return 0;
}
I written the below code to handle signals in separate thread to forcefully cleanup some resources and exit the complete process.
Here is the brief note about the below code.
When the signal is received, set volatile sig_atomic_t sig_set_flag = 1; inside signal handler.
In signal_handler_thread, checking sig_set_flag value in a loop.
if(sig_set_flag==1) send notifications like "i am going down" from signal_handler_thread and call exit(0); from the thread.
Signals can be received by any thread in a process. So i am setting the global variable.
I have 2 questions.
1) This implementation is fine? or i have to block the signals for the main thread and handle only by the spawned thread ?
2) How to block a signal to the main process and handle it in a thread?
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
/*
* Set this variable if any signal is received
*/
volatile sig_atomic_t sig_set_flag = 0;
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread
*/
void sig_term_handler(int sig_num) {
sig_set_flag = sig_num;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
while(1) {
if(sig_set_flag != 0) {
printf("%s : Signal flag is set for sig_no %d\n",__func__,sig_set_flag);
cleaup_resources();
break;
}
usleep(5);
}
exit(0);
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
struct sigaction sig;
sig.sa_handler = &sig_term_handler;
sig.sa_flags = 0;
sigaction(SIGTERM, &sig, NULL);
/*
* Spawn a thread to monitor signals.
* If signal received, Exit the process.
*/
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
Note:I know signals will interrupt the some system calls and EINTR will be set. Unfortunately some system calls (i.e) waitpid() will not be interrupted. So i spawned a thread to handle this scenario.
1) Your implementation seems to be correct. signal() and sigaction() register a handler function for the whole process, so it doesn't matter you call them in the main thread or in the spawned thread.
2) To block a signal in the main thread, and handle it in a thread, you have to design, not a handler function, but a handler thread, using sigwait() or sigwaitinfo(). So the thread will wait for the signals and the program execution won't be interrupted.
In this case, you have to block process-wide signals in all the threads, including the main thread. If it is not blocked, the signal will have the default behavior on the program.
You have to use pthread_sigmask() to block one or more signals. An example of code to block SIGTERM:
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGTERM);
pthread_sigmask(SIG_BLOCK,&set,NULL);
When a thread is created, it inherits of the blocked signals of the creator thread.
I modified your code to show you how to use sigwaitinfo() and pthread_sigmask():
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
siginfo_t info;
while(1) {
sigwaitinfo(&set,&info);
if(info.si_signo == SIGINT){
printf("\nSIGINT received\n");
cleaup_resources();
exit(0);
}
}
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
pthread_sigmask(SIG_BLOCK,&set,NULL);
// The new thread will inherit the blocked
// signals from the thread that create it:
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
Also, be careful of the fork(), from the tests I have done, the child process will inherit of the blocked signals.
If I setup and signal handler for SIGABRT and meanwhile I have a thread that waits on sigwait() for SIGABRT to come (I have a blocked SIGABRT in other threads by pthread_sigmask).
So which one will be processed first ? Signal handler or sigwait() ?
[I am facing some issues that sigwait() is get blocked for ever. I am debugging it currently]
main()
{
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
// Dont deliver SIGABORT while running this thread and it's kids.
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
pthread_create(&tAbortWaitThread, NULL, WaitForAbortThread, NULL);
..
Create all other threads
...
}
static void* WaitForAbortThread(void* v)
{
sigset_t signal_set;
int stat;
int sig;
sigfillset( &signal_set);
pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); // Dont want any signals
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT); // Add only SIGABRT
// This thread while executing , will handle the SIGABORT signal via signal handler.
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
stat= sigwait( &signal_set, &sig ); // lets wait for signal handled in CatchAbort().
while (stat == -1)
{
stat= sigwait( &signal_set, &sig );
}
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
// Abort signal handler executed via sigaction().
static void CatchAbort(int i, siginfo_t* info, void* v)
{
sleep(20); // Dont return , hold on till the other threads are down.
}
Here at sigwait(), i will come to know that SIGABRT is received. I will tell other threads about it. Then will hold abort signal handler so that process is not terminated.
I wanted to know the interaction of sigwait() and the signal handler.
From sigwait() documentation :
The sigwait() function suspends execution of the calling thread until
one of the signals specified in the signal set becomes pending.
A pending signal means a blocked signal waiting to be delivered to one of the thread/process. Therefore, you need not to unblock the signal like you did with your pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL) call.
This should work :
static void* WaitForAbortThread(void* v){
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigwait( &signal_set, &sig );
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
I got some information from this <link>
It says :
To allow a thread to wait for asynchronously generated signals, the threads library provides the sigwait subroutine. The sigwait subroutine blocks the calling thread until one of the awaited signals is sent to the process or to the thread. There must not be a signal handler installed on the awaited signal using the sigwait subroutine.
I will remove the sigaction() handler and try only sigwait().
From the code snippet you've posted, it seems you got the use of sigwait() wrong. AFAIU, you need WaitForAbortThread like below:
sigemptyset( &signal_set); // change it from sigfillset()
for (;;) {
stat = sigwait(&signal_set, &sig);
if (sig == SIGABRT) {
printf("here's sigbart.. do whatever you want.\n");
pthread_kill(tid, signal); // thread id and signal
}
}
I don't think pthread_sigmask() is really needed. Since you only want to handle SIGABRT, first init signal_set as empty then simply add SIGABRT, then jump into the infinite loop, sigwait will wait for the particular signal that you're looking for, you check the signal if it's SIGABRT, if yes - do whatever you want. NOTE the uses of pthread_kill(), use it to sent any signal to other threads specified via tid and the signal you want to sent, make sure you know the tid of other threads you want to sent signal. Hope this will help!
I know this question is about a year old, but I often use a pattern, which solves exactly this issue using pthreads and signals. It is a little length but takes care of any issues I am aware of.
I recently used in combination with a library wrapped with SWIG and called from within Python. An annoying issue was that my IRQ thread waiting for SIGINT using sigwait never received the SIGINT signal. The same library worked perfectly when called from Matlab, which didn't capture the SIGINT signal.
The solution was to install a signal handler
#define _NTHREADS 8
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // strerror
#define CallErr(fun, arg) { if ((fun arg)<0) \
FailErr(#fun) }
#define CallErrExit(fun, arg, ret) { if ((fun arg)<0) \
FailErrExit(#fun,ret) }
#define FailErrExit(msg,ret) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr); \
return ret; }
#define FailErr(msg) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr);}
typedef struct thread_arg {
int cpu_id;
int thread_id;
} thread_arg_t;
static jmp_buf jmp_env;
static struct sigaction act;
static struct sigaction oact;
size_t exitnow = 0;
pthread_mutex_t exit_mutex;
pthread_attr_t attr;
pthread_t pids[_NTHREADS];
pid_t tids[_NTHREADS+1];
static volatile int status[_NTHREADS]; // 0: suspended, 1: interrupted, 2: success
sigset_t mask;
static pid_t gettid( void );
static void *thread_function(void *arg);
static void signalHandler(int);
int main() {
cpu_set_t cpuset;
int nproc;
int i;
thread_arg_t thread_args[_NTHREADS];
int id;
CPU_ZERO( &cpuset );
CallErr(sched_getaffinity,
(gettid(), sizeof( cpu_set_t ), &cpuset));
nproc = CPU_COUNT(&cpuset);
for (i=0 ; i < _NTHREADS ; i++) {
thread_args[i].cpu_id = i % nproc;
thread_args[i].thread_id = i;
status[i] = 0;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutex_init(&exit_mutex, NULL);
// We pray for no locks on buffers and setbuf will work, if not we
// need to use filelock() on on FILE* access, tricky
setbuf(stdout, NULL);
setbuf(stderr, NULL);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (setjmp(jmp_env)) {
if (gettid()==tids[0]) {
// Main Thread
printf("main thread: waiting for clients to terminate\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
if (status[i] == 1)
printf("thread %d: terminated\n",i+1);
}
// On linux this can be done immediate after creation
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
else {
// Should never happen
printf("worker thread received signal");
}
return -1;
}
// Install handler
CallErr(sigaction, (SIGINT, &act, &oact));
// Block SIGINT
CallErr(pthread_sigmask, (SIG_BLOCK, &mask, NULL));
tids[0] = gettid();
srand ( time(NULL) );
for (i = 0; i < _NTHREADS; i++) {
// Inherits main threads signal handler, they are blocking
CallErr(pthread_create,
(&pids[i], &attr, thread_function,
(void *)&thread_args[i]));
}
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) {
fprintf(stderr, "main thread: can't block SIGINT");
}
printf("Infinite loop started - CTRL-C to exit\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
//printf("%d\n",status[i]);
if (status[i] == 2)
printf("thread %d: finished succesfully\n",i+1);
}
// Clean up and exit
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
static void signalHandler(int sig) {
int i;
pthread_t id;
id = pthread_self();
for (i = 0; i < _NTHREADS; i++)
if (pids[i] == id) {
// Exits if worker thread
printf("Worker thread caught signal");
break;
}
if (sig==2) {
sigaction(SIGINT, &oact, &act);
}
pthread_mutex_lock(&exit_mutex);
if (!exitnow)
exitnow = 1;
pthread_mutex_unlock(&exit_mutex);
longjmp(jmp_env, 1);
}
void *thread_function(void *arg) {
cpu_set_t set;
thread_arg_t* threadarg;
int thread_id;
threadarg = (thread_arg_t*) arg;
thread_id = threadarg->thread_id+1;
tids[thread_id] = gettid();
CPU_ZERO( &set );
CPU_SET( threadarg->cpu_id, &set );
CallErrExit(sched_setaffinity, (gettid(), sizeof(cpu_set_t), &set ),
NULL);
int k = 8;
// While loop waiting for exit condition
while (k>0) {
sleep(rand() % 3);
pthread_mutex_lock(&exit_mutex);
if (exitnow) {
status[threadarg->thread_id] = 1;
pthread_mutex_unlock(&exit_mutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&exit_mutex);
k--;
}
status[threadarg->thread_id] = 2;
pthread_exit(NULL);
}
static pid_t gettid( void ) {
pid_t pid;
CallErr(pid = syscall, (__NR_gettid));
return pid;
}
I run serveral tests and the conbinations and results are:
For all test cases, I register a signal handler by calling sigaction in the main thread.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A sleep, send target signal.
result: signal handler is executed in thread A.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A calls sigwait, send target signal.
result: sigwait is executed.
main thread does not block target signal, thread A does not block target signal, thread A calls sigwait, send target signal.
result: main thread is chosen and the registered signal handler is executed in the main thread.
As you can see, conbination 1 and 2 are easy to understand and conclude.
It is:
If a signal is blocked by a thread, then the process-wide signal handler registered by sigaction just can't catch or even know it.
If a signal is not blocked, and it's sent before calling sigwait, the process-wide signal handler wins. And that's why APUE the books require us to block the target signal before calling sigwait. Here I use sleep in thread A to simulate a long "window time".
If a signal is not blocked, and it's sent when sigwait has already been waiting, sigwait wins.
But you should notice that for test case 1 and 2, main thread is designed to block the target signal.
At last for test case 3, when main thread is not blocked the target signal, and sigwait in thread A is also waiting, the signal handler is executed in the main thread.
I believe the behaviour of test case 3 is what APUE talks about:
From APUE ยง12.8:
If a signal is being caught (the process has established a signal
handler by using sigaction, for example) and a thread is waiting for
the same signal in a call to sigwait, it is left up to the
implementation to decide which way to deliver the signal. The
implementation could either allow sigwait to return or invoke the
signal handler, but not both.
Above all, if you want to accomplish one thread <-> one signal model, you should:
block all signals in the main thread with pthread_sigmask (subsequent thread created in main thread inheris the signal mask)
create threads and call sigwait(target_signal) with target signal.
test code
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
FILE* file;
void* threadA(void* argv){
fprintf(file, "%ld\n", pthread_self());
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGUSR1);
int signo;
int err;
// sigset_t q;
// sigemptyset(&q);
// pthread_sigmask(SIG_SETMASK, &q, NULL);
// sleep(50);
fprintf(file, "1\n");
err = sigwait(&m, &signo);
if (err != 0){
fprintf(file, "sigwait error\n");
exit(1);
}
switch (signo)
{
case SIGUSR1:
fprintf(file, "SIGUSR1 received\n");
break;
default:
fprintf(file, "?\n");
break;
}
fprintf(file, "2\n");
}
void hello(int signo){
fprintf(file, "%ld\n", pthread_self());
fprintf(file, "hello\n");
}
int main(){
file = fopen("daemon", "wb");
setbuf(file, NULL);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = hello;
sigaction(SIGUSR1, &sa, NULL);
sigset_t n;
sigemptyset(&n);
sigaddset(&n, SIGUSR1);
// pthread_sigmask(SIG_BLOCK, &n, NULL);
pthread_t pid;
int err;
err = pthread_create(&pid, NULL, threadA, NULL);
if(err != 0){
fprintf(file, "create thread error\n");
exit(1);
}
pause();
fprintf(file, "after pause\n");
fclose(file);
return 0;
}
run with ./a.out & (run in the background), and use kill -SIGUSR1 pid to test. Do not use raise. raise, sleep, pause are thread-wide.
I need to intercept and trace signals from any binaries, like strace does it under linux.
I don't need a so verbose output like the real one strace.
I just want to know how it works, how can I intercept signal and how can I trace them.
Thanks in advance :)
strace uses the ptrace() system call for tracing, which also allows you to intercept (and possibly manipulate) signals sent to the process.
Here's a tiny example:
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/* simple example, child is traced, uses alarm which causes a signal to be
* set up */
pid_t child;
child = fork();
if (child == 0)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
alarm(3);
while(1)
{
}
exit(0);
}
/* parent */
while(1)
{
int wstatus;
int signum;
wait(&wstatus);
if (WIFEXITED(wstatus) || WIFSIGNALED(wstatus))
break;
signum = WSTOPSIG(wstatus);
printf("child stopped with signal %d\n", signum);
/* resume execution */
ptrace(PTRACE_CONT, child, NULL, signum);
}
return 0;
}
This is a simple implementation:
Put somewhere in your int main() several calls to signal(), one for each signal you want to catch. The first argument is the signal name; the second is the signal handler function (more on that below):
signal(SIGFPE, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGINT, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGTERM, SignalHandler);
#ifndef WIN32
signal(SIGHUP, SignalHandler);
signal(SIGQUIT, SignalHandler);
signal(SIGKILL, SignalHandler);
signal(SIGPIPE, SignalHandler);
signal(SIGCHLD, SignalHandler);
#endif
Now, write a signal function. It must return void and accept an int: void SignalHandler(int signal_number):
void SignalHandler(int signal_number)
{
printf("Received signal: %s\n", strsignal(signal_number);
// Do something
}
That's it! You can also test it by sending a signal to yourself with the function raise(SIGNAL_NAME); for example, try raise(SIGTERM);!
Intercepting signals to other processes is something you should not do for any reason other than debugging them. This is what strace is intended for. Processes should be capable of handling their own signals.
Needless to say, if you are writing a debugger, understand ptrace().