遇到个问题,使用spdlog的async_logger打印日志,一直都是正常的,但是调用了fork之后就不打印了
-
先使用打印TODO大法,使用的spdlog的default_logger打印,走的console打印,结果发现console打印没问题,就async_logger在fork之后就不打印了
-
使用strace -f运行看一下
这里的write 1就是console打印的,在fork前后都是正常的,但是write 3只在fork之前调用了,fork后没有调用,直接卡在了futex FUTEX_WAIT_PRIVATE,看来是死锁问题了
-
大概率是async_logger的多线程打印出了问题,先看下spdlog的源码整理一下调用顺序
- logger.h:125 ----> void logger::log(source_loc loc, level::level_enum lvl, string_view_t msg)
- logger-inl.h:166 ----> void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
- async_logger-inl.h:27 ----> void spdlog::async_logger::sink_it_(const details::log_msg &msg)
- thread_pool-inl.h:55 ----> void thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
- thread_pool-inl.h:76 ----> void thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
- mpmc_blocking_q.h:32 ----> void enqueue(T &&item)
-
在enqueue源码里面加点代码,在pop_cv_.wait前后加上打印,再strace -f运行一下
说明一下,修改.h文件需要make clean后重新编译一下才能生效,这里跟踪可以看到只打印了begin没有end,确实在这里卡死了
总结,由于async_logger使用了多线程从queue中读写日志消息,这里就加锁了,持有锁的是spdlog的线程池,我们调用fork在主线程中,这样只有主线程被继承下来成了子进程,虽然这些锁都被继承下来了,但是spdlog的线程池没有被继承下来,没有了持有者就没法释放,那么再入队列的时候就直接死锁了。可以参考下apue线程和fork,顺带一提,fork不继承文件锁,所以想用文件锁来判断进程是否单实例启动,在fork后就不生效了。