zoukankan      html  css  js  c++  java
  • 线程与信号处理

    参考这篇文章:

    http://www.cnblogs.com/coding-my-life/p/4782529.html

    在linux下,每个进程都有自己的signal mask,这个信号掩码指定哪个信号被阻塞,哪个不会被阻塞,通常用调用sigmask来处理。同时每个进程还有自己的signal action,这个行为集合指定了信号该如何处理,通常调用sigaction来处理。

    使用了多线程后,便有些疑问:

    1. 信号发生时,哪个线程会收到
    2. 是不是每个线程都有自己的mask及action
    3. 每个线程能按自己的方式处理信号么

    首先,信号的传递是根据情况而定的:

    • 如果是异常产生的信号(比如程序错误,像SIGPIPE、SIGEGV这些),则只有产生异常的线程收到并处理。
    • 如果是用pthread_kill产生的内部信号,则只有pthread_kill参数中指定的目标线程收到并处理。
    • 如果是外部使用kill命令产生的信号,通常是SIGINT、SIGHUP等job control信号,则会遍历所有线程,直到找到一个不阻塞该信号的线程,然后调用它来处理。(一般从主线程找起),注意只有一个线程能收到。

     其次,每个线程都有自己独立的signal mask,但所有线程共享进程的signal action。这意味着,你可以在线程中调用pthread_sigmask(不是sigmask)来决定本线程阻塞哪些信号。

    但你不能调用sigaction来指定单个线程的信号处理方式。如果在某个线程中调用了sigaction处理某个信号,那么这个进程中的未阻塞这个信号的线程在收到这个信号都会按同一种方式处理这个信号。

    另外,注意子线程的mask是会从主线程继承而来的。

    问:每个线程能按自己的方式处理信号么?

    第三个问题,因为signal action共享的问题,已经知道不能

    来个例子:

    /*threadsig.c*/
    #include <signal.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void sighandler(int signo);
     
    void *
    thr1_fn(void *arg)
    {
        struct sigaction    action;
        action.sa_flags = 0;
        action.sa_handler = sighandler;
           
        sigaction(SIGINT, &action, NULL);
        
        pthread_t   tid = pthread_self();
        int     rc;
     
        printf("thread 1 with tid:%lu
    ", tid);
        rc = sleep(60);
        if (rc != 0)
            printf("thread 1... interrupted at %d second
    ", 60 - rc);
        printf("thread 1 ends
    ");
        return NULL;
    }
     
    void *
    thr2_fn(void *arg)
    {
        struct sigaction    action;
        pthread_t       tid = pthread_self();
        int         rc, err;
       
        printf("thread 2 with tid:%lu
    ", tid);
         
        action.sa_flags = 0;
        action.sa_handler = sighandler;
           
        err = sigaction(SIGALRM, &action, NULL);
         
        rc = sleep(60);
        if (rc != 0)
            printf("thread 2... interrupted at %d second
    ", 60 - rc);
        printf("thread 2 ends
    ");
        return NULL;
    }
     
    void *
    thr3_fn(void *arg)
    {
        pthread_t   tid = pthread_self();
        sigset_t    mask;
        int     rc, err;
       
        printf("thread 3 with tid%lu
    ", tid);
     
         
        sigemptyset(&mask); /* 初始化mask信号集 */
       
        sigaddset(&mask, SIGALRM);
        err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
        if (err != 0)
        {
            printf("%d, %s/n", rc, strerror(rc));
            return NULL;
        }
       
        rc = sleep(10);
        if (rc != 0)
            printf("thread 3... interrupted at %d second
    ", 60 - rc);
        err = pthread_sigmask( SIG_UNBLOCK,&mask,NULL );
        if ( err != 0 )
        {
            printf("unblock %d, %s/n", rc, strerror(rc));
            return NULL;
        }
        
        rc = sleep(10);
        if (rc != 0)
            printf("thread 3... interrupted at %d second after unblock
    ", 60 - rc);
    
        printf("thread 3 ends
    ");
        return NULL;
     
        return NULL;
    }
     
    int
    main(void)
    {
        int     rc, err;
        pthread_t   thr1, thr2, thr3, thrm = pthread_self();
     
        printf("thread main with pid %lu
    ",thrm);
        err = pthread_create(&thr1, NULL, thr1_fn, NULL);
        if (err != 0) {
            printf("error in creating pthread:%d	%s
    ",err, strerror(rc));
            exit(1);
        }
     
         
    /*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2 set the signal handler, then the whole process will be terminated*/
        err = pthread_create(&thr2, NULL, thr2_fn, NULL);
        if (err != 0) {
            printf("error in creating pthread:%d	%s
    ",err, strerror(rc));
            exit(1);
        }
         
        err = pthread_create(&thr3, NULL, thr3_fn, NULL);
        if (err != 0) {
            printf("error in creating pthread:%d	%s
    ",err, strerror(rc));
            exit(1);
        }
     
        sleep(10);
        //内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
        pthread_kill(thr1, SIGALRM);
        pthread_kill(thr2, SIGALRM);
        pthread_kill(thr3, SIGALRM);
        pthread_kill(thr3, SIGALRM);
        pthread_kill(thr3, SIGALRM);
        sleep(5);
        pthread_join(thr1, NULL);   /*wait for the threads to complete.*/
        pthread_join(thr2, NULL);
        pthread_join(thr3, NULL);
        printf("main ends
    ");
        return 0;
    }
     
    void
    sighandler(int signo)
    {
        pthread_t   tid = pthread_self();
         
        printf("thread with pid:%lu receive signo:%d
    ", tid, signo);
        return;
    }
    View Code

    在上面的代码中,主线程创建三个线程。线程1注册SIGINT信号(即ctrl+c) ,线程2注册SIGALRM,线程三则是先阻塞SIGALRM,然后解除阻塞。

    编译后看运行结果:

    xzc@xzc-HP-ProBook-4446s:~/code/test$ gcc -o threadsig threadsig.c -pthread
    xzc@xzc-HP-ProBook-4446s:~/code/test$ ./threadsig 
    thread main with pid 139946922108736
    thread 2 with tid:139946905396992
    thread 1 with tid:139946913789696
    thread 3 with tid139946897004288
    ^Cthread with pid:139946922108736 receive signo:2
    thread with pid:139946913789696 receive signo:14
    thread 1... interrupted at 4 second
    thread 1 ends
    thread with pid:139946905396992 receive signo:14
    thread 2... interrupted at 4 second
    thread 2 ends
    ^Cthread with pid:139946922108736 receive signo:2
    ^Cthread with pid:139946922108736 receive signo:2
    thread with pid:139946897004288 receive signo:14
    thread 3 ends
    main ends
    xzc@xzc-HP-ProBook-4446s:~/code/test$
    View Code

    在第一行红色的地方,主线程正在sleep,我按下ctrl+c,只有主线程收到并处理了信号。说明进程会从主线程开始查找不阻塞该信号的线程来处理job control类的信号。

    由于主线程sleep被打断,随后向三个线程发送了SIGALRM,线程1、2由于没有阻塞该信号,被迫从sleep中醒来,并结束线程。线程3仍在sleep。

    在第二行红色的地方,线程3第一次sleep终于完成,解除了对SIGALRM的阻塞。于是马上收到被阻塞的SIGALRM(发送3次,只收到一次)。

    PS:请注意信号阻塞与忽略的区别。

    也就是没有被处理的信号,在阻塞解除之后,仍然是要被处理的。这个从内核对信号的处理流程也能够推断出来,因为

    进程的task_struct结构中有关于本进程中未决信号的数据成员: struct sigpending pending:

    struct sigpending{

            struct sigqueue *head, *tail;

            sigset_t signal;

    };

    这里面的signal里面记录了新的SIGALAM,始终没有线程会处理,也就始终还留在这里。

  • 相关阅读:
    volley框架使用
    Insert Interval
    candy(贪心)
    Best Time to Buy and Sell Stock
    Best Time to Buy and Sell Stock III
    distinct subsequences
    edit distance(编辑距离,两个字符串之间相似性的问题)
    trapping rain water
    word break II(单词切分)
    sudoku solver(数独)
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6263150.html
Copyright © 2011-2022 走看看