Многопоточная обработка сигналов в C на Linux

Общий вопрос: что не так с моим кодом, чтобы все генерируемые сигналы не перехватывались двумя потоками обработчика?

Неудачные детали моего плохого вопроса: я должен написать некоторый код с основной функцией, 3 потока генератора для генерации сигналов типа sig1 и sig2 и два потока обработки сигналов. Я пытался решить эту проблему с помощью кода, показанного ниже, но я сталкиваюсь с некоторыми ошибками. Я пытался использовать sigaction с sigwaitinfo и sigwait для перехвата сигналов. Но оба метода, похоже, не работают правильно. В прикрепленном коде handler1 использует sigaction и sigwaitinfo, handler2 использует sigwait. Но я пытался, чтобы оба обработчика использовали любой из них, и мои результаты никогда не были такими, как я полагаю, они должны быть. Кажется, что некоторые сигналы никогда не улавливаются. Что не так с моим кодом, чтобы все сигналы не перехватывались? Вот пример вывода

Пример вывода

сигнал 1 получен

сигнал 2 получен

сигнал 1 получен

сигнал 2 получен

сигнал 2 получен

sigSent1==2,sigSent2==7,sigReceived1==2,sigReceived2==3

Желаемый результат будет

Возможный желаемый выход

сигнал 1 получен

сигнал 2 получен

сигнал 1 получен

сигнал 2 получен

сигнал 2 получен

сигнал 1 получен

сигнал 2 получен

сигнал 1 получен

сигнал 2 получен

sigSent1==4,sigSent2==5,sigReceived1==4,sigReceived2==5

Извините, если этот вопрос задает много, но я действительно понятия не имею, почему не все сигналы перехватываются, они гуглили и проверяли это примерно 6 часов сегодня и 3 часа вчера, а также просматривали справочные страницы... Я может быть упущено что-то очевидное...

#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>

/*
   Pre-definitions of functions
 */
void generator();
void handler1();
void handler2();
void reporter();
/*
   Global Variables 
 */
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;
int receivedSignal1=0;
int receivedSignal2=0;
sem_t s_lock;
sem_t r_lock;
sigset_t set;
pthread_mutex_t lock;
pthread_t tid[5];
/*
   Main function
 */
int main(int argc, char ** argv)
{
    int i=0;
    int randomNum=0;
    int error;
    int pid;
    sigset_t mask_all,mask_one,prev_one;
    //Setting up signals 
    //Get Random time
    time_t now;
    time(&now);
    //semaphore is initialized to be global and val 1
    sem_init(&s_lock,0,1);
    sem_init(&r_lock,0,1);
    srand((unsigned) time(&now));
    //Blakc in main thread
    sigemptyset(&set);
    sigaddset(&set,SIGUSR1);
    sigaddset(&set,SIGUSR2);
    pthread_sigmask(SIG_BLOCK,&set,NULL);
    pthread_sigmask(SIG_BLOCK,&set,NULL);
    //Loops until more threads created than 2
    while(i<3)
    {   error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
        if(error!=0)
        {
            printf("failed to create thread\n");
        }
        i++;
    }//end while loop
    while(i<5)
    {
        error=pthread_create(&tid[3],NULL,(void*)handler1,NULL);
        if(error!=0)
        {
            printf("failed to create thread\n");
        }
        error=pthread_create(&tid[4],NULL,(void*)handler2,NULL);
        if(error!=0)
        {
            printf("failed to create thread \n");
        }
        i++;
    }
    //join the threads so main won't return
    i=0;
    int returnVal;
    sleep(10);
    printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
    while(i<5)//Loops until threads are joined
    {
        //  printf("gonna join %d\n",i);
        pthread_join(tid[i],NULL);
        /*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
          {
          printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
          }*/
        i++;
    }//end while
    return 0;
}//end of main function
/*
   Generator threads
 */
void generator()
{
    sleep(1);
    int i=3;
    int randomNum=0;
    int val=0;
    int total_signal_c=9997;
    while(total_signal_c<10000)
    {
        usleep(1);
        //Randomly select to generate SIGUSR1 or SIGUSR2
        //Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
        //  printf("total_signal_count%d\n",total_signal_c);
        //Create either a sig1 signal or sig2 signal
        randomNum=rand()%2;
        switch(randomNum)
        {
            case 0:
                val=pthread_kill(tid[3],SIGUSR1);
                if(val!=0)
                {
                    printf("kill fail ==%d\n",val);
                }
                sem_wait(&s_lock);
                //semaphore
                //mutex
                sentSignal1++;
                sem_post(&s_lock);
                break;
            case 1:
                val=pthread_kill(tid[4],SIGUSR2);
                if(val!=0)
                {
                    printf("kill fail2\n");
                }
                sem_wait(&s_lock);
                sentSignal2++;
                sem_post(&s_lock);
                //
                //
                break;
        }

        i++;
        total_signal_c++;
        //delay for a random time, 0.01 to 0.1 second
    }
}
/*
   Handler 1 threads
 */
