Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

skynet.abort遇到奇怪错误 #1314

Closed
fisher-an opened this issue Jan 5, 2021 · 12 comments
Closed

skynet.abort遇到奇怪错误 #1314

fisher-an opened this issue Jan 5, 2021 · 12 comments

Comments

@fisher-an
Copy link

Program terminated with signal SIGSEGV, Segmentation fault.
#0 atomic_load_p (mo=atomic_memory_order_relaxed, a=0x0) at include/jemalloc/internal/atomic.h:62
(gdb) bt
#0 atomic_load_p (mo=atomic_memory_order_relaxed, a=0x0) at include/jemalloc/internal/atomic.h:62
#1 rtree_leaf_elm_bits_read (dependent=true, elm=0x0, rtree=, tsdn=) at include/jemalloc/internal/rtree.h:175
#2 rtree_leaf_elm_szind_read (dependent=true, elm=0x0, rtree=, tsdn=) at include/jemalloc/internal/rtree.h:227
#3 rtree_szind_read (dependent=true, key=5, rtree_ctx=, rtree=, tsdn=) at include/jemalloc/internal/rtree.h:434
#4 arena_salloc (ptr=0x5, tsdn=) at include/jemalloc/internal/arena_inlines_b.h:191
#5 isalloc (ptr=0x5, tsdn=) at include/jemalloc/internal/jemalloc_internal_inlines_c.h:38
#6 je_malloc_usable_size (ptr=ptr@entry=0x5) at src/jemalloc.c:3740
#7 0x0000564cb4b6b88e in clean_prefix (ptr=0x5 <error: Cannot access memory at address 0x5>) at skynet-src/malloc_hook.c:104
#8 free (ptr=0x5) at skynet-src/malloc_hook.c:204
#9 0x00007f7a01f094a1 in OPENSSL_sk_pop_free () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
#10 0x00007f7a0220de09 in ?? () from /usr/lib/x86_64-linux-gnu/libssl.so.1.1
#11 0x00007f7a01eb2c2a in OPENSSL_cleanup () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
#12 0x00007f7a281fe940 in __run_exit_handlers (status=0, listp=0x7f7a285625d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:83
#13 0x00007f7a281fe99a in __GI_exit (status=) at exit.c:105
#14 0x00007f7a281e92e8 in __libc_start_main (main=0x564cb4b63360

, argc=2, argv=0x7ffe9738b5c8, init=, fini=, rtld_fini=,
stack_end=0x7ffe9738b5b8) at ../csu/libc-start.c:325
#15 0x0000564cb4b636ca in _start ()

调用skynet.abort()关服时, 偶发性core。ubuntu18.4
有大佬遇到过吗?

@cloudwu
Copy link
Owner

cloudwu commented Jan 6, 2021

@lvzixun 疑似 ssl 库卸载问题。so 先于退出函数卸载了。

@cloudwu
Copy link
Owner

cloudwu commented Jan 6, 2021

暂时没看出什么问题,看看能否排除是 ssl 相关引起的。另外,是否有类似报告?

@fisher-an
Copy link
Author

只需在某个服务A开启时require "ltls.c", 并未调用过任何内部函数。关服的时候就有很大几率core。即使在调用skynet.abort()前,先kill了该服务A也一样,但发生core却不是A服务退出时。系统debian9.7(xubuntu 无法复现,不知道是否与系统有关)

@lvzixun
Copy link
Contributor

lvzixun commented Jan 8, 2021

是不是除了ltls.so 使用了libssl之外还有其他的第三方库或者代码也使用了libssl

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2021

我怀疑和 tls 库使用了 https://github.com/cloudwu/skynet/blob/master/lualib-src/ltls.c#L394 __attribute__((constructor)) 有关。

这里动态库有一个动态加载和卸载的流程,但最好 ssl 模块只加载一次。其实卸载未必是有必要的。

目前看这里的写法没有什么问题。但我有一些疑虑:

这里的 constructor 系统是否保证了线程安全?constructor 和 destructor 是否严格配对。

