精华内容
下载资源
问答
  • linux 多线程信号处理总结
    2017-11-09 09:20:23
    origin: https://www.cnblogs.com/cobbliu/p/5592659.html

    linux 多线程信号总结(一)

    1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知。也就是说,信号会随机发个该进程的一个线程。

    2 signal函数BSD/Linux的实现并不在信号处理函数调用时,恢复信号的处理为默认,而是在信号处理时阻塞此信号,直到信号处理函数返回。其他实现可能在调用信号处理函数时,恢复信号的处理为默认方式,因而需要在信号处理函数中重建信号处理函数为我们定义的处理函数,在这些系统中,较好的方法是使用sigaction来建立信号处理函数。

    3 发送信号给进程,哪个线程会收到?APUE说,在多线程的程序中,如果不做特殊的信号阻塞处理,当发送信号给进程时,由系统选择一个线程来处理这个信号。

    4 如果进程中,有的线程可以屏蔽了某个信号,而某些线程可以处理这个信号,则当我们发送这个信号给进程或者进程中不能处理这个信号的线程时,系统会将这个信号投递到进程号最小的那个可以处理这个信号的线程中去处理。

    5 如果我们同时注册了信号处理函数,同时又用sigwait来等待这个信号,谁会取到信号?经过实验,Linux上sigwait的优先级高。 

    6 在Linux中的posix线程模型中,线程拥有独立的进程号,可以通过getpid()得到线程的进程号,而线程号保存在pthread_t的值中。而主线程的进程号就是整个进程的进程号,因此向主进程发送信号只会将信号发送到主线程中去。如果主线程设置了信号屏蔽,则信号会投递到一个可以处理的线程中去。

    7 当调用SYSTEM函数去执行SHELL命令时,可以放心的阻塞SIGCHLD,因为SYSTEM会自己处理子进程终止的问题。 

    8 使用sleep()时,要以放心的去阻塞SIGALRM信号,目前sleep函数都不会依赖于ALRM函数的SIGALRM信号来工作。

     

     

    linux 多线程信号总结(二)

    1. 默认情况下,信号将由主进程接收处理,就算信号处理函数是由子线程注册的

    2. 每个线程均有自己的信号屏蔽字,可以使用sigprocmask函数来屏蔽某个线程对该信号的响应处理,仅留下需要处理该信号的线程来处理指定的信号。

    3. 对某个信号处理函数,以程序执行时最后一次注册的处理函数为准,即在所有的线程里,同一个信号在任何线程里对该信号的处理一定相同

    4. 可以使用pthread_kill对指定的线程发送信号

    APUE的说法:每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有的线程共享的,这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为后,所有的线程都共享这个处理行为的改变。这样如果一个线程选择忽略某个信号,而其他线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。

    进程中的信号是送到单个线程的,如果信号与硬件故障或者计时器超时有关,该型号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。

    sigprocmask的行为在多线程的进程中没有定义,线程必须使用pthread_sigmask

    总结:一个信号可以被没屏蔽它的任何一个线程处理,但是在一个进程内只有一个多个线程共用的处理函数。......

     

    linux 多线程信号总结(三)

    1 Linux 多线程应用中,每个线程可以通过调用pthread_sigmask() 设置本线程的信号掩码。一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如SIGSEGV;另外不能被忽略处理的信号SIGKILL 和SIGSTOP 也无法被阻塞。

    2 当一个线程调用pthread_create() 创建新的线程时,此线程的信号掩码会被新创建的线程继承。

    信号安装最好采用sigaction方式,sigaction,是为替代signal 来设计的较稳定的信号处理,signal的使用比较简单。signal(signalNO,signalproc);

    不能完成的任务是:1.不知道信号产生的原因;

    2.处理信号中不能阻塞其他的信号

    而signaction,则可以设置比较多的消息。尤其是在信号处理函数过程中接受信号,进行何种处理。

    sigaction函数用于改变进程接收到特定信号后的行为。

    4 sigprocmask函数只能用于单线程,在多线程中使用pthread_sigmask函数。

    5 信号是发给进程的特殊消息,其典型特性是具有异步性。

    6 信号集代表多个信号的集合,其类型是sigset_t。

    7 每个进程都有一个信号掩码(或称为信号屏蔽字),其中定义了当前进程要求阻塞的信号集。

    所谓阻塞,指Linux内核不向进程交付在掩码中的所有信号。于是进程可以通过修改信号掩码来暂时阻塞特定信号的交付,被阻塞的信号不会影响进程的行为直到该信号被真正交付。 

    忽略信号不同于阻塞信号,忽略信号是指Linux内核已经向应用程序交付了产生的信号,只是应用程序直接丢弃了该信号而已。

    10 sleep和nanosleep,如果没有在它调用之前设置信号屏蔽字的话,都可能会被信号处理函数打断。参见sleep和nanosleep的mannual。

     

    转自:http://blog.chinaunix.net/uid-12274566-id-3050955.html


    更多相关内容
  • 本文实例为大家分享了python多线程信号处理程序示例的具体代码,供大家参考,具体内容如下 下面是一个网上转载的实现思路,经过验证,发现是可行的,就记录下来。 思路 python多线程中要响应Ctrl+C的信号以杀死...
  • linux多线程信号处理

    千次阅读 2016-07-04 20:28:50
    在linux下,每个进程都有自己的signal mask,这个信号掩码指定哪个信号被阻塞,哪个不会...使用了多线程后,便有些疑问: 信号发生时,哪个线程会收到 是不是每个线程都有自己的mask及action 每个线程能按自己的方

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

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

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

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


    如果是异常产生的信号(比如程序错误,像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\n", tid);
        rc = sleep(60);
        if (rc != 0)
            printf("thread 1... interrupted at %d second\n", 60 - rc);
        printf("thread 1 ends\n");
        return NULL;
    }
     
    void *
    thr2_fn(void *arg)
    {
        struct sigaction    action;
        pthread_t       tid = pthread_self();
        int         rc, err;
       
        printf("thread 2 with tid:%lu\n", 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\n", 60 - rc);
        printf("thread 2 ends\n");
        return NULL;
    }
     
    void *
    thr3_fn(void *arg)
    {
        pthread_t   tid = pthread_self();
        sigset_t    mask;
        int     rc, err;
       
        printf("thread 3 with tid%lu\n", 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\n", 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\n", 60 - rc);
        printf("thread 3 ends\n");
        return NULL;
     
        return NULL;
    }
     
    int
    main(void)
    {
        int     rc, err;
        pthread_t   thr1, thr2, thr3, thrm = pthread_self();
     
        printf("thread main with pid %lu\n",thrm);
        err = pthread_create(&thr1, NULL, thr1_fn, NULL);
        if (err != 0) {
            printf("error in creating pthread:%d\t%s\n",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\t%s\n",err, strerror(rc));
            exit(1);
        }
         
        err = pthread_create(&thr3, NULL, thr3_fn, NULL);
        if (err != 0) {
            printf("error in creating pthread:%d\t%s\n",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\n");
        return 0;
    }
     
    void
    sighandler(int signo)
    {
        pthread_t   tid = pthread_self();
         
        printf("thread with pid:%lu receive signo:%d\n", tid, signo);
        return;
    }


    在上面的代码中,主线程创建三个线程。线程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$ 


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

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

    在第二行红色的地方,线程3第一次sleep终于完成,解除了对SIGALRM的阻塞。于是马上收到被阻塞的SIGALRM(发送3次,只收到一次)。PS:请注意信号阻塞与忽略的区别。


    pthread & signal 
     
    pthread线程和信号


    所有的异步信号发到整个进程的所有线程(异步信号如kill, lwp_kill, sigsend, kill等调用产生的都是,异步信号也称为中断),而且所有线程共享信号的处理行为(即sigaction的设置,对于同一信号的设置,某一线程的更改会影响到所有线程)。但每个线程可以有自己的mask来阻止信号的发送,所以可以通过线程对mask的设置来决定信号发送到哪个线程。设置mask的函数为:

    #include <signal.h>
    int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)

    此外,线程可以通过sleep(超过指定时间或调用的进程/线程捕捉到某个信号并从信号处理程序返回时,sleep返回)或者sigwait来等待一个或多个信号发生。

    #include <signal.h>
    int pthread_sigwait(const sigset_t *restrict set, int *restrict signop);

    给进程发送信号可以调用kill,同样给线程调用信号可以使用pthread_kill

    #include <signal.h>
    int pthread_kill(pthread_t thread, int signo);

    可以发送一个0的signo来检查线程是否存在,如果信号的默认行为是终止进程(例如SIGARLM),那么把该信号发送给某个线程会杀掉整个进程的所有线程。

    另外注意ALARM是进程资源,并且所有线程共享相同的ALARM,设置一个alarm()会发送SIGARLM信号给所有线程,所以他们不可能互补干扰的使用alarm()。
     
    here comes an example:
     

       /*threadsig.c*/ 
        #include <signal.h> 
        #include <pthread.h> 
        #include <stdio.h> 
         
        void sighandler(int signo); 
         
        void * 
        thr1_fn(void *arg) 
        { 
            pthread_t   tid = pthread_self(); 
            int     rc; 
         
            printf("thread 1 with tid:%u\n", tid); 
            rc = sleep(60); 
            if (rc != 0) 
                printf("thread 1... interrupted at %d second\n", 60 - rc); 
            printf("thread 1 ends\n"); 
            return NULL; 
        } 
         
        void * 
        thr2_fn(void *arg) 
        { 
            struct sigaction    action; 
            pthread_t       tid = pthread_self();   
            int         rc, err;   
           
            printf("thread 2 with tid:%u\n", 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\n", 60 - rc); 
            printf("thread 2 ends\n"); 
            return NULL; 
        } 
         
        void * 
        thr3_fn(void *arg) 
        { 
            pthread_t   tid = pthread_self();   
            sigset_t    mask;   
            int     rc, err;   
           
            printf("thread 3 with tid%u\n", 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(60); 
                if (rc != 0) 
                        printf("thread 3... interrupted at %d second\n", 60 - rc); 
                printf("thread 3 ends\n"); 
                return NULL; 
         
            return NULL; 
        } 
         
        int 
        main(void) 
        { 
            int     rc, err;   
            pthread_t   thr1, thr2, thr3, thrm = pthread_self(); 
         
            printf("thread main with pid %u\n", (unsigned int)thrm); 
            err = pthread_create(&thr1, NULL, thr1_fn, NULL); 
            if (err != 0) { 
                printf("error in creating pthread:%d\t%s\n",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\t%s\n",err, strerror(rc)); 
                exit(1); 
            } 
             
            err = pthread_create(&thr3, NULL, thr3_fn, NULL); 
            if (err != 0) { 
                printf("error in creating pthread:%d\t%s\n",err, strerror(rc)); 
                exit(1); 
            } 
         
            sleep(3); 
            pthread_kill(thr1, SIGALRM); 
            pthread_kill(thr2, SIGALRM); 
            pthread_kill(thr3, SIGALRM); 
            pthread_join(thr1, NULL);   /*wait for the threads to complete.*/ 
            pthread_join(thr2, NULL); 
            pthread_join(thr3, NULL); 
            printf("main ends\n"); 
            return 0; 
        } 
         
        void  
        sighandler(int signo) 
        { 
            pthread_t   tid = pthread_self(); 
             
            printf("thread with pid:%u receive signo:%d\n", tid, signo); 
            return; 
        } 
    luffy@luffy-laptop:~/workspace/myapue$ ./threadsig 
    thread main with pid 3557979936
    thread 1 with tid:3549923072
    thread 2 with tid:3541530368
    thread 3 with tid3533137664
    thread with pid:3549923072 receive signo:14
    thread with pid:3541530368 receive signo:14
    thread 2... interrupted at 3 second
    thread 1... interrupted at 3 second
    thread 1 ends
    thread 2 ends    #then wait for 27 seconds and thread-3 ends
    thread 3 ends
    main ends

     
    thr2设置的信号处理程序sighandler也应用到其他线程,thr3由于设置mask所有阻塞了SIGARLM信号。

     
    Reference:
    APUE


    Linux线程信号 

    1. 概念


        按照 POSIX, 异步 (外部) 信号发送到整个进程.
        所有线程共享同一个设置, 即通过 sigaction 设置的线程处置方法.
        每个线程有自己的信号掩码, 线程库根据该掩码决定将信号发送到哪个线程.
        由于Linux 线程实现上的独特性, 外部信号始终发送到特定的线程.  

    2. 例子

    #include <pthread.h>
    #include <stdio.h>
    #include <sys/signal.h>
    #define NUMTHREADS 3
    void sighand(int signo);
    void *threadfunc(void *parm)
    {
        pthread_t             tid = pthread_self();
        int                   rc;
        printf("Thread %u entered/n", tid);
        rc = sleep(30); /* 若有信号中断则返回剩余秒数 */
        printf("Thread %u did not get expected results! rc=%d/n", tid, rc);
        return NULL;
    }
    void *threadmasked(void *parm)
    {
        pthread_t             tid = pthread_self();
        sigset_t              mask;
        int                   rc;
        printf("Masked thread %lu entered/n", tid);
        sigfillset(&mask); /* 将所有信号加入mask信号集 */
        /* 向当前的信号掩码中添加mask信号集 */
        rc = pthread_sigmask(SIG_BLOCK, &mask, NULL);
        if (rc != 0)
        {
            printf("%d, %s/n", rc, strerror(rc));
            return NULL;
        }
        rc = sleep(15);
        if (rc != 0)
        {
            printf("Masked thread %lu did not get expected results! rc=%d /n", tid, rc);
            return NULL;
        }
        printf("Masked thread %lu completed masked work/n", tid);
        return NULL;
    }
    int main(int argc, char **argv)
    {
        int                     rc;
        int                     i;
        struct sigaction        actions;
        pthread_t               threads[NUMTHREADS];
        pthread_t               maskedthreads[NUMTHREADS];
        printf("Enter Testcase - %s/n", argv[0]);
        printf("Set up the alarm handler for the process/n");
        memset(&actions, 0, sizeof(actions));
        sigemptyset(&actions.sa_mask); /* 将参数set信号集初始化并清空 */
        actions.sa_flags = 0;
        actions.sa_handler = sighand;
        /* 设置SIGALRM的处理函数 */
        rc = sigaction(SIGALRM,&actions,NULL);
        printf("Create masked and unmasked threads/n");
        for(i=0; i<NUMTHREADS; ++i)
        {
            rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
            if (rc != 0)
            {
                printf("%d, %s/n", rc, strerror(rc));
                return -1;
            }
            rc = pthread_create(&maskedthreads[i], NULL, threadmasked, NULL);
            if (rc != 0)
            {
                printf("%d, %s/n", rc, strerror(rc));
                return -1;
            }
        }
        sleep(3);
        printf("Send a signal to masked and unmasked threads/n");
         /* 向线程发送SIGALRM信号 */
        for(i=0; i<NUMTHREADS; ++i)
        {
            rc = pthread_kill(threads[i], SIGALRM);
            rc = pthread_kill(maskedthreads[i], SIGALRM);
        }
        printf("Wait for masked and unmasked threads to complete/n");
        for(i=0; i<NUMTHREADS; ++i) {
            rc = pthread_join(threads[i], NULL);
            rc = pthread_join(maskedthreads[i], NULL);
        }
        printf("Main completed/n");
        return 0;
    }
    void sighand(int signo)
    {
        pthread_t             tid = pthread_self();
        printf("Thread %lu in signal handler/n", tid);
        return;
    }


    3. 打印结果

    Enter Testcase - ./test
    Set up the alarm handler for the process
    Create masked and unmasked threads
    Thread 3085065104 entered
    Masked thread 3076672400 entered
    Thread 3068279696 entered
    Masked thread 3059886992 entered
    Thread 3051494288 entered
    Masked thread 3043101584 entered
    Send a signal to masked and unmasked threads
    Thread 3085065104 in signal handler
    Thread 3085065104 did not get expected results! rc=27
    Thread 3068279696 in signal handler
    Thread 3068279696 did not get expected results! rc=27
    Thread 3051494288 in signal handler
    Thread 3051494288 did not get expected results! rc=27
    Wait for masked and unmasked threads to complete
    Masked thread 3076672400 completed masked work
    Masked thread 3059886992 completed masked work
    Masked thread 3043101584 completed masked work
    Main completed
     

    4. 相关函数 

    sigaction(查询或设置信号处理方式)


    #include<signal.h>
    int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);

    sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。


    如参数结构sigaction定义如下

    struct sigaction
    {
       void (*sa_handler) (int);
       sigset_t sa_mask;
       int sa_flags;
       void (*sa_restorer) (void);
    }

    sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal()。
    sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置。
    sa_restorer 此参数没有使用。
    sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。

     
    sigfillset(将所有信号加入此信号集)

    #include<signal.h>
    int sigfillset(sigset_t * set);
     
    sigfillset()用来将参数set信号集初始化,然后把所有的信号加入到此信号集里。
     
    sigemptyset(初始化信号集)  

    #include<signal.h>
    int sigemptyset(sigset_t *set);
     
    sigemptyset()用来将参数set信号集初始化并清空。
     
    pthread_sigmask(更改或检查调用线程的信号掩码)
     
    #include <pthread.h>
    #include<signal.h>
    int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
     
    how用来确定如何更改信号组,可以为以下值之一:

        SIG_BLOCK:向当前的信号掩码中添加new,其中new表示要阻塞的信号组。
        SIG_UNBLOCK:向当前的信号掩码中删除new,其中new表示要取消阻塞的信号组。
        SIG_SETMASK:将当前的信号掩码替换为new,其中new表示新的信号掩码。

    pthread_kill(向线程发送信号)
     
    #include <pthread.h>
    #include<signal.h>
    int pthread_kill(thread_t tid, int sig);
     
    pthread_kill()将信号sig发送到由tid指定的线程。tid所指定的县城必须与调用线程在同一个进程中。

    展开全文
  •  Linux多线程环境中的信号处理不同于进程的信号处理。一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理。  本文首先介绍信号处理在进程中和线程间的...
  • 易语言-易语言多线程控制:信号量控制线程数量
  • QT多线程信号槽机制关键点

    千次阅读 多人点赞 2020-06-24 16:29:18
    多线程下qt注意点 一些qt心得知识点 1)一个带入点问题 这个问题,重在搞明白QT的信号、槽函数在何时、何地、由谁发出、由谁执行。 不要小看这个例子,笔者见过一些“用QT工作过五六年”的人士,被问...

    本文档将介绍如下内容:

    1. QT 信号槽机制
    2. 多线程下qt注意点
    3. 一些qt心得知识点

     

     

    1)一个带入点问题

     

    这个问题,重在搞明白QT的信号、槽函数在何时、何地、由谁发出、由谁执行。

     

    不要小看这个例子,笔者见过一些“用QT工作过五六年”的人士,被问到该问题时还是“王顾左右而言他”,不知道该怎么回答。可以想象,这些人只能算处于使用 QT的初级阶段,连核心问题的门都还没有摸到。

     

    在回答这个问题前,我们必须要介绍一些基础知识。

     

    2)对象属于哪个线程

     

    给出一个代码片段,借此说明问题:
     

    class MyThread:public QThread
    
    {
    
    MyThread(){p1 = new A()} //p1对象在旧线程
    
    void run(){p2 = new A()}//p2对象在新线程
    
    }
    
    void mian() {
    
    MyThread thread1; //thread1对象在旧线程中
    
    thread1.start();
    
    }
    

    QT 多线程下只有QThread::run() 函数是在新线程中。

     

    run中new的对象,是在新线程中。

    除此以外,构造函数中new的对象,线程对象本身,还是在旧线程中。

    由于MyThread的构造函数还是在主线程中调用的,所以p1是在主线程中。

     

    这几点非常关键。

     

    3)教科书中令人疑惑的qt connect第五个参数

    第五个参数代表槽函数在哪个线程中执行 :

    1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。

    2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行,即槽函数和信号发送者在同一线程

    3)队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行,即槽函数与信号接受者在同一线程

    4)锁定队列连接(QueuedConnection)

    Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

    5)单一连接(QueuedConnection)

    Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接

    注意:此处教科书说法“可能”有歧义错误。后续我们会纠正这里的错误。

    如果你第一次看到这样的说法,有没有对“发送者”这个概念感到疑惑。或者说,仅仅用这里的描述,你可以回答最开篇我们的提问吗?

     

    4)回到例子

     

    在开篇的例子中,我们加入一个新的限定,信号和槽采用直连接方式。

     

    1)pa 属于主线程

    2)采用直接连接方式

     

    信号发送者是谁,是主线程还是子线程,还是其它什么“东西” ?

     

    注:不清楚这个概念,无法清晰回答左边三个问题,就还不算完全理解了多线程下信号槽机制。

     

    答案:信号的发送者是线程,不是其它“东西”,而且是子线程。由于是直连方式,槽函数是在子线程中调用。

    何时调用?类似函数指针的方式,在emit提交的时候,直接类似调用“函数指针”的方式立刻在子线程中执行。

     

    5)纠正教科书的说法

    第五个参数代表槽函数在哪个线程中执行 :

    1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是“发送信号的线程”与“接受者所在的线程”是同一线程,等同于直接连接;如果“发送信号的线程”与“接受者所在的线程”不是一个线程,等同于队列连接。

    2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在“发送信号的线程”中执行,即槽函数和“信号发送线程”在同一线程

    3)队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行,即槽函数与"信号接受者所在线程"在同一线程

    4)锁定队列连接(QueuedConnection)

    Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后“发送信号的线程”会阻塞,直到槽函数运行完。“接收者所在线程”和“发送信号的线程”绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

    5)单一连接(QueuedConnection)

    Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

     

     

    注意点:

     

    “发送信号的线程”就是指代码中调用 emit 信号时的执行线程;而不是“信号所在对象“所属的线程。

    三个要素,来决定槽函数在哪个线程调用。

     

    1. 调用emit 发送信号的线程。
    2. 接受者对象所在的线程。
    3. connect的第五个参数。

     

    注:槽函数在何处执行是动态决定的,而不是在写connect函数时(编译时)决定的。

     

    6)槽函数何时执行

     

    每个线程均有自己的消息事件循环队列

    直连方式,是所谓立即执行,就是函数直接调用。

    队列方式,不会立即执行,分单/多线程情况:

    单线程(发送、接收同一线程):消息放入消息队列。线程进入消息队列时,依次执行队列上消息对应槽函数。

    跨线程(发送、接收不同线程):消息放入接收者线程队列。接收者线程运行时(可能阻塞、睡眠、或退出),进入它的消息队列后,依次执行队列上的消息。这也意味着,同一个槽函数不会重入,不用考虑重入互斥访问,因为都在队列上排队等待依次执行(注意不要乱用postEvents函数,后续我们有机会单独讲一讲该问题)。

     

    7)一个错误示范

    class Thread :public QThread{
    
    public:
    
    Thread();
    
    Thread(Test *outObj) { m_outObj = outObj; };
    
    virtual ~Thread();
    
    protected:
    
    void run() {
    
    emit m_outObj->sig_test(); }
    
    private:
    
    Test * m_outObj;
    
    };
    
    
    
    int main(int argc, char *argv[])
    
    {
    
    QCoreApplication a(argc, argv);
    
    Test *t = new Test;
    
    QObject::connect(t, &Test::sig_test, t, &Test::slot_test, Qt::QueuedConnection);
    
       Thread *thread = new Thread(t);
    
    t->moveToThread(thread);
    
    thread->start();
    
    return a.exec();
    
    }

     

    如果你完全理解了上述要素,这个例子会有什么问题吗?

     

    分析如下:

    1. 上例是队列连接,槽函数会在接受者所在线程(who),等到该线程回到事件循环后(when)执行槽函数。
    2. T 调用moveToThread后,t作为接受者,已经被移到到子线程中去。
    3. 子线程执行start后,run中调用emit信号触发后,子线程不会立刻执行槽函数;而是等到子线程回到事件循环后,才会执行槽函数。但是子线程此时会直接退出;不会再有机会回到子线程的事件循环。

    最终导致槽函数没有机会再去执行。

     

    8)QT多线程编程注意点

    1 QT主线程不能阻塞。因为UI在主线程中,阻塞则界面卡死。使用while()QCoreApplication::processEvents(); qapp->processEvents()替换睡眠操作。[重要]

     

    2 非主线程不能操作UI控件,否则QT崩溃。这也是要分离界面与逻辑的重要原因。[重要]

     

    3 父子QObject对象,必须在同一个线程;不同线程的对象,不能是父子关系。

    否则会报错,或者产生未知情况。[重要]

     

    4 官方优先推荐使用moveToThread方式,其次使用继承QThread方式。理解后都一样。

     

    5 注意槽函数是在线程中执行。如果执行线程睡眠、阻塞,槽函数没有机会执行。如果你的槽函数要快速响应,不要让它在可能阻塞或睡眠的线程中。[重要]

     

    6 要注意volidate修饰共享变量、要注意加锁。不同的锁行为会导致线程不同状态,得根据线程业务状态去考虑用什么锁。[重要]

     

    7 活用慎用processEvents

    线程(包括主线程和其它线程)执行很繁重的计算任务中,为防止消息事件一直无机会处理,则在函数中手动调用processEvents,让消息事件有机会更新。界面不会假死。

    另一方面,槽函数中不可调用processEvents,因为这会导致“中断”当前槽函数,进而去执行消息队列中后续的槽函数,可能会引发同一槽函数重入问题,将编程问题复杂化。

     

    8 new Qobject对象哪些需要手动释放?

    一个QObject对象,如果有父节点,则可以不手动delete释放,父节点释放时会自动去释放所有子节点;反之没有父节点,须手动调用delete释放。

    delete QObject时,会把对象从父节点摘掉;再删除并释放它的所有子节点。一些addChild操作,会主动把对象加入父节点。父子关系不是可有可无的,会涉及到对象内存回收问题,要做到心中有数。

     

    1. 尾语

    最近面试过一些号称做过多年QT开发的程序员,有些连QT第5个参数要么没听过,要么听过却没有深入理解原理。可想而知,这些人在平时工作中要么没有深入思考,要么没有深入刨根问底。或许更多的人处于没有机会去触及这些本质问题。因为他们实在太忙了,忙于低水平的原地重复。

    写此文,纯粹是为了“治病救人”。限于本人水平有限,如有错误,还请赐教讨论。

    展开全文
  • 多线程信号

    千次阅读 2019-04-11 10:10:38
    在一个单进程中,信号会打断进程的执行,并且递送到进程中处理,而对于多线程环境,信号会递送给其中的一个线程,这个被递送的线程是不确定的。每个线程存在自己的信号屏蔽字,可以通过如下函数设置: int pthread_...

    多线程环境的信号递送

    在一个单进程中,信号会打断进程的执行,并且递送到进程中处理,而对于多线程环境,信号会递送给其中的一个线程,这个被递送的线程是不确定的。每个线程存在自己的信号屏蔽字,可以通过如下函数设置:

    int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
    

    该函数设置线程对应的信号屏蔽字,被屏蔽的信号会被挂起从而无法递送到对应的线程。这里有一点需要特别注意,线程的信号屏蔽字是可以向下继承的,主线程设置的信号屏蔽字可以被后续创建的线程继承。

    信号的阻塞

    进程环境中sigprocmask可以用来阻塞进程中信号的递送,而pthread_sigmask可以阻塞线程中的信号递送,阻塞信号的可以阻塞几乎所有的信号,但是有两个信号除外:SIGKILL和SIGSTOP。
    另外为了保证程序的健壮性,一些必要的错误信号最好在程序编写时也不要阻塞,比如:

    TSIGFPE,SIGILL,SIGSEGV,SIGBUS
    

    这些信号的到来都表示此进程已经处于不可控的状态,比如算术运算错误或者内存引用错误,或者是硬件错误等。

    sigwait同步处理

    由于每个线程都可能接收到信号,当进程收到信号时,如果是多线程的情况,我们是无法确定是哪一个线程处理这个信号,因此多线程的异步信号处理就显得很复杂,而sigwait可以实现信号的同步处理。sigwait是从进程被阻塞而挂起的信号中,接收指定的信号,多线程环境一般都会设置一个线程sigwait专门处理信号,那么所有线程都必须要先屏蔽要处理的信号,可以利用继承关系方便达到此目的。

    #include <signal.h>
    int sigwait(const sigset_t *set, int *sig);
    

    需要注意的是sigwait不改变当前线程的信号屏蔽字,只是从pending的信号中取出信号去处理,因而不会触发进程的signal handler。sigwait的第一个参数是一个信号集,指的含义是该函数所等待的信号集,它不会修改信号屏蔽字。
    如果信号集中不存在的信号,即使它是处于pending状态,sigwait依然不会返回。

    系统调用的中断

    在多线程环境中,只有接收了信号的线程,阻塞的系统调用才会中断返回,而未接受的线程,系统调用不受影响,因此我们使用sigwait专门线程处理信号,会使系统调用的使用更加简单,工作线程肯定不会接收信号,因此不用担心被信号中断。

    信号的两种处理方式

    多线程环境下,信号处理有两种方式:

    1. 依然使用进程的信号处理程序处理,对于多线程环境下,signal和sigaction的处理函数是所有线程共享的,在任一个线程修改,都会影响整个进程。如果有信号递送到了该进程,它依然会有效触发,只是它所打断的线程是不确定的。
    2. sigwait来处理,如果信号被sigwait所接收,那么它肯定是挂起的,也就是说此信号一定是被阻塞递送的,因此signal或者sigaction处理函数不会处理它。

    假想如下场景:多线程环境中,如果有一个信号,我们不想使用sigwait来处理,而使用进程默认的signal handler去处理,而其他信号都想被一个线程sigwait同步处理,那要如果实现呢?

    1. 该信号想要触发signal handler,一定要能够被递送到进程,或者说一定要存在一个线程允许递送它,也就是不能完全阻塞。
    2. 想要被sigwait接收的信号,一定是pending信号,那么一定要先阻塞对应的信号。
    3. 为了防止工作线程系统调用被打断,创建一个线程处理信号,保证signal handler还是sigwait都在该特定线程运行。
      示例代码:
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    #include <sys/stat.h>
    #include <sys/resource.h>
    #include <pthread.h>
    #include "include/debug.h"
    
    
    void alarm_func(int signo)
    {
    	pr_info("SIGALRM receive!\n");
    }
    
    void *thr_fn(void *arg)
    {
    	int err, signo;
    	sigset_t	set,mask;
    
    	signal(SIGALRM, alarm_func);
    
    	sigfillset(&mask);
    	sigdelset(&mask, SIGALRM);
    	if ((err = pthread_sigmask(SIG_SETMASK, &mask, NULL)) != 0)
    		err_exit("SIG_BLOCK error\n");
    
    	sigfillset(&set);
    	sigdelset(&set, SIGALRM);
    	for (;;) {
    		err = sigwait(&set, &signo);
    		if (err != 0) {
    			err_exit("sigwait failed\n");
    		}
    
    		switch (signo) {
    		case SIGTERM:
    			pr_info("Catch SIGTERM; exiting\n");
    			break;
    		case SIGQUIT:
    			pr_info("Catch SIGQUIT; exiting\n");
    			exit(0);
    			break;
    		case SIGINT:
    			pr_info("Catch SIGINT; exiting\n");
    			break;
    
    		default:
    			pr_info("Unexpected signal %d\n", signo);
    		}
    	}
    	return(0);
    }
    
    pthread_t setup_signal_thread(void)
    {
    	int err;
    	struct sigaction	sa;
    	sigset_t	mask;
    	pthread_t	tid;
    
    	/*
    	 * Block all signals in main thread
    	 */
    	sigfillset(&mask);
    	if ((err = pthread_sigmask(SIG_SETMASK, &mask, NULL)) != 0)
    		err_exit("SIG_BLOCK error\n");
    
    	/*
    	 * Create a child thread to handle SIGHUP and SIGTERM.
    	 */
    	err = pthread_create(&tid, NULL, thr_fn, 0);
    	if (err != 0)
    		err_exit("can't create thread\n");
    	return tid;
    }
    
    int main()
    {
    	char buf[10];
    	pthread_t tid;
    	void *tret;
    
    	tid = setup_signal_thread();
    
    	pr_info("signal thread created\n");
    	read(STDIN_FILENO, buf, 10);
    	pr_info("read was interrupted!\n");
    }
    

    该示例在工作线程(主线程中)阻塞了所有的信号,这样后续创建的线程都会继承该屏蔽字。新创建的信号线程处理两种情况:

    1. SIGALRM 信号使用signal handler来处理,因此该线程的信号屏蔽字必须没有SIGALRM。
    2. 其他信号使用sigwait来处理,sigwait接收的信号必须是线程阻塞的信号。
      运行结果如下:
    $ ./syscall_intr 
    [5216] INFO:  signal thread created
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  SIGALRM receive!
    [5216] INFO:  Unexpected signal 6
    [5216] INFO:  Unexpected signal 6
    [5216] INFO:  Unexpected signal 6
    [5216] INFO:  Catch SIGQUIT; exiting
    
    
    展开全文
  • SIGNALFD :http://www.chineselinuxuniversity.net/man-pages/man2/signalfd.2.html linux信号signal处理函数:... Linux 多线程应用中如何编写安全的信号处理函数:http
  • 多线程下的信号处理

    千次阅读 2015-08-28 16:45:57
    2.sigprocmask用于但线程的进程中阻止信号发送,但是在多线程的进程中并没有定义 线程必须使用pthread_sigmask. 如果有的线程可以屏蔽了某个信号,而某些线程可以处理这个信号,则当我们发送这个信号给进程或者进程...
  • Linux 多线程应用中如何编写安全的信号处理函数
  • [linux专题]基于linux线程信号处理

    千次阅读 2021-11-28 17:51:07
    目录 1.linux线程 1.1 基本介绍 1.2 线程信息 1.3 线程创建 1.4 线程终止 1.5 线程指定数据 1.5 线程同步 2.linux 信号与信号处理 2.1 信号介绍 2.2 信号种类 2.3 信号处理 2.4 信号发送 2.5 信号屏蔽 1.linux线程 ...
  • python多线程下的信号处理程序示例

    千次阅读 2015-06-27 07:25:43
    python多线程中要响应Ctrl+C的信号以杀死整个进程,需要: 1.把所有子线程设为Daemon; 2.使用isAlive()函数判断所有子线程是否完成,而不是在主线程中用join()函数等待完成; 3.写一个响应Ctrl+C信号的函数,修改...
  • PyQt多线程将使用QThread函数,QThread是Qt的线程类中最核心的底层类。
  • Linux高级编程——线程信号处理

    千次阅读 2017-09-27 12:08:04
    Linux 多线程环境中的信号处理不同于进程的信号处理。一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理。  本文首先介绍信号处理在进程中和线程间的不同...
  • 在Linux的多线程中使用信号机制,与在进程中使用信号机制有着根本的区别,可以说是完全不同。在进程环境中,对信号的处理是,先注册信号处理函数,当信号异步发生时,调用处理函数来处理信号。它完全是异...
  • linux多线程信号总结

    2019-05-17 11:13:24
    同一个进程中的所有线程共享一份信号处理函数(注:一个信号对应于一个信号函数,没有则调用默认的),任何线程注册信号函数都会覆写之前的信号函数。所以最终进程中的信号函数是由最后一次注册信号函数的线程决定的...
  • 多线程信号的接受处理

    千次阅读 2014-09-05 16:34:33
    最近刚从linux C转做android,老大突然看着我闲,叫我去验证一下“一个进程有多个子线程,子线程都注册监听某个信号,另一个进程向它发送该...大体得出,如果一个多线程的进程得到了信号,它是会在它诸多子线程里面选一
  • Qt多线程中的信号与槽

    万次阅读 多人点赞 2017-09-29 19:47:15
    1. Qt对象的依附性和事务循环  ...  (1) 线程开始运行时发射的信号void started()  (2) 线程完成运行时发射的信号void finished()  (3) 线程被异常终止时发射的信号void terminated()  多线程中的信号与槽
  • Linux线程信号

    千次阅读 2018-12-01 15:17:30
    信号处理函数必须在多线程进程的所有线程之间共享, 但是每个线程要有自己的挂起信号集合和阻塞信号掩码。 POSIX函数kill/sigqueue必须面向进程, 而不是进程下的某个特定的线程。 每个发给多线程应用的信号仅递...
  • 多线程常见的几种同步方式分为以下几点: 只是阐述相关性,具体代码逻辑,网上一抓一大片,再次不在详述。 1、信号量:主要用于线程之间的数据同步,比如A线程数据处理完成之后会立马通知B线程进行相应的操作。常见...
  • 1.使用信号量完成多线程顺序处理

    千次阅读 2018-04-18 17:53:52
    信号量的主要函数有:int sem_init(sem_t *sem,int pshared,...sem_init用于对指定信号初始化,pshared为0,表示信号在当前进程的线程之间共享,value表示初始化信号的值。 sem_wait可以用来阻塞当前线程...
  • PyQt5多线程以及信号的使用

    千次阅读 2018-10-08 10:58:26
    借用并修改了一段代码,原来这个代码只有从后台向前台发送信号emit的代码,因为次点击窗口的submit按键会起条进程,比较乱,而每条进程的emit都是在一个死循环中不停的进行,所以我希望再启动后一条进程时,将前...
  • Linux多线程3-4_向线程发送信号

    千次阅读 2019-12-26 21:10:11
    一、发送信号的函数 int pthread_kill(pthread_t thread, int sig); 1、别被名字吓到,pthread_kill可不是kill,而是向线程发送signal。...​ 2、向指定ID的线程发送sig信号,如果线程代码内不做处理,则...
  • 100-多线程信号

    千次阅读 2017-03-17 15:15:56
    不知道你是否还记得之前在进程中的信号处理时,提到过阻塞信号集与未决信号集的概念,如果你已经忘记了,请参考《阻塞信号与未决信号》一文回忆一下。1. 多线程程序中的信号在多线程中,每一个线程都有属于自己的...
  • 多线程并发之Semaphore(信号量)使用详解

    万次阅读 多人点赞 2019-01-02 17:04:03
    多线程并发之CountDownLatch(闭锁)使用详解 多线程并发之显示锁Lock与其通信方式Condition源码解读 多线程并发之读写锁(ReentranReadWriteLock&amp;amp;amp;amp;ReadWriteLock)使用详解 多线程并发之线程池...
  • 程序逻辑很简单,就是让一个独立线程发送一个信号,UI线程的槽响应,但是运行报错居然是找不到信号 No such signal ,但是编译可以通过,moc文件也有这个信号。大侠们指点一下子,谢谢 #include "dialog.h" #...
  • 相关技术版本 python 3.8.2 PyQt5 5.15.4 PyQt5-Qt5 5.15.2 ...1. 自定义线程 from PyQt5.QtCore import pyqtSignal, QThread class ThreadUtils(QThread): # 数据信号 data_signal = pyqtSignal(dict) .
  • 1.sigaction() 函数是进程的信号处理函数 返回值:若成功则返回0,若出错则返回-1 该函数有三个参数 第一个参数:信号 第二个参数:指向struct sigaction类型的结构体指针 该结构体中含有,信号屏蔽字、信号集...
  • 从Ctrl-C看Python多线程信号处理

    千次阅读 2017-03-06 17:38:53
    也许你会偶然发现Python的多线程程序使用Ctrl-C杀不掉,必须拿到pid用kill -9才能干掉,研究这个问题的原因可以使得对Python多线程信号处理及线程的退出机制有更好的理解。 假如有一个Python写成的用多线程模拟...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 199,027
精华内容 79,610
关键字:

多线程信号处理