void  handler1()
{
    //Setting up signals
    //  printf("In handler1\n");
    struct sigaction s;
    siginfo_t info;
    sigemptyset(&s.sa_mask);
    //use signal to perma block for handler2
    signal(SIGUSR2,handler1);
    //Add Sigusr1 to set
    sigaddset((&s.sa_mask),SIGUSR1);
    pthread_sigmask(SIG_BLOCK,&s.sa_mask,NULL);
    int val=-1;
    //use signal(), sigaddset(), pthread_sigmask() etc to block and unblock signals as required.
    while(1)
    {   //use sigwaitinfo(); to receive a signal
        val=-1;
        val=sigwaitinfo(&s.sa_mask,&info);
        //if signal received modify the corresponding counter
        if(info.si_signo==SIGUSR1){
            //increment semaphore lock
            sem_wait(&r_lock);
            receivedSignal1++;
            //decrement semaphore lock
            sem_post(&r_lock);
            printf("signal 1 received\n");
        }
        if(val==-1)     
        {
            //      break;
        }
    }
    pthread_exit(NULL);
}
/*
   Handler2 threads
 */
void handler2()
{
    int sigInfo=0;
    //use signal to perma block for handler2
    signal(SIGUSR1,handler2);
    int val=-1;
    while(1)
    {       //use sigwaitinfo(); to receive a signal
        val=-1;
        val=sigwait(&set,&sigInfo);
        //if signal received modify the corresponding counter
        if(sigInfo==SIGUSR2){
            //increment semaphore lock
            sem_wait(&r_lock);
            receivedSignal2++;
            //decrement semaphore lock
            sem_post(&r_lock);
            printf("signal 2 received\n");
        }       
    }
    pthread_exit(NULL);
}

2 ответа

Решение

Некоторые сигналы могут быть потеряны, если есть ожидающий сигнал с тем же кодом. Из спецификации sigaction:

Если последующее вхождение ожидающего сигнала генерируется, это зависит от реализации того, доставлен или принят сигнал более одного раза в обстоятельствах, отличных от тех, в которых требуется организация очереди в соответствии с опцией Расширения сигналов в реальном времени. Порядок, в котором несколько одновременно ожидающих сигналов вне диапазона от SIGRTMIN до SIGRTMAX доставляются или принимаются процессом, не уточняется.

Если вы хотите поймать все сигналы, у вас есть два решения:

  • Используйте сигналы реального времени со значением от SIGRTMIN до SIGRTMAX вместо SIGUSR1 и SIGUSR2. И то и другое pthread_sigqueue() а также pthread_kill() не сможет отправить сигнал, если SIGQUEUE_MAX ожидающие сигналы или если у системы недостаточно ресурсов для постановки в очередь сигнала.
  • Подождите, пока прецедент сигнал был пойман, прежде чем отправить еще один.

РЕДАКТИРОВАТЬ:

1. Некоторые объяснения, чтобы ответить на ваш последний комментарий.

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

Я думаю, что вы хотите, чтобы вы обрабатывали один тип сигнала, например, SIGUSR1, используя signal() и функция-обработчик, и обрабатывать SIGUSR2 с помощью потока sigwaitinfo(),

С помощью signal() вам не нужно блокировать сигналы, которые вы хотите перехватить, и это можно сделать в главном потоке.

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

Вы можете взглянуть на исходный код, который я вставил в конце этого поста.

2. Больше точности.

Чтобы заблокировать сигнал без установки функции автоматического захвата / обработки, вы должны использовать sigprocmask() в однопоточной программе, или pthread_sigmask() в многопоточной программе. Вы также можете использовать sigaction() чтобы заблокировать некоторые входящие сигналы во время выполнения функции обработчика сигнала.

Что касается перехвата сигнала, существует два способа перехвата сигнала:

  • Функция обработчика сигнала зарегистрирована signal() (или же sigaction()) и автоматически вызывается при получении сигнала, если только сигнал не был заблокирован во всех потоках. Чтобы сделать signal() работать, вы должны позволить хотя бы одному потоку, который не блокирует сигнал. Вы не должны использовать sigwait() обрабатывать сигнал, потому что программа будет автоматически ожидать параллельного выполнения.

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

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

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

