首页 > 其他分享 >多线程sigpipe

多线程sigpipe

时间:2023-03-24 22:38:30浏览次数:43  
标签:struct signal sig mask 线程 信号 sigpipe 多线程

 之前记录过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协议的限制, 一个端点无法获知对端已经完全关闭.

多线程sigpipe_信号处理

  对一个已经收到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]

多线程sigpipe_多线程_02

 

 

 当选择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);不能完成的任务是:
  1. 不知道信号产生的原因;
  2. 处理信号中不能阻塞其他的信号

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

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

  • sigprocmask函数只能用于单线程(进程),在多线程中使用pthread_sigmask函数。
  • 信号是发给进程的特殊消息,其典型特性是具有异步性。
  • 信号集代表多个信号的集合,其类型是sigset_t。
  • 每个进程都有一个信号掩码(或称为信号屏蔽字),其中定义了当前进程要求阻塞的信号集。
  • 同时注册了信号处理函数,同时又用sigwait来等待这个信号,谁会取到信号?经过实验,Linux上sigwait的优先级高。

 

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子



标签:struct,signal,sig,mask,线程,信号,sigpipe,多线程
From: https://blog.51cto.com/u_15404950/6148285

相关文章

  • 多线程
    一、线程1、概念线程在一个进程的内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”叫做线程是操作系统能够进行运算调度的最小单位。它......
  • 2 - 线程 - Windows 10 - CPython 解释器 - 多线程并行(实际并发)
    @目录一、线程和进程介绍进程基本概念面向线程设计的系统内部解析-用户态/内核态线程基本概念二、对进程线程并发并行的实际运行过程的理解:CPython多线程争抢GIL——......
  • 日志之MDC和异步多线程间传递线程id
    目录1MDC1.1简介1.2MDC坐标和使用1.3主要方法2多线程间使用2.1MDC工具类2.2拦截器定义和配置2.3Java线程池中使用2.3.1配置线程池2.3.2使用ExecutorCompletionSe......
  • Java多线程之ExecutorCompletionService
    目录1ExecutorCompletionService1.1简介1.2原理1.3Demo示例1.3.1未使用ExecutorCompletionService1.3.2使用ExecutorCompletionService1.4深入分析说明1.4.1所有方......
  • qt 多线程 moveToThread 的一个骚操作
    moveToThread 相当于是一个多线程的阻塞函数,本案例可多次点击按钮,多次触发,这个信号触发类似于内部建立一个队列,处理函数会按照顺序处理信号 test_moveToThread.p......
  • 爬虫进阶之多线程爬虫问题详解
    大多数正常人在下载图片的时候都是一个一个点击保存,图片越多花费的时间越多,大大的降低了工作效率。如果是学了爬虫的,一定会想到多线程来自动下载保存图片。多线程介绍:多......
  • NodeJS 多线程编程
    一、开发环境Node.JSv14.8.0二、快速开始-worker_threadsjs和nodejs一直都是单线程,直到官方推出了worker_threads模块,用来解决CPU密集型计算场景。可以通过......
  • C# 多线程访问之 SemaphoreSlim(信号量)【进阶篇】
    C#多线程访问之SemaphoreSlim(信号量)【进阶篇】 阅读目录一、简介二、用法示例 三、属性or函数or方法释义属性-AvailableWaitHandle属性-CurrentCount......
  • 多线程 ForkJoinPool
    ava7提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。ForkJoinPool是ExecutorService的实现类,因此是一种......
  • 【Thread -- 1.1】 实现多线程的正确姿势
    【Thread--1.1】实现多线程的正确姿势一、实现多线程的方法有几种--两种1、正确方法--Oraclle官方文档--2种[Oraclle官方文档](Overview(JavaPlatformSE8)(ora......