热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

游戏服务端之C++游戏服务端防崩溃

在游戏开发当中,不用我多说了,游戏的稳定性是相当重要的。为了追求游戏的稳定性,很多开发者都会选择脚本语言作为游戏的主要开发语言。因为使用脚本语言,即使游戏出现重大的bug,由于脚本支持热更的天然优势,

在游戏开发当中,不用我多说了,游戏的稳定性是相当重要的。

为了追求游戏的稳定性,很多开发者都会选择脚本语言作为游戏的主要开发语言。因为使用脚本语言,即使游戏出现重大的bug,由于脚本支持热更的天然优势,使开发者更迅速地解决问题;而且脚本的良好容错性,也会使游戏系统不会轻易崩溃。但是,一些即时性游戏需要更高性能的,开发者可能会选择C/C++作为开发语言,但是怎样来保证游戏的稳定性呢?

关键词是 信号

其实在Linux系统中防崩溃的相关内容,我已经在以前的一篇博文粗略提及过。下面再更详细地描述一下。

#ifndef SHOWCRASH_H
#define SHOWCRASH_H

#include
#include
#include
#include
#include
#include
#include
#include "globalvariable.h"
#include "globalfunction.h"
using namespace std;


class ShowCrash
{
public:
ShowCrash();

static void CrashFunction(int);

static const int MAX_JMP_BUF = 16;

static const int MAX_CALLSTACK_DEPTH = 32;

enum buffername
{
BUF_MAIN ,
BUF_COUNT
};


private:


};

extern int buf_index;

#define SETJMP(index)\
setjmp(buff[index]);\
buf_index = index;\


extern ShowCrash g_showcrash;
extern jmp_buf buff[ShowCrash::MAX_JMP_BUF];

#endif

#include "showcrash.h"



ShowCrash g_showcrash;
jmp_buf buff[ShowCrash::MAX_JMP_BUF];
int buf_index;

ShowCrash::ShowCrash()
{
struct sigaction act, oact;
act.sa_handler = CrashFunction;
sigemptyset(&act.sa_mask); //娓呯┖姝や俊鍙烽泦
act.sa_flags = SA_NODEFER;
sigaction(SIGINT, &act, &oact);
sigaction(SIGABRT, &act, &oact);
sigaction(SIGSEGV, &act, &oact);
sigaction(SIGFPE, &act, &oact);
sigaction(SIGBUS, &act, &oact);
sigaction(SIGTRAP,&act,&oact);
}

void ShowCrash::CrashFunction(int)
{
static void *traceback[MAX_CALLSTACK_DEPTH];
static string cmd;
if (cmd.length() <= 0)
{
ProgramName name;
GLOBALFUNCTION::GetProgramName(name);
string temp = name;
cmd = "addr2line -f -e " + temp;
}

FILE *fp = popen(cmd.c_str(), "w");
int depth = backtrace(traceback, MAX_CALLSTACK_DEPTH);
for (int i = 0; i {
fprintf(fp, "%p\n", traceback[i]);
}
fclose(fp);
longjmp(buff[buf_index],1);
}


下面是测试用例

#include 
#include

#include "globalvariable.h"
#include "luaengine.h"
#include "gamesocket.h"
#include "log.h"
#include "dll.h"
#include "MyDll.h"
#include "gametime.h"
#include "frame.h"
#include "datatable.h"
#include "showcrash.h"
#include "globalfunction.h"

int main()
{
int i = -3;
SETJMP(ShowCrash::BUF_MAIN);
while (i <2)
{
i++;
printf("%d\n",10/i);
}
return 0;

}

在上面的测试用例可以看出,如果按一般的情况,当i加到0的时候,10会除0崩溃,程序就会终止执行。

但是当我加一句SETJMP(ShowCrash::BUF_MAIN);程序就会发生奇妙的变化!


程序不仅继续执行了,而且还输出了出错的行数,在main.cpp的23行中出错。

为什么会这样呢?

原因是这样的,当程序发生致命的错误时,系统会向程序发送信号,而这些信号的默认处理是终止程序的的继续执行。

例如:

SIGABRT 异常终止

SIGINT 终端中断符(在Linux中经常在终端使用的Ctrl + c)

SIGSEGV 无效内存引用

SIGFPE 算术异常(就是我上面的测试用例了)

等等

但是,我们可以捕获系统的信号,捕获了信号我们不让程序退出,而是做一些其它的处理,例如打印出错的位置。这样就提高了程序的稳定性了。

单单上面的代码是不能正常执行的,因为里面还有这样的一个函数GLOBALFUNCTION::GetProgramName(name);,这个主要是获得当前程序的程序名。

#include "globalfunction.h"
namespace GLOBALFUNCTION
{
void GameInit()
{

}


void GetProgramName(ProgramName name)
{
static char filename[PROGRAMNAMELEN]={'\0'};
if (filename[0] != '\0')
{
memcpy(name, filename, PROGRAMNAMELEN * sizeof(char));
}
char sysfile[15] = "/proc/self/exe";

if ( -1 == readlink( sysfile,filename,PROGRAMNAMELEN ) )
{
memcpy(filename,"GameEgine",sizeof("GameEngine"));
}
memcpy(name, filename, PROGRAMNAMELEN * sizeof(char));
}

}

加上这个函数,就可以正常执行了。

最后总结一下:(PS:详细的关键字,自己搜索一下)

1、为了不让程序崩溃,主要是signal,在构造函数里面已经做了

2、打印出错信息,在CrashFunction中,主要通过管道调用addr2line和调用backtrace来实现

3、其实即使程序做了以上两步,程序还是会退出的,程序不退出的关键在setjmp和longjmp

4、其实上面的代码有一个巧妙的用法就是,ShowCrash g_showcrash;,这样就不会重复包含,而且也能初始化signal。


推荐阅读
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Iamtryingtocreateanarrayofstructinstanceslikethis:我试图创建一个这样的struct实例数组:letinstallers: ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了使用C++Builder实现获取USB优盘序列号的方法,包括相关的代码和说明。通过该方法,可以获取指定盘符的USB优盘序列号,并将其存放在缓冲中。该方法可以在Windows系统中有效地获取USB优盘序列号,并且适用于C++Builder开发环境。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • STM32 IO口模拟串口通讯
    转自:http:ziye334.blog.163.comblogstatic224306191201452833850647前阵子,调项目时需要用到低波 ... [详细]
  • 重组蛋白/细胞因子的实验操作
    在我们进行抗体制备、ELISA、药物研究、免疫实验、细胞培养、晶体结构分析等实验时,免不了要和重组蛋白打交道。MCE重组蛋白产品涵盖超过2000种不同功能的重组蛋白& ... [详细]
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼*madebyebhrz*#include#include#include#include#include#include#include ... [详细]
  • 设备模型三(潜谈sysfs)
    前言引出一个问题:假设sysaxx,xx是kobja的属性文件,当对xx进行写操作时,即echo‘1’sysaxx实际上,调用了kobja的ktype中定义的接口函 ... [详细]
  • 一条数据的漫游 XEngine SIGMOD Paper Introduction
    大多数人追寻永恒的家园(归宿),少数人追寻永恒的航向。----瓦尔特.本雅明背景X-Engine是阿里数据库产品事业部自研的OLTP数据库存储引擎, ... [详细]
author-avatar
打酱油的伦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有