3. Некоторые объяснения того, что делает ваша программа.

  • В основном потоке сигналы SIGUSR1 и SIGUSR2 блокируются, поэтому все потоки, созданные основным потоком после этой блокировки, будут заблокированы этими сигналами, поскольку они наследуют маску создаваемого потока.

    Когда вы звоните signal() это зарегистрирует функции handler1() а также handler2() в качестве функций обработки сигналов, вызываемых потоком сигналов. Но эти сигналы заблокированы для всех потоков, поэтому handler1() а также handler2() не будет вызываться как функция обработчика сигнала. Итак, используя signal() в твоей программе бесполезно.

    Более того, handler1() а также handler2() предназначены для обработки потоков, а не для функций обработки сигналов. Таким образом, вы не должны регистрировать их signal(), вы должны зарегистрировать непотоковые функции.

  • Вы должны увеличивать счетчики для отправленных сигналов только тогда, когда pthread_kill() не удалось

  • При создании потоков обработки программа создает 2 бесполезных потока, потому что цикл выполняется для i = 3 а также i = 4и вы создаете 2 потока в этом цикле. Итак, правильный код while(i < 4)или лучше убери петлю.

4. Я изменил вашу программу, чтобы ловить SIGUSR1, используя signal():

  • Вы увидите, что нужно только заблокировать SIGUSR2 в handler2_thread(), Никаких других блокировок в программе не требуется.

  • В этом коде вы увидите разницу между потоком обработки и функцией обработчика сигнала, сигналы, полученные thread1 обрабатываются функцией обработчика сигнала handler1_func()в то время как сигналы получены handler2_thread обрабатываются в самой теме.

  • Переменная receivedSignal1_flag объявлен volatile и типа sig_atomic_t потому что существует условие гонки между потоком, который проверяет и сбрасывает его, и функцией-обработчиком, которая устанавливает его 1, Используя этот способ, некоторые пойманные сигналы не будут учитываться. Относительно того, что я прочитал на sig_atomic_tЯ не уверен, что можно увеличить счетчик receivedSignal1 прямо в handler1_func() потому что операция приращения не атомарна, и поэтому может быть нарушена другим обработчиком сигнала. Но, возможно, это возможно, если handler_func() единственный обработчик сигнала для чтения и записи receivedSignal1 и объявив это volatile а также sig_atomic_t, Также обратите внимание, что receivedSignal1_flag не заблокирован ни семафором, ни мьютексом, потому что его использует только один поток.


#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>

/*
   Pre-definitions of functions
 */
void generator();
void handler1_func(int);
void thread1();
void handler2_thread();
void reporter();
/*
   Global Variables 
 */
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;



///////////////////////////////////////
//
// receivedSignal1_flag is volatile and
// sig_atomic_t because there is a race
// condition on it (used in the signal
// handler, and in the thread).
//
///////////////////////////////////////
volatile sig_atomic_t receivedSignal1_flag;



int receivedSignal1=0;
int receivedSignal2=0;

sem_t s_lock;
sem_t r_lock;

pthread_mutex_t lock;
pthread_t tid[5];
/*
   Main function
 */
int main(int argc, char ** argv)
{

    int i=0;
    int randomNum=0;
    int error;
    int pid;
    sigset_t mask_all,mask_one,prev_one;
    //Setting up signals 
    //Get Random time
    time_t now;
    time(&now);
    //semaphore is initialized to be global and val 1
    sem_init(&s_lock,0,1);
    sem_init(&r_lock,0,1);
    srand((unsigned) time(&now));
    //Loops until more threads created than 2
    while(i<3)
    {   error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
        if(error!=0)
        {
            printf("failed to create thread\n");
        }
        i++;
    }//end while loop

    error=pthread_create(&tid[3],NULL,(void*)thread1,NULL);
    if(error!=0)
    {
        printf("failed to create thread\n");
    }
    error=pthread_create(&tid[4],NULL,(void*)handler2_thread,NULL);
    if(error!=0)
    {
       printf("failed to create thread \n");
    }

    //join the threads so main won't return
    i=0;
    int returnVal;
    sleep(15);
    printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
    while(i<5)//Loops until threads are joined
    {
        //  printf("gonna join %d\n",i);
        pthread_join(tid[i],NULL);
        /*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
          {
          printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
          }*/
        i++;
    }//end while
    return 0;
}//end of main function
/*
   Generator threads
 */
