Linux上C语言中的多线程信号处理

时间:2016-11-01 04:04:47

标签: c multithreading pthreads signals signal-handling

广泛的问题:我的代码出了什么问题,以致所有生成的信号都没有被两个处理程序线程捕获?

我可怜的问题的不幸细节:我应该编写一些带有main函数的代码,3个生成器线程来生成sig1和sig2类型信号以及两个信号处理线程。我尝试使用下面显示的代码解决这个问题,但我遇到了一些错误。我尝试使用sigwaitinfo和sigwait的sigaction来捕获信号。但这两种方法似乎都无法正常工作。在附加的代码中,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 个答案:

答案 0 :(得分:2)

当存在具有相同代码的待处理信号时,某些信号可能会丢失。从sigaction的规范:

  

如果生成后续发生的待处理信号,则在实时信号扩展选项下需要排队的情况以外的情况下,是否依次实现信号是否被传送或接受。未指定SIGRTMIN到SIGRTMAX范围之外的多个同时挂起信号被传递到流程或由流程接受的顺序。

如果你想捕获所有信号,你有两个解决方案:

  • 使用具有SIGRTMIN到SIGRTMAX值的实时信号,而不是SIGUSR1和SIGUSR2。如果pthread_sigqueue()信号未决,或者系统没有足够的资源对信号进行排队,pthread_kill()SIGQUEUE_MAX都将无法发送信号。
  • 等待之前发现的先前信号发送另一个信号。

编辑:

1。一些解释,以回答你的上一个评论。

您不能使用signal()阻止信号,您可以忽略它(使用SIG_IGN而不是处理函数)或注册处理函数。使用处理函数,我想我们可以说信号被阻止并被捕获。

我认为你的t.a.希望你处理一种类型的信号,例如SIGUSR1,使用signal()和处理函数,并使用sigwaitinfo()处理带有线程的SIGUSR2。

使用signal()您不需要阻止要捕获的信号,而且可以在主线程中完成。

使用sigwaitinfo(),您需要阻止至少在接收它的线程中捕获的信号。

您可以查看我在本文末尾粘贴的源代码。

2。更精确。

要在不放置自动catch / handler函数的情况下阻止信号,您必须在单线程程序中使用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 = 3i = 4执行的,并且您在此循环中创建了2个线程。所以正确的代码是while(i < 4),或者更好地删除循环。

4。我修改了你的程序,以便使用signal()

来捕获SIGUSR1
  • 您将看到它只需要阻止handler2_thread()中的SIGUSR2。该计划不需要其他阻止。

  • 在此代码中,您将看到处理线程和信号处理函数之间的区别,thread1接收的信号由信号处理函数handler1_func()处理,而信号handler2_thread的receveid在线程本身处理。

  • 变量receivedSignal1_flag声明为volatile且类型为sig_atomic_t,因为在检查和重置它的线程与设置的处理函数之间存在竞争条件它到1。使用这种方式,一些被捕获的信号不会被计算在内。关于我在sig_atomic_t上所阅读的内容,我不确定是否可以直接在receivedSignal1中递增计数器handler1_func(),因为增量操作不是原子的,因此可能会受到另一个信号处理程序的干扰。但也许有可能handler_func()是唯一一个读写receivedSignal1并声明volatilesig_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 :(得分:2)

  1. 只能从信号处理程序安全地调用异步信号安全函数。 sigwait()sigwaitinfo() async-signal-safe。请参阅http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html处的 2.4信号概念see the Linux signal.7 man page也是pthread_exit() in signal handler。 <{1}} async-signal-safe也不是。

  2. 在信号处理程序中调用printf()是未定义的行为。它将终止线程 - 但在信号处理环境中,可能会导致重大问题。以下问题刚刚开始涉及在信号处理程序中调用pthread_exit()导致的问题:How to properly terminate a thread in a signal handler?http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html另请参阅{{3}}

  3. 从根本上说,您的代码很困惑。您将pthread_exit()handler1()作为单独的线程启动,然后将相同的函数注册为信号处理程序,然后在函数内调用handler2() / sigwait()

    考虑到代码组合线程,信号处理程序,sigwaitinfo()循环的方式,甚至不可能开始猜测发生了什么。例如,你可能会获得产生陷入无限循环的信号处理程序的线程。

    这行代码:

    while(1)...

    表示收到signal(SIGUSR1,handler2); 时,系统会在信号上下文中调用SIGUSR1 - 但handler2()中有handler2()个循环...

    异步信号处理是一个难以掌握的概念。我要说你需要从比试图相互发信号的多个线程更简单的东西开始。