You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已设置signal(SIGPIPE, SIG_IGN),OpenSSL BIO_read()仍触发SIGPIPE

问题描述

我使用OpenSSL的BIO_write()和BIO_read()进行数据收发,偶尔在执行写入操作后尝试读取时,进程会因broken pipe崩溃,触发SIGPIPE信号,调用栈如下:

Thread 5 received signal SIGPIPE, Broken pipe.
[Switching to Thread 0x7fff8212b640 (LWP 39357)]
__GI___libc_write (nbytes=24, buf=0x7fff472a7a63, fd=11) at ../sysdeps/unix/sysv/linux/write.c:26
(gdb) bt
#0  __GI___libc_write (nbytes=24, buf=0x7fff472a7a63, fd=11) at ../sysdeps/unix/sysv/linux/write.c:26
#1  __GI___libc_write (fd=11, buf=0x7fff472a7a63, nbytes=24) at ../sysdeps/unix/sysv/linux/write.c:24
#2  0x000055555574ef3e in conn_write ()
#3  0x000055555574b32a in bwrite_conv ()
#4  0x0000555555749aa7 in bio_write_intern ()
#5  0x000055555574998f in BIO_write ()
#6  0x0000555555716135 in tls_retry_write_records ()
#7  0x00005555556f0531 in ssl3_dispatch_alert ()
#8  0x000055555572205d in ossl_statem_fatal ()
#9  0x000055555570f01b in ossl_tls_handle_rlayer_return ()
#10 0x000055555570f2bf in ssl3_read_bytes ()
#11 0x00005555556ef23d in ssl3_read_internal ()
#12 0x00005555556ec7d9 in ssl_read ()
#13 0x000055555574988b in bio_read_intern ()
#14 0x000055555574976f in BIO_read ()

已知外部原因导致该问题,但我希望自行处理,让底层::recv()通过BIO_read()返回错误码而非触发信号。我已在int main()开头设置:

signal(SIGPIPE, SIG_IGN);

但该设置是在主线程而非读写线程进行的,这是否会有影响?为何设置未生效?还有哪些解决方法?

我的代码逻辑如下:

if(BIO_write(bio, buf, strlen(buf)) <= 0)
{
    return std::nullopt;
}

read = BIO_read(bio, (void*)&recv_buffer, num_bytes);

// 我希望在此处处理SIGPIPE而非进程崩溃

解决方案

1. 主线程设置SIG_IGN未生效的原因

  • 线程创建顺序决定继承关系:主线程设置的信号处理规则会被后续创建的子线程继承,但如果先创建读写线程再设置signal(SIGPIPE, SIG_IGN),已存在的线程不会自动更新处理规则,仍会使用默认的SIG_DFL(触发崩溃)。
  • 排查是否有其他代码覆盖设置:检查读写线程的入口逻辑,是否有重新设置SIGPIPE处理函数的代码,覆盖了主线程的配置。

2. 避免触发SIGPIPE的针对性方案

方案一:设置SO_NOSIGPIPE套接字选项

针对BIO对应的底层套接字,直接禁用SIGPIPE信号触发:

  1. 先从BIO中取出底层文件描述符:
    int fd;
    if (BIO_get_fd(bio, &fd) <= 0) {
        // 处理获取失败逻辑
    }
    
  2. 设置套接字选项:
    int opt_val = 1;
    setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &opt_val, sizeof(opt_val));
    

这样底层write/recv遇到broken pipe时,会直接返回EPIPE错误码,最终通过BIO_read()/BIO_write()传递到上层,你可以在代码中直接处理该错误。

方案二:使用OpenSSL内置标志(1.1.0+版本支持)

给BIO设置BIO_FLAGS_NOSIGPIPE标志,OpenSSL会自动处理套接字的SO_NOSIGPIPE配置:

BIO_set_flags(bio, BIO_FLAGS_NOSIGPIPE);

3. 信号处理的线程安全补充

如果坚持使用信号忽略的方式,确保所有线程都正确应用规则:

  • 必须在主线程创建所有子线程之前调用signal(SIGPIPE, SIG_IGN);
  • 或者在每个读写线程的入口函数中,再次调用signal(SIGPIPE, SIG_IGN),确保线程的信号处理规则正确。

内容的提问来源于stack exchange,提问作者rare77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 00:32:04