工作点滴(五)Linux多线程编程中的信号问题

来源:互联网 发布:jar软件网站 编辑:程序博客网 时间:2024/06/02 04:16

0 引言

由于工作保密原因,只简单介绍下背景:

本系统中有一个进程A,A中又分别生成5个线程分别为a,b,c,d和e。

其中,

a是主线程(也可以叫做进程A),采用epoll(select)通过socket负责和其他进程间的通信。

b是计时器线程,通过alarm调用产生信号中断来进行计时。(主要是通过类似如下代码进行)

 signal(SIGALRM,timer_handle);    if(setitimer(ITIMER_REAL,&gTimerVal,NULL) == -1)        printf("setitimer error! ");


c,d和e是其他工作线程。

1 问题产生

如果初始化的时候将b线程(计时器线程)打开。

在a线程的socket的通信中就会产生“Interrupted system call”(中文的意思:系统调用被中断)。

如果关闭b线程,该bug就不会产生。

2 分析问题

通过研究epoll及signal原理,发现是由于多线程调用的信号问题产生。

3 解决问题

通过在a线程中设置中断屏蔽即可解决,同时不影响其他线程接受signal。

代码如下:

                ...        struct sigaction action;  bzero(&action,sizeof(action));action.sa_sigaction = siga_handler;  sigset_t set;  sigemptyset(&set)  sigaddset(&set,SIGALRM); //sigprocmask(SIG_BLOCK,&set,NULL);pthread_sigmask(SIG_BLOCK,&set,NULL);action.sa_mask = set;  action.sa_flags = SA_SIGINFO;  // if(sigaction(SIGINT,&action,NULL)==-1)perror("error"),exit(-1); if(sigaction(SIGALRM,&action,NULL)==-1)perror("error"),exit(-1);         ...
4 总结

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

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

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

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

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

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

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

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

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

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

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

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

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

5 程序实例

#include <stdio.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#include <pthread.h>struct sigaction act;void catchctrlc(int signo){char msg[]="Ctrl-C entered!\n";char errmsg[]="Failed to restore SIGINT\n";int msglen = sizeof(msg);int emsglen = sizeof(errmsg);act.sa_handler = SIG_DFL;write(STDERR_FILENO,msg,msglen);if(sigaction(SIGINT,&act,NULL) == -1)write(STDERR_FILENO,errmsg,emsglen);}int main(){act.sa_handler = catchctrlc;act.sa_flags = 0;if((sigemptyset(&act.sa_mask) == -1) ||(sigaction(SIGINT,&act,NULL) == -1))perror("Failed to set SIGINT to handle Ctrl-c");for(;;);return 0;}

运行结果:

lab@ubuntu:~/ctest/signal$ ./cc //运行程序                                //回车^CCtrl-C entered!               //第一次按Ctrl+c                              //回车^C                             //第二次按Ctrl+clab@ubuntu:~/ctest/signal$ 






原创粉丝点击