已设置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信号触发:
- 先从BIO中取出底层文件描述符:
int fd; if (BIO_get_fd(bio, &fd) <= 0) { // 处理获取失败逻辑 } - 设置套接字选项:
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
相关产品推荐
相关产品推荐

