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+c lab@ubuntu:~/ctest/signal$