Основная проблема заключается в том, что restore original signal handler
выполняется слишком рано.
Я написал сценарий SystemTap для трассировки do_sigaction
и do_signal
, как показано ниже:
probe begin {
printf("start.\n");
}
probe kernel.function("do_signal") {
if ("test_sigaction" == execname()) {
printf("do_signal pid=%d\n", pid());
}
}
probe kernel.function("handle_signal") {
if ("test_sigaction" == execname()) {
printf("handle_signal pid=%d\n", pid());
}
}
probe kernel.function("do_sigaction").return {
if ("test_sigaction" == execname()) {
printf("do_sigaction: ret=%d sig=%d act=%p oact=%p pid=%d\n",
$return, @entry($sig), @entry($act),
@entry($oact), pid());
}
}
Результат:
do_sigaction: ret=0 sig=37 act=0xffffc90006ccbec8 oact=0xffffc90006ccbee8 pid=45920 //STEP1
do_sigaction: ret=0 sig=37 act=0xffffc90006ccbec8 oact=0x0 pid=45920 //STEP2
do_signal pid=45920 //STEP3
do_signal pid=45920
Причина:
- STEP1: установить обработчик сигнала на my_signal_handler в главном потоке
- STEP2: восстановить оригинальный обработчик сигнала в главном потоке
- STEP3: do_signal выполняется, когда дочерний поток возвращается из режима ядра в режим пользователя.
Очевидно, что здесь есть проблемы с параллелизмом.Можно восстановить обработчик сигнала перед выполнением do_signal.Параллельное управление требуется перед восстановлением обработчика сигнала или перемещением обработчика восстановления в функцию my_signal_handler, например:
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
struct sigaction oldHandler;
void my_signal_handler(int signo, siginfo_t *info, void *extra)
{
printf("my signal handler\n" );
// restore original signal handler
sigaction( SIGRTMIN + 3, &oldHandler, NULL );
}
int threadsupervisor(pthread_t thread_id)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = my_signal_handler;
sigaction(SIGRTMIN + 3, &action, &oldHandler );
// send signal to affected thread
pthread_kill(thread_id, SIGRTMIN + 3 );
return 0;
}
void *test_thread(void *args)
{
long loop = 0;
while(1) {
printf("sleep %ld\n", ++loop);
sleep(1);
}
return (void *)NULL;
}
int main()
{
pthread_t thread_id;
pthread_create(&thread_id, NULL, test_thread, NULL);
threadsupervisor(thread_id);
pthread_join(thread_id, NULL);
return 0;
}