[高并发服务器]DEBUG日志

1.客户端主动断开TCP连接,引发的段错误

2.定时任务执行后,引发的段错误

2.1.问题描述

在服务器启动非活跃连接超时销毁后,

当定时任务执行时,

触发了段错误;

下面是相关日志

bash 复制代码
//相关日志:
//设定的定时任务为Connection::releaseInLoop()
[DEBUG][2025-12-19 15:07:08]TimerTask::~TimerTask(): execute timer task,id:1
[DEBUG][2025-12-19 15:07:08]TimerTask::~TimerTask(): execute timer task,id:2
[DEBUG][2025-12-19 15:07:08][tid=140399404377664]执行Connection::releaseInLoop(),conn_id=1
[DEBUG][2025-12-19 15:07:08][tid=140399412770368]执行Connection::releaseInLoop(),conn_id=2
Segmentation fault (core dumped)

2.2.问题定位

下面是TimerTask::~TimerTask()的具体实现

cpp 复制代码
TimerTask::~TimerTask()
{
    //如果定时任务没有被取消
    if(false == _isCanceled) 
    {
        lg(DEBUG,"TimerTask::~TimerTask(): execute timer task,id:%lu",_id);
        _taskCb();//执行定时任务
    }
    //lg(DEBUG,"TimerTask::~TimerTask(): _deleteCb() ,id:%lu",_id);

    //将id->TimerTask的映射关系从TimerWheel​​​​​​​::_idMapTimerTask内移除
    _deleteCb();
}

下面是Connection::releaseInLoop()的具体实现

cpp 复制代码
void Connection::releaseInLoop()
{
#ifdef DEBUG
    std::thread::id gtid=std::this_thread::get_id();
    std::ostringstream oss;
    oss << gtid;
    std::string tid=oss.str();
    lg(DEBUG,"[tid=%s]执行Connection::releaseInLoop(),conn_id=%lld",tid.c_str(),getId());
#endif
    //assert(CONNECTED == _status ||DISCONNECTING == _status );
    //修改连接状态
    _status = DISCONNECTED;

    //取消该连接的定时任务
    _eventLoop->cancelTimerTask(_id);

    //从epoll中移除该channel
    _channel.remove();

    //关闭描述符
    _socket.close();

    lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 1",tid.c_str(),getId());

    //调用用户设置的回调
    if(_closeCb) _closeCb(shared_from_this());

    // std::thread::id gtid=std::this_thread::get_id();
    // std::ostringstream oss;
    // oss << gtid;
    // std::string tid=oss.str();
    lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 2",tid.c_str(),getId());

    //调用组件本身设置的回调
    if(_serverCloseCb) _serverCloseCb(shared_from_this());

    lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 3",tid.c_str(),getId());
    
}

Connection::releaseInLoop()没有执行后续的DEBUG日志代码,

说明问题出现在下面这段区间,

cpp 复制代码
    //assert(CONNECTED == _status ||DISCONNECTING == _status );
    //修改连接状态
    _status = DISCONNECTED;

    //取消该连接的定时任务
    _eventLoop->cancelTimerTask(_id);

    //从epoll中移除该channel
    _channel.remove();

    //关闭描述符
    _socket.close();

后续在cancelTimerTask(_id)定位到如下内容

cpp 复制代码
void TimerWheel::cancelTaskToLoop(uint64_t id)
{
    //lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) called");
    if(exist(id) == false) return ; //判断id是否在TimerWheel​​​​​​​::_idMapTimerTask内
    //lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) 1");
    std::shared_ptr<TimerTask> ptr=_idMapTimerTask[id].lock();
    //lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) 2 ,ptr==nullptr:%d",(ptr==nullptr));
    ptr->cancel();
    //lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) over");
}   

上述代码的**ptr->cancel();**是引发段错误的原因

2.3.问题产生原因分析

在时间轮TimerWheel模块的实现中,

有如下成员;

cpp 复制代码
    /// @brief 时间轮数组
    std::vector<std::list<std::shared_ptr<TimerTask>>> _wheel;

    /// @brief id->timeTask映射表
    std::unordered_map<uint64_t,std::weak_ptr<TimerTask>> _idMapTimerTask;

当_wheel内的定时任务TimerTask结构体 调用析构函数执行定时任务(即Connection::releaseInLoop())时,

在此之前对应的shared_ptr的引用计数已经变为0 ,置为nullptr,且被释放;

因为**exist(id)**的返回结果为true,

因此会执行std::shared_ptr<TimerTask> ptr=_idMapTimerTaskid.lock()(这句代码位于Connection::releaseInLoop()中的_eventLoop->cancelTimerTask(_id);的执行流程内);

获取到的ptr的值为nullptr,

从而导致段错误

2.4.问题解决方案

修改TimerTask::~TimerTask(),在定时任务执行前移除id->TimerTask的映射关系,如下;

cpp 复制代码
TimerTask::~TimerTask()
{
    //将id->TimerTask的映射关系从TimerWheel​​​​​​​::_idMapTimerTask内移除
    _deleteCb();

    //如果定时任务没有被取消
    if(false == _isCanceled) 
    {
        lg(DEBUG,"TimerTask::~TimerTask(): execute timer task,id:%lu",_id);
        _taskCb();//执行定时任务
    }
    //lg(DEBUG,"TimerTask::~TimerTask(): _deleteCb() ,id:%lu",_id);
}
相关推荐
comedate12 小时前
Windows / Linux GPU 虚拟内存布局差异与 CUDA 越界访问行为
linux·windows·mmu·cuda 越界访问
暴力求解13 小时前
Linux网络---传输层协议TCP(二)
linux·服务器·开发语言·网络·tcp/ip
JoyCong199814 小时前
ToDesk个人版、专业版、游戏版、设计版、性能版、团队版权益介绍
大数据·运维·游戏·远程工作·远程操作
别动我齐刘海14 小时前
机器人运动控制学习2——基础进阶
c++·人工智能·神经网络·学习·目标检测·机器学习·机器人
ltl14 小时前
序列化格式深度对比:Protobuf、FlatBuffers、Cap’n Proto
linux
lengjingzju14 小时前
编译与调试完全指南—第17章 总结
linux
ltl14 小时前
容器网络性能真相:veth vs macvlan vs eBPF 数据面
linux
傻啦嘿哟14 小时前
英雄联盟皮肤爬虫:爬取全皮肤价格与特效,做比价工具
java·c++·爬虫
Elastic 中国社区官方博客15 小时前
ES95:面向 Elasticsearch 时间序列指标的自适应压缩
大数据·运维·elasticsearch·搜索引擎·架构·全文检索
码匠许师傅15 小时前
【C++ 面试真题】24. 聊聊 C++ 的时间日期处理
java·c++·面试