在使用zeromq 退出的时候还遇到一点坑,对于服务deaman(守护进程)化的进程可能会遇到这个问题。

现象:

这个问题导致的现象是服务一旦关闭(stop),就会 core dump,core dump 的信息如下。意思大概是使用了无效的描述符。

img

(gdb) bt
#0 0x00007f522e2le387 in raise () from /lib64/libec.so.6
#1 0x00007f522e21fbb8 in abort () from /lib64/libec.so.6
#2 0x00000000004d5909 in zmq::zmq_abort (errmsg_errmsg_@entry=0x7f522e37025f "Bad file descriptor") at src/err.cpp :88
#3 0x00000000004d5242 in zmq::epoll_t::add_fd_(this=0x1b25520, fd_efd_@entry=15, events_events_@entry=0x1b28580) at src/epoll.cpp:100
#4 0x00000000004e42e2 in zmq::socket_base_t::start_reapiing (this=0x1b28140, poller_=<optimized out>) at src/socket_base.cpp:1465
#5 0x000000000004ela63 in zmq::reaper_t::process_reap (this=0xlb21580, socket_=<optimized out>) at src/reapercpp:133
#6 0x000000000004ele7c in zmq::reaper_t::in_event (this=0x21b21580) at src/reaper.cpp:104
#7 0x00000000004d508e in zmq::epoll_t::loop (this=0x1b255520) at src/epoll.cpp:206
#8 0x00000000004f0d45 in thread_routine (arg_=0x1b255778) at src/thread.cpp:257
#9 0x00007f522f23dea5 in start_thread () from /lib64/libpthread.so.0
#10 0x00007f522e2e6b0d in clone () from /lib64/libc.so.6

产生的原因:

我们服务的 Server 是个 static Instance 单例,在 Server 里默认分配内存的方式声明了 zeromq 的 ContextsocketServer 单例创建的时候,就会创建好 Context 和 socket。


class Server
{
	....
  private:
	  zmq::context_t m_context;
    zmq::socket_t  m_socket;
}

在启动服务后,会判断是否需要守护化(daemon化),如果需要,就会 fork 进程并创建守护进程,主进程退出。

正是主进程退出时没有调用 Context 的销毁函数,导致子进程退出时,虽然处理了 Context 的销毁,但是主进程创建的 Context 却没有调用销毁函数,导致和 zeromq 内部线程还在访问失效的描述符,从而出现了 core dump。这里需要解释一下,在 fork 时,子进程也会拷贝父进程的 static 数据。

下面是关于 fork 后父进程和子进程 static 内存数据的介绍:

在 fork 函数调用时,父进程的内存会被复制到子进程中,包括 static 数据的内存。这意味着子进程也会拥有与父进程相同的 static 数据,但是它们是相互独立的。如果在父进程或子进程中修改了 static 数据,则不会影响另一个进程中的 static 数据。

补充一段static类析构调用时机的介绍:

解决方法:

m_contextm_socket 声明为指针类型,在守护化后的 Init 调用中进行初始化。

  private:
	  zmq::context_t * m_context;
    zmq::socket_t  * m_socket;

这也给我们一些启示,对于类成员是第三方类,最好声明为指针,在初始化函数里调用初始化。

作者:|聆湖听风|,原文链接: https://www.cnblogs.com/listenwind666/p/17239467.html

文章推荐

数据库基础(上)

从源码深入理解读写锁(golang-RWMutex)

深入理解 python 虚拟机:字节码教程(1)——原来装饰器是这样...

程序化广告还有未来么?(4/5)——程序化领域变化的底层逻辑...

前端性能精进(六)——网络

关于 Web 应用的内联 css 和 scss 文件里的 var 关键字用法

Linux进程通信 | 信号

细说React组件性能优化

nacos初探

线程池 - ThreadPoolExecutor源码分析

Python <算法思想集结>之抽丝剥茧聊动态规划

golang泛型实现--双hash表