void generator()
{
    sleep(5);
    int i=3;
    int randomNum=0;
    int val=0;
    int total_signal_c=9990;
    while(total_signal_c<10000)
    {
        usleep(1);
        //Randomly select to generate SIGUSR1 or SIGUSR2
        //Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
        //  printf("total_signal_count%d\n",total_signal_c);
        //Create either a sig1 signal or sig2 signal
        randomNum=rand()%2;
        switch(randomNum)
        {
            case 0:
                /////////////////////////////////////////
                // Send SIGUSR1 to thread1
                /////////////////////////////////////////
                val=pthread_kill(tid[3],SIGUSR1);
                if(val!=0)
                {
                    printf("\nkill fail ==%d",val);
                } else {
                  sem_wait(&s_lock);
                  //semaphore
                  //mutex
                  sentSignal1++;
                  sem_post(&s_lock);
                }
                break;
            case 1:
                /////////////////////////////////////////
                // Send SIGUSR2 to handler2_thread
                /////////////////////////////////////////
                val=pthread_kill(tid[4],SIGUSR2);
                if(val!=0)
                {
                    printf("\nkill fail2");
                } else {
                  sem_wait(&s_lock);
                  sentSignal2++;
                  sem_post(&s_lock);
                  //
                  //
                }
                break;
        }

        i++;
        total_signal_c++;
        //delay for a random time, 0.01 to 0.1 second
    }
}




//////////////////////////////////////////
//
// Signal handler function for SIGUSR1:
//
//////////////////////////////////////////
void  handler1_func(int signo)
{
  // write on stdout using an async-signal-safe function:
  write(STDOUT_FILENO,"\nSignal handler function: SIGUSR1 caught\n",41);
  // set the received signal flag to 1:
  if(signo == SIGUSR1) receivedSignal1_flag = 1;
}


/////////////////////////////////////////////////////////////
//
// The thread that will receive SIGUSR1 but not handle it
// because handler1_func() will handle it automatically:
//
/////////////////////////////////////////////////////////////
void  thread1()
{
    //////////////////////////////////////////////
    //
    // register handler1_func() as signal handler
    // for the whole process, not only the thread.
    // It means that if another thread doesn't 
    // block SIGUSR1 and receive it, then
    // handler1_func() will also be called:
    //
    //////////////////////////////////////////////
    signal(SIGUSR1,handler1_func);

    while(1)
    {   
        ///////////////////////////////////////////////////
        // If a signal has been handled by handler1_func()
        // then receivedSignal1_flag = 1.
        // And so increment receivedSignal1 and print.
        ///////////////////////////////////////////////////
        if(receivedSignal1_flag == 1) {
          // reset the flag:
          receivedSignal1_flag = 0;

          sem_wait(&r_lock);
          receivedSignal1++;
          printf("\nThread1: SIGUSR1 received and handled by handler1_func()\n");
          sem_post(&r_lock);
        }

    }
    pthread_exit(NULL);
}





////////////////////////////////////////
//
// Handling thread for SIGUSR2:
//
////////////////////////////////////////
void handler2_thread()
{
    ///////////////////////////////////////////////
    //
    // Need to block SIGUSR2 in order to avoid
    // the default handler to be called.
    //
    ///////////////////////////////////////////////
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGUSR2);
    pthread_sigmask(SIG_BLOCK,&set,NULL);

    siginfo_t info;
    int val=-1;
    while(1)
    {       
        val=-1;
        val=sigwaitinfo(&set,&info);
        //if signal received modify the corresponding counter
        if(info.si_signo==SIGUSR2){
            //increment semaphore lock
            sem_wait(&r_lock);
            receivedSignal2++;
            //decrement semaphore lock
            printf("\nhandler2_thread: signal 2 received\n");
            sem_post(&r_lock);
        }       
    }
    pthread_exit(NULL);
}
  1. Только асинхронно-безопасные функции могут быть безопасно вызваны из обработчика сигнала. sigwait() а также sigwaitinfo() не безопасны для асинхронных сигналов. См. 2.4 Концепции сигналов на http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html. Также смотрите Linux signal.7 справочная страница. Также printf() асинхронный сигнал безопасности.

  2. призвание pthread_exit() в обработчике сигналов неопределенное поведение. Это прервет поток - но в контексте обработки сигналов, потенциально вызывая значительные проблемы. Следующие вопросы только начинают касаться проблем, с которыми звонят pthread_exit() в обработчике сигнала причина: pthread_exit () в обработчике сигнала и как правильно завершить поток в обработчике сигнала? Смотрите также http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html

По сути, ваш код запутан. Ты начинаешь handler1() а также handler2() как отдельные потоки, затем зарегистрируйте те же функции в качестве обработчиков сигналов, а затем вызовите sigwait() / sigwaitinfo() в пределах функций.

Учитывая, как код объединяет потоки, обработчики сигналов, while(1)... петли, почти невозможно даже начать догадываться, что происходит. Например, вы можете получать потоки, которые порождают обработчики сигналов, которые застревают в бесконечных циклах.

Эта строка кода:

signal(SIGUSR1,handler2);

означает, что когда SIGUSR1 получено, handler2() будет вызван в контексте сигнала - но handler2() имеет while(1) цикл в нем...

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

Другие вопросы по тегам