在使用zeromq 退出的时候还遇到一点坑,对于服务deaman(守护进程)化的进程可能会遇到这个问题。
现象:
这个问题导致的现象是服务一旦关闭(stop),就会 core dump,core dump 的信息如下。意思大概是使用了无效的描述符。
(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 的 Context
和 socket
。Server
单例创建的时候,就会创建好 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_context
和 m_socket
声明为指针类型,在守护化后的 Init 调用中进行初始化。
private:
zmq::context_t * m_context;
zmq::socket_t * m_socket;
这也给我们一些启示,对于类成员是第三方类,最好声明为指针,在初始化函数里调用初始化。