之前记录过socket读写异常相关情况,socket 链接错误以及原因
以及信号相关处理:多线程信号处理
目前调试引擎时候出现了 错误;signal SIGPIPE, Broken pipe
一开始以为是没有忽略sigpipe信号,立即在代码里面加上如下代码,
signal(SIGPIPE, SIG_IGN);
received signal SIGPIPE, Broken pipe
仔细分析了一下,才想起来,多线程对于信号的处理有所不同,也就是说这个bug一直存在这个产品很长时间了。
也就是多线程中对sigpipe信号的处理,解决方法是每一个线程启动之前时,先执行下面代码:
#ifndef WIN32
sigset_t signal_mask;
sigemptyset (&signal_mask);
sigaddset (&signal_mask, SIGPIPE);
int rc = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
if (rc != 0) {
printf("block sigpipe error\n");
}
#endif
sigset_t 的blocked
new_blocked = current->blocked;
switch (how) {
case SIG_BLOCK:
sigaddsetmask(&new_blocked, new_set);
当创建线程/进程的时候, 所有的线程进程都会copy继承一份。
问题场景还原分析:
对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程.
具体的分析可以结合TCP的”四次握手”关闭. TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制, 一个端点无法获知对端已经完全关闭.
对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.
问题:为什么signal(SIGPIPE, SIG_IGN);将信号转为sig_ign后还是会出现 epipe错误呢?
首先:创建thread线程的时候, 只会共享shared_pending 队列,每个线程会初始化 init_sigpending(&p->pending)信号私有队列,
(1)在操作系统内核”struct task_struct“结构体内部有一个变量"struct sigpending pending;"
(2)内核定义的结构体"struct sigpending"当中有两个变量:一个是内核定义的双向链表;一个是:”sigset_t signal“
(3)内核定义的类型”sigset_t“为一个结构体,在结构体内部有一个变量,该变量为一个数组(无符号长整型的数组)
struct sigpending {
struct list_head list;
sigset_t signal;
};
同时每一个sigal都对应一个 action[signal]
当选择action 为sig_ign的时候其sa_handler为SIG_IGN,但是线程的sighand 共享。
if (clone_flags & CLONE_SIGHAND) {
atomic_inc(¤t->sighand->count);
return 0;
}
先注册信号处理函数为sig_ign,但是创建线程后,每个线程的私有pending被初始化,只是公共shared_penging还在。
int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
struct task_struct *p = current, *t;
struct k_sigaction *k;
sigset_t mask;
if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
return -EINVAL;
k = &p->sighand->action[sig-1];
spin_lock_irq(&p->sighand->siglock);
if (oact)
*oact = *k;
if (act) {
sigdelsetmask(&act->sa.sa_mask,
sigmask(SIGKILL) | sigmask(SIGSTOP));
*k = *act;
/*
* POSIX 3.3.1.3:
* "Setting a signal action to SIG_IGN for a signal that is
* pending shall cause the pending signal to be discarded,
* whether or not it is blocked."
*
* "Setting a signal action to SIG_DFL for a signal that is
* pending and whose default action is to ignore the signal
* (for example, SIGCHLD), shall cause the pending signal to
* be discarded, whether or not it is blocked"
*/
if (sig_handler_ignored(sig_handler(p, sig), sig)) {
sigemptyset(&mask);
sigaddset(&mask, sig);
flush_sigqueue_mask(&mask, &p->signal->shared_pending);
for_each_thread(p, t)
flush_sigqueue_mask(&mask, &t->pending);
}
}
spin_unlock_irq(&p->sighand->siglock);
return 0;
}
如果设置为sig_ign,当前进程线程组会删除对应的信号集mask以及信号队列中已经生成的信号
ps:统中所有的进程都组织在init_task的tasks链表下面,每个进程的线程组织在每个进程task_sturct->signal的链表下
这样看代码,每个线程会执行对应sig_ign互联动作函数啊???
怎么不生效呢??为什么必须给线程设置信号屏蔽集呢?
查阅文档:
Initially I tried signal(SIGPIPE,SIG_IGN)
, but signal(2) man says:
The effects of signal() in a multithreaded process are unspecified.
为什么是这样还是没搞清楚!!!!
PS:如果需要用相同的方式处理信号的多次出现,建议使用sigaction函数;若可以保证信号长时间内只出现并只需要处理一次,则可以使用signal函数;
signal函数,只能生效一次;sigaction函数设置后一直有效
ps:目前是用demo测试多线程sigpipe 是正常的!!!这尼玛。。。。。。
总结一下:
- Linux 多线程应用中,每个线程可以通过调用pthread_sigmask() 设置本线程的信号掩码。一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如SIGSEGV;另外不能被忽略处理的信号SIGKILL 和SIGSTOP 也无法被阻塞。
- 当一个线程调用pthread_create() 创建新的线程时,此线程的信号掩码会被新创建的线程继承。
- 信号安装最好采用sigaction方式,sigaction,是为替代signal 来设计的较稳定的信号处理,signal的使用比较简单。signal(signalNO,signalproc);不能完成的任务是:
- 不知道信号产生的原因;
- 处理信号中不能阻塞其他的信号
而signaction,则可以设置比较多的消息。尤其是在信号处理函数过程中接受信号,进行何种处理。
sigaction函数用于改变进程接收到特定信号后的行为。
- sigprocmask函数只能用于单线程(进程),在多线程中使用pthread_sigmask函数。
- 信号是发给进程的特殊消息,其典型特性是具有异步性。
- 信号集代表多个信号的集合,其类型是sigset_t。
- 每个进程都有一个信号掩码(或称为信号屏蔽字),其中定义了当前进程要求阻塞的信号集。
- 同时注册了信号处理函数,同时又用sigwait来等待这个信号,谁会取到信号?经过实验,Linux上sigwait的优先级高。
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子