Webserver解决segmentation fault(core dump)段错问问题

news2024/9/20 6:11:35

前言

在完成了整个项目后,我用make命令编译了server,当我运行./server文件时,出现了段错误

在大量的代码中找出错因并不是一件容易的事,尤其是对新手程序员来说。而寻找bug的过程就像是侦探调查线索追查凶手一样,我们要通过一点一点的蛛丝马迹来剥离表象,找到真凶。

今天,就由我来扮演一次侦探,调查一番这个段错误到底出自谁手。

段错误:我们面临的是什么敌人

在解决问题之前,让我先来了解一下什么是段错误:

"Segmentation fault (core dumped)" 是一种程序运行时错误,通常表示程序试图访问无效的内存地址。这种错误可能由多种原因引起,包括指针错误、数组越界、使用已释放的内存等。

一般情况下,解决段错误的方法是使用gdb调试段错误生成的core文件。但是许多人会发现系统不会生成core dump文件,这是因为 core dump文件是由linux系统进行生成,而且其往往较大,默认情况下,Linux是不允许生成core dump文件的。

我们可以使用

ulimit -c

命令来查看,如果是 0,说明Linux允许的core文件最大大小为0,即不允许生成core文件

这时,我们要使用

ulimit -c unlimited

来将core文件最大大小改为无限。

我们再编译运行程序就会产生core文件,但是默认的core文件生成目录不在本目录,因此需要把默认core文件生成目录改成运行目录,再然后.....

......

这也太麻烦了,有没有更简单的方法。哎,你别说,还真有,接下来我们不借助core文件来"查案"

GDB调试探方向,段错误真相初现端倪

首先,我们在makefile文件里的编译代码加上-g的可选项,这样生成的server就是一个可以用gdb调试的可执行文件。

随后,我们使用gdb命令进入server可执行文件

gdb server

进入gdb调试器以后,我们使用run运行

gdb给出已下报错:

注意看这里的MYSQL Error: mysql_real_connect,虽然gdb告诉我们是在 iofputs.c 的__GI__IO_fputs函数出现的段错误,但是这是系统调用,无数人用了那么多年,不太可能出现错误,所有应该还是我们自己的代码有问题。那我们该从何找起呢?

哎,我们发现这里有一条日志:

MySQL Error : mysql_real_connect\n

这不是我们写的日志吗,太好了,我们终于发现了错误的蛛丝马迹。

深入代码危险区,巧设监控锁暗敌

经过我们的重重调查,我们终于定位到了错误代码函数所在地,即sql_connection_pool.cpp的init函数

void connection_pool::init(string url, string User, string PassWord, string DBName, int MaxConn, int Port, int close_log) {
    m_url = url;
    m_Port = Port;
    m_User = User;
    m_PassWord = PassWord;
    m_DatabaseName = DBName;
    m_close_log = close_log;

    for (int i = 0; i < MaxConn; ++i) {
        MYSQL *con = NULL;
        con = mysql_init(con);

        if (con == nullptr) {
            LOG_ERROR("MySQL Error : mysql_init");
            exit(1);
        }

        /*真正的连接函数*/
        con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);

        if (con == nullptr) {
            LOG_ERROR("MySQL Error : mysql_real_connect");
            exit(1);
        }

        connList.push_back(con);
        m_FreeConn++;
    }

    reserve = sem(m_FreeConn);//信号量记录共享资源总量

    m_MaxConn = m_FreeConn;
}

其中,GDB告诉我们的线索即是这里的”人证“给出的 , 即第22行的

LOG_ERROR("MySQL Error : mysql_real_connect");

让我们看看这个函数里的关键API:mysql_init和mysql_real_connect

mysql_init 是 MySQL C API 中的一个函数,用于初始化和分配一个 MYSQL 结构,这个结构是用于表示 MySQL 连接的句柄。这个函数通常是在开始使用 MySQL C API 之前调用的,以确保连接句柄是有效的。

mysql_real_connect 函数是 MySQL C API 中的一个关键函数,用于建立与 MySQL 服务器的连接

可见,这个函数是通过mysql_init创建句柄,再用该句柄创建mysql连接

咋一看好像这一块的代码都没有问题,那是怎么回事了?

为了深一步调查,我们使用时空回溯大法,我们在这里设下”监控“,重现当时的”犯罪场景“,那么我们该如何设下监控呢?其实很简答,就是我们在c++中常用debug方法,在程序中打印出调试信息,这里我们就在第19行代码

con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);

的前后,分别写上

cout<<"before mysql_real_connect"<<endl;
con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);
cout<<"after mysql_real_connect"<<endl;

然后我们再编译运行,出现以下结果:

可以看到,程序并非是没进入mysql_real_connect就报错,而是循环了无数遍后才出现的报错;这是怎么回事了,为了清晰地看到循环次数,我们为“监控”加上计数器

cout<<"第"<<i<<"次 before mysql_real_connect"<<endl;
con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);
cout<<"第"<<i<<"次 after mysql_real_connect"<<endl;

可以看到,程序循环了151次后就结束了,随后发生了段错误。这是什么原因?让我们接着“调查”。

日夜辗转寻真相,辛勤探寻不负望

经过前面的“调查”,我们得到了线索,再init循环中,当程序循环151次,第152次就发生了段错误。没办法,我们先去调查一下是谁调用了init

我们发现,调用该函数的是WebServer.cpp的void sql_pool()函数,而且该函数给init的循环上限MaxConn设置的是3306

void WebServer::sql_pool() {
    /*初始化数据库连接池*/
    m_connPool = connection_pool::Getinstance();
    m_connPool->init("localhost", m_user, m_passWord, m_databaseName, 3306, m_sql_num, m_close_log);

    /*初始化数据库读取表*/
    users->initmysql_result(m_connPool);
}

我们对比一下m_connPool的定义,发现了问题所在

void connection_pool::init(string url, string User, string PassWord, string DBName,  int MaxConn, int Port,int close_log) 

原来是因为Port和MaxConn在定义在前的为端口,定义在后的为循环上限了,导致了端口号被用来当循环的上限;而数据库最大能创建的连接数是152,超出了152自然就触发了段错误;那么,我们将调用init的地方改过来即可。

后记

经过不懈的努力,我终于是解决了这个BUG。但其实在解决bug的过程中我并不像文章中说的那么容易,包括最开始我为了去得到 core文件,找了许多方法,也花了很多时间,但是一直没有什么显著效果。中间我一度沮丧到想哭。最后也是不管三七二十一用GDB运行了一次server文件才找到了一点点蛛丝马迹。而后面在发现循环151次后就会段错误的时候,我也一度找错了方向,找了许多方法,把数据库的连接上限改成3500,系统能容纳的最大文件描述符也被我改成了3500,如下图

但是当我以为解决问题的时候,再运行虽然超过了151,但在1000多的时候还是会段错误,当时我想了好多办法,但一直收效见微。可见在错误的方向上,你越努力,错的就越离谱。最后还是检查源码的时候发现这里的调用把port和Maxconn写错位了,也算是给写这篇文章的大家一个警醒吧。

像这样的BUG我在项目中遇到不止一个,其实项目我在两天前就写完了,这两天一直在debug,几乎可以说是不吃不喝地程度了,连上厕所,睡觉都在想怎么debug。皇天不负有心人,项目我也终于是完成了,后面把剩下的博客写完,我的将近30天Webserver之旅就到此为止了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1471452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vite搭配vue2创建工程

一、安装vite npm init vite2.8.0 vite默认支持的是vue3&#xff0c; 这里选择框架和版本vanilla&#xff0c; 方便以后自己安装vue2. 二、修改package.json 默认生成的pacakage.json文件 {"name": "vite-project","private": true,"v…

AI对话系统app开源

支持对接gpt&#xff0c;阿里云&#xff0c;腾讯云 具体看截图 后端环境&#xff1a;PHP7.4MySQL5.6 软件&#xff1a;uniapp 废话不多说直接上抗揍云链接&#xff1a; https://mny.lanzout.com/iKFRY1o1zusf 部署教程请看源码内的【使用教程】文档 欢迎各位转载该帖/源码

企业过二级等保采购哪家堡垒机好?

企业常见过等保&#xff0c;一般是指等保二级或者三级。这不2024开年&#xff0c;不少企业在问&#xff0c;过二级等保采购哪家堡垒机好&#xff1f;电话多少&#xff1f;这里我们小编就给大家毛遂自荐一下吧&#xff01; 企业过二级等保采购哪家堡垒机好&#xff1f; 【回答】…

2024首场沙龙|上海 · 得物技术沙龙-「稳定生产」专场报名开启!

在互联网业界&#xff0c;伴随业务需求更新、用户流量增长、创新技术迭代&#xff0c;稳定性一直是十分复杂且极具挑战性的课题。近年业界发生较多Bad case&#xff0c;得物技术团队迎难而上&#xff0c;通过制定年度全局目标、持续技术创新、深挖产线问题、严控内建质量及文化…

第四十四回 杨雄醉骂潘巧云 石秀智杀裴如海-python驱动关系型数据库

石秀在杨雄家帮忙&#xff0c;发现杨雄的妻子潘巧云跟她的师兄、和尚裴如海有情况。 潘巧云以到寺里还愿的名义&#xff0c;去见裴如海。两人见面后&#xff0c;此处省略三百字。潘巧云说晚上如果杨雄不在家&#xff0c;就烧夜香为讯号&#xff0c;你可以来。你再找一个和尚提…

llm的inference(二)

文章目录 Tokenizer分词1.单词分词法2.单字符分词法3.子词分词法BPE(字节对编码&#xff0c;Byte Pair Encoding)WordPieceUnigram Language Model(ULM) embedding的本质推理时的一些指标参考链接 Tokenizer 在使用模型前&#xff0c;都需要将sequence过一遍Tokenizer&#xf…

AI智能分析网关V4智慧工厂视频智能监管与风险预警平台建设方案

一、背景需求分析 1&#xff09;随着信息技术的迅猛发展和制造业竞争的加剧&#xff0c;智慧工厂成为了推动制造业转型升级的重要引擎。智慧工厂解决方案通过整合物联网、人工智能、大数据分析等先进技术&#xff0c;实现生产过程的智能化、自动化和高效化&#xff0c;为企业提…

Nginx——安装和反向代理

Nginx安装与应用 1.1 Nginx介绍 Nginx 是一个高性能的HTTP和反向代理服务器,特点是占有内存少&#xff0c;并发能力强 Nginx可以作为静态页面的web服务器&#xff0c;同时还支持CGI协议的动态语言&#xff0c;比如perl、php等。但是不支持java。Java程序只能通过与tomcat配合…

Windows部署WebDAV服务并映射到本地盘符实现公网访问本地存储文件

文章目录 前言1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 安装cpolar内网穿透3.2 配置WebDav公网访问地址 4. 映射本地盘符访问 前言 在Windows上如何搭建WebDav&#xff0c;并且结合cpolar的内网穿透工具实现在公网访…

sqli-labs(less-46)order by 注入

我们打开sql-labs的第46关然后在输入框内输入?id1时会发现页面没有任何的变化&#xff0c;此时我们用Visual Studio Code查看第46关的代码 此时我们发现sql语句是$sql "SELECT * FROM users ORDER BY $id"; &#xff0c;所以现在我们需要了解一下order by语句的作…

防御保护笔记3

内容安全 攻击可能只是一个点&#xff0c;防御需要全方面进行 DFI和DPI技术 --- 深度检测技术 DPI --- 深度包检测技术 --- 主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#xff09;&#xff0c;之后对 数据包的内容进行识别。&#xff08;应用层&am…

Python服务器监测测试策略与工具:确保应用的高可用性!

在构建高可用性的应用程序时&#xff0c;服务器监测测试是至关重要的一环。Python作为一种强大的编程语言&#xff0c;提供了丰富的工具和库来帮助我们进行服务器监测测试。本文将介绍一些关键的策略和工具&#xff0c;帮助你确保应用的高可用性。 1. 监测策略的制定&#xff…

回归预测 | Matlab实现CPO-HKELM冠豪猪算法优化混合核极限学习机多变量回归预测

回归预测 | Matlab实现CPO-HKELM冠豪猪算法优化混合核极限学习机多变量回归预测 目录 回归预测 | Matlab实现CPO-HKELM冠豪猪算法优化混合核极限学习机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-HKELM冠豪猪算法优化混合核极限学习机…

前端开发_Vue入门

Vue概念 Vue 是一个用于构建用户界面的渐进式框架 构建用户界面&#xff1a;基于数据渲染出用户看到的页面渐进式&#xff1a;循序渐进框架&#xff1a;一套完整的项目解决方案 创建Vue实例 准备容器 引包&#xff08;开发版本/生产版本&#xff09; <script src"h…

Leetcode刷题笔记题解(C++):6. Z 字形变换

思路&#xff1a;遍历时候需要更新步进长度 到达0行的时候步进长度为1&#xff1b;到达最后一行numRows-1行的时候步进长度为-1&#xff1b;代码如下所示&#xff1a; class Solution { public:string convert(string s, int numRows) {//如果字符串长度为1或者所给行数为1 …

WampServer环境下载安装并结合内网穿透实现远程访问管理界面

文章目录 前言1.WampServer下载安装2.WampServer启动3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 Wamp 是一个 Windows系统下的 Apache PHP Mysql 集成安装环境&#xff0c;是一组常用来…

【C#】获取文本中的链接,通过正则表达式的方法获取以及优化兼容多种格式

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

007 地理配准纸质地图

大部分的GIS都会需要对某些影像进行“地理配准 &#xff08;Georeferencing&#xff09;”&#xff0c;也就是说要为影像的每个像素指定它在世界上的 地理空间座标。在多数的情况下&#xff0c;这些座标是通过野外调查收集而来&#xff0c;例如&#xff0c;用GPS设备&#xff0…

Python3.7.1标准安装

Python3.7.1标准安装 官网下载 官网地址&#xff1a;https://www.python.org/downloads/ 下载3.7.1 下载64/32bitwindows安装文件&#xff0c;下图x86-64是64bit&#xff0c;x86是32bit 双击exe文件安装 第一个界面如下图选择 第二个界面默认选择&#xff0c;然后点击Nex…

C++笔记(面对对象部分复习向)

B站&#xff1a;黑马程序员C教程 栈区&#xff0c;全局区&#xff0c;堆区和代码区 析构、构造和static 对象成员与类本身构造顺序&#xff0c;先成员后自己&#xff1b;析构则相反 static修饰成员变量,所有对象共享一份内存&#xff0c;编译阶段分配内存&#xff0c;类内声明…