[高并发服务器]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);
}
相关推荐
邪修king1 小时前
Re:Linux 系统篇(三十五):进程间通信Chapter2:从匿名管道到进程池:Linux 进程间通信实战(深度理解「先描述,再组织」)
java·linux·运维
2301_777998342 小时前
ARP协议进阶:ARP缓存、RARP与ARP欺骗原理解析
linux·服务器·网络·网络协议·缓存
运维行者_3 小时前
应用性能监控怎么选?从调用链到用户体验的五个能力维度
运维·服务器·数据库·人工智能·mysql·性能优化·应用性能监控
考虑考虑7 小时前
nginx路由重写
运维·后端·自动化运维
Zenova EdgeOS9 小时前
Linux 内核日志 dmesg 进阶实战:从现场过滤到持久化闭环
linux·运维·服务器
郝亚军9 小时前
ec_private.pem的生成方法
linux·运维·服务器
NendoZen10 小时前
Arch Linux解决Grub无法SAVEDEFAULT问题(uki启动问题)
linux·运维·服务器
努力实干10 小时前
面向对象设计原则——C++
开发语言·c++·数码相机
Elastic 中国社区官方博客11 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
忆挽篱笙歌11 小时前
进程控制管理
linux·运维·服务器