我倾向于去掉 destructor ,并不使用 constructor 来初始化 ssl 模块。或许,把 constructor 和 destructor 全部导出成 lua 函数更好。然后,我们通过 config 配置 enable ssl 模块,启动一个服务 require 一次 ssl 并不再退出。这个服务可选在 abort 的时候由 gc 方法去调用 destructor api (但不知道能否保证次序?)

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2021

@lvzixun 我的方案

在 ltls 的 so 中增加一个 ltls.init 子库,用来初始化。

https://github.com/cloudwu/skynet/blob/master/service/bootstrap.lua 中增加对 config 的配置项的检查,如果在 config 文件中配置了 enable ssl ,那么,就加载一个服务:这里可以用 skynet.service 注入代码,不需要额外加一个服务文件。这个服务仅仅 require ltls.init 。

constructor 依然设置 C 的全局变量。而之后 ltls.c 库的初始化会检查是否有 require 过 ltls.init 。

@lvzixun
Copy link
Contributor

lvzixun commented Jan 8, 2021

可以。

@fisher-an
Copy link
Author

按大神的意思,我简单修改了一下把ltls_init,ltls_destory 都注册到lua函数(attribute((constructor)) ltls_init,attribute((destructor)) ltls_destory 已注释)。在main服务启动时init,调用abort关服前主动ltls_destory 。

但是关服时依然在OPENSSL_sk_pop_free 挂了(项目中并没有其他地方使用libssl),奇怪的是core并不是在主动调用ltls_destory 时( 调用ltls_destory 后sleep(100),还能打印日志),感觉就像是系统会自动调用到OPENSSL_sk_pop_free相关函数。

虽然也没什么影响,只是每次关服都有可能生成core文件。所以只能把卸载函数注释了。

@cloudwu
Copy link
Owner

cloudwu commented Jan 8, 2021

注意,你简单注释掉 so 中的卸载函数有可能有问题。

因为,如果动态库被完全卸载后再加载,初始化有可能多次运行。且之前的卸载可能不干净。你需要考虑这个问题。

另外,你的 main 服务会不会一直保留在内存?如果不是(会退出),那么 ltls.so 依然可能提前释放。

这里的问题很有可能是在动态库中调用 atexit 造成的:因为从你的 coredump 看,OPENSSL_cleanup() 这个函数进入了 run_list_atexit 。而这个函数在 libcrypto.so 中。

.so 中调用 atexit 是不安全的,参见:https://stackoverflow.com/questions/29345750/how-to-call-atexit-in-a-shared-library

@fisher-an
Copy link
Author

注意,你简单注释掉 so 中的卸载函数有可能有问题。

因为,如果动态库被完全卸载后再加载,初始化有可能多次运行。且之前的卸载可能不干净。你需要考虑这个问题。

另外,你的 main 服务会不会一直保留在内存?如果不是(会退出),那么 ltls.so 依然可能提前释放。

这里的问题很有可能是在动态库中调用 atexit 造成的:因为从你的 coredump 看,OPENSSL_cleanup() 这个函数进入了 run_list_atexit 。而这个函数在 libcrypto.so 中。

.so 中调用 atexit 是不安全的,参见:https://stackoverflow.com/questions/29345750/how-to-call-atexit-in-a-shared-library

非常感谢大神指点。我的main服务是伴随整个进程生命周期,不会提前退出。并且初始化函数也是有判断并修改 TLS_IS_INIT 这个静态变量,确保只会初始化一次。目前想不到好的方式,临时只能这样修改。希望有官方的解决方案 ... O(∩_∩)O哈哈~

lvzixun pushed a commit to lvzixun/skynet that referenced this issue Jan 11, 2021
@lvzixun
Copy link
Contributor

lvzixun commented Jan 11, 2021

@fisher-an 你看下这个PR 是否能解决你的问题?

cloudwu pushed a commit that referenced this issue Jan 11, 2021
* bugfix ltls init #1314

* 修改init失败信息

* fix destructor

Co-authored-by: zixun <lvzxiun@gmail.com>
@fisher-an
Copy link
Author

@lvzixun 解决了。感谢大佬

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants