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

前言

在完成了整个项目后,我用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://xiahunao.cn/news/2809427.html

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

相关文章

皓学IT:WEB05-Servlet

一、Servlet 1.1.概述 Servlet是SUN公司提供的一套规范&#xff0c;名称就叫Servlet规范&#xff0c;它也是JavaEE规范之一。我们可以像学习Java基础一样&#xff0c;通过API来学习Servlet。这里需要注意的是&#xff0c;在我们之前JDK的API中是没有Servlet规范的相关内容&am…

模型 金字塔原理

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_总纲目录。清晰逻辑&#xff0c;有效沟通。 1 金字塔原理的应用 1.1 应用金字塔原理提出一个新产品的市场推广策略 确认结论&#xff1a;我们应该采取在线社交媒体广告和口碑营销的策略来推广新产品。 构建层级1&…

使用apt-mirror做一个本地ubuntu离线apt源

1. 安装 apt-mirror sudo apt-get install apt-mirror2. 创建文件夹 mkdir ./apt_mirror_dir3. 修改apt-mirror的配置文件 sudo gedit /etc/apt/mirror.list得到以下文件,重点对两个位置进行修改&#xff1a; ############# config ################## # ## 修改1&#xff…

基于Clion+stm32cubemx+rt-thread os进行环境搭建

前言 RT-Thread文档中心Clion开发STM32的环境搭建,请参考之前的文章本次使用的芯片为STM32F407VET6,其他芯片相似.项目创建 使用STM32CubeMx快速生成项目工程,此步骤的话可以参考官方文档 基础配置如下

BoomWorks使用wxWidgets+CodeBlocks+GCC开发的软件合集

♦️ 定时执行专家&#xff08;TimingExecutor&#xff09; V7.0 《定时执行专家》是一款制作精良、功能强大、毫秒精度、专业级的定时任务执行软件。软件具有 25 种【任务类型】、12 种【触发器】触发方式&#xff0c;并且全面支持界面化【Cron表达式】设置。软件采用多线程并…

AI 绘画:人工智能绘画之美

人工智能&#xff08;AI&#xff09;是当今科技领域的热门话题&#xff0c;它不仅可以帮助我们解决各种复杂的问题&#xff0c;还可以创造出令人惊叹的艺术作品。AI 绘画是一种利用 AI 技术生成图像的方法&#xff0c;它可以模仿不同的风格、主题和技巧&#xff0c;甚至可以创造…

idea集成git(实用篇)

0.Git常用命令 Git常用命令-CSDN博客 1.下载git Git - Downloads 一路傻瓜式安装即可&#xff08;NEXT&#xff09; 2.软件测试 在Windows桌面空白处&#xff0c;点击鼠标右键&#xff0c;弹出右键菜单 Git软件安装后&#xff0c;会在右键菜单中增加两个菜单 Git GUI He…

【PyQt5桌面应用开发】3.Qt Designer快速入门(控件详解)

一、Qt Designer简介 Qt Designer是PyQt程序UI界面的实现工具&#xff0c;可以帮助我们快速开发 PyQt 程序的速度。它生成的 UI 界面是一个后缀为 .ui 的文件&#xff0c;可以通过 pyiuc 转换为 .py 文件。 Qt Designer工具使用简单&#xff0c;可以通过拖拽和点击完成复杂界面…

仿12306校招项目业务三(用户注册)

用户表结构 原本的表结构如下 由于用户量大&#xff0c;采用分库分表&#xff1a; 分库分表设计 根据系统设计的假设&#xff0c;12306 的注册用户规模约为 10 亿&#xff0c;每年新增用户约 1000 万。在用户数据分库或分表之前&#xff0c;我们需要先考虑拆分成多少个库或表…

力扣模板题:回文链表

请牢记检测回文串的模板 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ bool isPalindrome(struct ListNode* head) {int size0;struct ListNode* pointhead;while(point){size;pointpoint->next;}int arr…

Mac使用K6工具压测WebSocket

commend空格 打开终端&#xff0c;安装k6 brew install k6验证是否安装成功 k6 version设置日志级别为debug export K6_LOG_LEVELdebug执行脚本&#xff08;进入脚本所在文件夹下&#xff09; k6 run --vus 100 --duration 10m --out csvresult.csv script.js 脚本解释&…

[AutoSar]BSW_Com03 DBC详解 (二)

目录 关键词平台说明一、前言二、DBC Attributes2.1 添加方法2.2 GenMsgCycleTime2.3 GenMsgSendType2.4 GenSigStartValue 三、DBC Attributes 对照表 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c;芯片厂商TI 英…

vim恢复.swp [BJDCTF2020]Cookie is so stable1

打开题目 扫描目录得到 关于 .swp 文件 .swp 文件一般是 vim 编辑器在编辑文件时产生的&#xff0c;当用 vim 编辑器编辑文件时就会产生&#xff0c;正常退出时 .swp 文件被删除&#xff0c;但是如果直接叉掉&#xff08;非正常退出&#xff09;&#xff0c;那么 .swp 文件就会…

select a,b,c from 表 where b=1 and c=1; abc是联合索引,这样查询会命中索引吗?

如果select 只显示索引列 那么会命中索引 如果select * 那么不会走索引&#xff0c;会查全表

介绍 PIL+IPython.display+mtcnn for 音视频读取、标注

1. nn.NLLLoss是如何计算误差的? nn.NLLLoss是负对数似然损失函数&#xff0c;用于多分类问题中。它的计算方式如下&#xff1a;首先&#xff0c;对于每个样本&#xff0c;我们需要将其预测结果通过softmax函数转换为概率分布。softmax函数可以将一个向量映射为一个概率分布&…

力扣LCR 140. 训练计划 II(顺序遍历,快慢指针)

Problem: LCR 140. 训练计划 II 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1&#xff1a;顺序遍历 欲返回倒数第cnt个节点则需要顺序遍历到len-cnt&#xff08;其中len为链表的长度&#xff09; 思路2&#xff1a;快慢指针 让一个快指针fast指向cnt 1个节点&#x…

2024022502-数据库绪论

数据库绪论 数据管理的三个阶段 人工管理阶段 文件系统阶段 数据库系统阶段 基本术语 数据&#xff08;Data&#xff09; 计算机用来描述事物的记录&#xff08;文字&#xff0e;图形&#xff0e;图像&#xff0e;声音&#xff09;数据的形式本身并不能完全表达其内容&am…

综合服务 IntServ

目录 综合服务 IntServ IntServ 定义的两类服务 IntServ 的四个组成部分 流 (flow) 资源预留协议 RSVP RSVP 协议的工作原理 IntServ 体系结构在路由器中的实现 综合服务 IntServ 体系结构存在的主要问题 综合服务 IntServ 综合服务 IntServ (Integrated Services) 可…

电机控制----电机反电动势波形的测量

电机控制----电机反电动势波形的测量 很多人在开发霍尔传感器方波控制时&#xff0c;在如何准确确定出三相绕组的通电顺序方面存在疑惑&#xff0c;在网上找了很多资料都是只给出了相序表&#xff0c;但是真正拿过来引用时却往往对应不了自己的电机&#xff0c;导致项目开发过…

全志H713/H618方案:调焦电机(相励磁法步进电机)的驱动原理、适配方法

一、篇头 全志H713平台&#xff0c;作为FHD投影的低成本入门方案&#xff0c;其公板上也配齐了许多投影使用的模组&#xff0c;本文即介绍投影仪调焦所用的步进电机&#xff0c;此模组的驱动原理、配制方法、调试方法。因为条件限制&#xff0c;本文采用的是H618香橙派Z3平台&…