Подтвердить что ты не робот

Обработка сигналов в pthreads

Я создал pthread и установил в нем обработчик сигналов так же, как и в main( ). Обработчик сигнала потока является отдельной функцией. Удивительно, но он не работает, то есть обработчик сигнала нити не способен поймать сигналы.

Вот код:

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 printf("Caught signal: %d\n",sig);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 printf("This is from thread function\n");
 signal(SIGSEGV,sig_func); // Register signal handler inside thread
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data *ptr;

 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 printf("Name:%s\n",ptr->name);
 printf("Age:%d\n",ptr->age);
}

Вывод:

Ошибка сегментации (что означает, что сигнал не пойман обработчиком)

4b9b3361

Ответ 1

В вашем коде есть несколько проблем:

  • ptr не инициализируется, поэтому все части ptr-> будут разбивать программу
  • вы вызываете pthread_kill() немедленно, очень вероятно, до того, как обработчик сигнала был установлен, и в потоке (который имеет неуказанное поведение).
  • вы вызываете printf() из обработчика сигнала, который не гарантированно работает (см. man 7 signal для списка безопасных функций)

Это будет работать намного лучше, хотя вам все равно нужна правильная синхронизация потоков, и, как указано в другом месте, вы должны использовать sigaction():

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 write(1, "Caught signal 11\n", 17);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 fprintf(stderr, "This is from thread function\n");
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data d;
 data *ptr = &d;

 signal(SIGSEGV,sig_func); // Register signal handler before going multithread
 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 sleep(1); // Leave time for initialisation
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 fprintf(stderr, "Name:%s\n",ptr->name);
 fprintf(stderr, "Age:%d\n",ptr->age);
}

Изменить: установить sighandler в основной поток

Ответ 2

Я считаю, что суть проблемы заключается в том, что сигналы передаются процессу в целом, а не отдельным потокам. Как правило, один поток назначается для обработки всех сигналов; все остальные потоки (включая основной поток) должны блокировать сигналы с помощью pthread_sigmask().

Вы можете установить маску для блокировки всех сигналов, запустить поток обработчика сигнала, разобрать сигналы, которые вы хотите обработать, а затем вернуться в основной поток, запустить все остальные потоки, которые вам нужны. Они наследуют маску "блокировать все сигналы" из основного потока.

Кстати, пришло время отойти от signal(3) и переключиться на sigaction(2), который имеет надежную семантику и лучше стандартизован. (И, следовательно, более портативный.)

Ответ 3

Одна проблема с вашим кодом, о которой никто еще не упомянул, заключается в том, что, хотя блокировка сигнала (и доставка, если вы используете pthread_kill или raise), относятся к потоку, обработчики сигналов - это процессы. Это означает, что они являются очень плохим механизмом для межпоточной связи, особенно если ваш код когда-либо будет использоваться в качестве кода библиотеки, поскольку это крайне плохое поведение для библиотеки, чтобы изменить обработчики сигналов вызывающего абонента.

Также обратите внимание, что использование обработчиков сигналов для связи между потоками имеет субоптимальную производительность по сравнению с другими методами сигнализации потока, такими как переменные условий или барьеры, поскольку существует как минимум один дополнительный переход пользователя-ядра-пользователя (когда обработчик сигнала возвращается).