??xml version="1.0" encoding="utf-8" standalone="yes"?>久久精品无码一区二区无码,精品久久人人爽天天玩人人妻,久久国产精品国语对白http://www.shnenglu.com/feixuwu/I则独善其nQ达则兼天下?/description>zh-cnTue, 06 May 2025 22:07:15 GMTTue, 06 May 2025 22:07:15 GMT60记录一个栈错误问题的解?/title><link>http://www.shnenglu.com/feixuwu/archive/2013/12/02/204555.html</link><dc:creator>feixuwu</dc:creator><author>feixuwu</author><pubDate>Mon, 02 Dec 2013 12:51:00 GMT</pubDate><guid>http://www.shnenglu.com/feixuwu/archive/2013/12/02/204555.html</guid><wfw:comment>http://www.shnenglu.com/feixuwu/comments/204555.html</wfw:comment><comments>http://www.shnenglu.com/feixuwu/archive/2013/12/02/204555.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/feixuwu/comments/commentRss/204555.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/feixuwu/services/trackbacks/204555.html</trackback:ping><description><![CDATA[<h1>问题  </h1>   上周开始,我们一个已l在U运行了(jin)?q的游戏H然频繁宕机Q宕机前刚好上了(jin)一个资料片Q提交了(jin)大批量的代码?br />比较ȝ(ch)的是宕机的core文g里没有Q何有效CallStack信息。在随后的多ơ宕机core文g里也都找不到有效的CallStack信息Q定位问题变得无从入手?br /><br /><h1>原因<br /> <br /><p><span style="font-weight: normal; font-size: 12pt;">   Ҏ(gu)l验Q这是一个典型的栈破坏问题。一旦栈破坏?jin)函数返回值后Q堆栈完全是错ؕ的,得不CQ何有效信息?br />最开始我目l的同事查看最q提交的代码Q看看能否找到线索。不q由于近一个月提交的代码实在太多,大v捞针?jin)一D|间后Q?br />毫无头A?br />   栈覆盖一般是因ؓ(f)memcpy或者是循环赋D句导致的Q一般栈覆盖的层ơ不?x)太多,所以从底部往(xin)上找Q应该能扑ֈ些有效的U烦(ch)?br />不过Q由于服务器函数l常?x)有Package的(f)时变量,D函数栈很大,从下往(xin)上找U烦(ch)也很困难Q很多似是而非的合法地址很容易分散精力?br /></span></p></h1><h1><font size="3"><span style="font-size: 24pt;">解决<br /><br /></span></font><p><font size="3"><span style="font-size: 12pt; font-weight: normal;">  按照上面的分析,从底部往(xin)上找是大h针,那么从顶部往(xin)下找如何呢?<br />q里先说明下一般函数堆栈的徏?未优化情况下的用户函?:<br />push rbp<br />mov rbp, rsp<br />从这里可以看出,本层函数的返回值是存储?[rbp + 8]Q而上层函数的rbp地址则存储在 [rbp]?br />所以,从下|上扄时候,可以Ҏ(gu)rbp逐步扑ֈ上层函数和上层函数的堆栈帧?br /><br />那么如何往(xin)下找呢,假如知道?jin)一个上层函数的rbpQ如何获取下层函数呢Q?br />q里有个窍门,gdb7.X的版本有一个find功能Q可以在内存区域搜烦(ch)数|<br />从上往(xin)下找的时候,可以在堆栈查找本?rbp的存攑֜址Q从而确定下层函数rbp的存攑֜址?br />举个例子:<br /><br /></span></font></p><div><span style="font-weight: normal; font-size: 12pt;">#0  0x00007ffff77d7830 in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6</span></div><div><span style="font-weight: normal; font-size: 12pt;">#1  0x00007ffff77d76ec in sleep () from /lib/x86_64-linux-gnu/libc.so.6</span></div><div><span style="font-weight: normal; font-size: 12pt;">#2  0x000000000040070a in test1 () at main.cpp:9</span></div><div><span style="font-weight: normal; font-size: 12pt;">#3  0x0000000000400715 in test () at main.cpp:14</span></div><div><span style="font-weight: normal; font-size: 12pt;">#4  0x000000000040072b in main (argc=1, argv=0x7fffffffe648) at main.cpp:19<br /><br /></span><span style="font-weight: normal; font-size: 12pt;">q是一个典型的CallStack,让我们先扑ֈ</span><span style="font-size: 16px; font-weight: normal;">0x000000000040072b的堆栈信息吧?br />?info r 查看当前的寄存器信息:<br /></span><div><span style="font-weight: normal; font-size: 12pt;">得到 rsp?/span><span style="font-size: 14px;">0x7fffffffe358</span></div><span style="font-size: 16px; font-weight: normal;"><br /></span><div><span style="font-weight: normal; font-size: 12pt;"></span></div><div><span style="font-weight: normal; font-size: 12pt;">find $rsp, +0x300, 0x000000000040072b</span></div><div><span style="font-weight: normal; font-size: 12pt;">0x7fffffffe548</span></div><div><span style="font-weight: normal; font-size: 12pt;">1 pattern found.<br /><br />只有一个地址Q那么存放rbp的地址是</span><span style="font-size: 16px; font-weight: normal;">0x7fffffffe540?jin)?br /></span></div><div><span style="font-weight: normal; font-size: 12pt;">l箋(hu) find $rsp, +0x300, </span><span style="font-size: 16px; font-weight: normal;">0x7fffffffe540</span><span style="font-weight: normal; font-size: 12pt;"><br /></span><div><span style="font-weight: normal; font-size: 12pt;">0x7fffffffe530</span></div><div><span style="font-weight: normal; font-size: 12pt;">1 pattern found.<br />验证下是否正:(x)<br />x/10xg </span><span style="font-size: 16px; font-weight: normal;">0x7fffffffe530<br /><br /></span><div><span style="font-weight: normal; font-size: 12pt;">0x7fffffffe530: 0x00007fffffffe540      </span><span style="font-size: 12pt;">0x0000000000400715</span></div><div><span style="font-weight: normal; font-size: 12pt;">0x7fffffffe540: 0x00007fffffffe560      0x000000000040072b</span></div><div><span style="font-weight: normal; font-size: 12pt;">0x7fffffffe550: 0x00007fffffffe648      0x0000000100000000<br /><br />看到?jin)吧Q就是这hC(jin)下一U的函数?br />真实环境中往(xin)往(xin)没这么简单,有时候会(x)扑ֈ好几个地址Q这个时候需要自己逐个M存真?jin)?br /></span></div></div></div></div></h1><p> </p><img src ="http://www.shnenglu.com/feixuwu/aggbug/204555.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/feixuwu/" target="_blank">feixuwu</a> 2013-12-02 20:51 <a href="http://www.shnenglu.com/feixuwu/archive/2013/12/02/204555.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Bugȝ一(场景理问题)http://www.shnenglu.com/feixuwu/archive/2012/07/15/183613.htmlfeixuwufeixuwuSun, 15 Jul 2012 14:13:00 GMThttp://www.shnenglu.com/feixuwu/archive/2012/07/15/183613.htmlhttp://www.shnenglu.com/feixuwu/comments/183613.htmlhttp://www.shnenglu.com/feixuwu/archive/2012/07/15/183613.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/183613.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/183613.html

场景理

   Z(jin)说明Bug产生的原因,先描qC场景理的实现方式吧?/span>

  1、游戏场景是游戏地图的一个实?假设地图是class)Q一个地囑֏以创建多个场景,场景主要负责理玩家的移动、广播等处理?/span>
  2、场景的q播是采取经典的?ji)宫格方式来实现的,每一个格子的我们定义为Area对象,一个场景的格子l成其实是一个二l数l?/span>
  3、玩家进入场景的时候,Ҏ(gu)坐标可以知道要进入哪个格子,每个格子内会(x)保留一个Head指针Q标记最新进入的玩家对象。玩家对象上?个指针,标记玩家所在格子的前一个和后一个对象。可以通过格子内的Head指针便利Area内的所有玩家对象?/span>
  4、玩家移动切换格子的时候,先从原来的格子内LeaveQ这?x)调用原来Area对象的Leave函数。再q入新的格子Q调用Area的Enter函数。很明显QLeave函数是一个链表删除操作,如果玩家是HeadQ则讄新的Head?/span>
   Enter操作是新q入的玩安接到原队列里Q新q入的玩家会(x)被设|ؓ(f)Head?/span>

问题表现  

   Ҏ(gu)上面的描qͼ如果一切按照正常程序,q个Ҏ(gu)q{是没问题的。最初上U的时候,也没有出现问题,但是在出?jin)一个资料片之后Q服务器基本上每隔半时左右׃(x)发现有死循环或者宕机问题?/span>

d@环的表现很明显,是在遍历场景玩家的时候,出现d@环。宕机则更加复杂些,每次宕机位置不同Qȝ来说大概?-4个地方,每个地方单看都不合常理?/span>
直接分析上面的表玎ͼ都找不到真正的的原因Q只好扩大搜索范围了(jin)?/span>
比较倒霉的是那个资料片的主要pȝ都是我开发的Q所以自然嫌疑最大,然后大家集中_֊来分析我的代码,׃每个人风格都不同Q所以大家看的不太明白的地方都会(x)来问我,所以那个晚上基本就在解{设计疑问了(jin)?/span>
被轮?jin)大半个晚上Q知道凌?点,大家也没分析出问题,只好先回ȝ觉了(jin)。结果早?点,试l我打电(sh)话了(jin)Q没办法只好跑过M(jin)Q一到公司,发现围了(jin)一堆老大Q老大们很严肃Q这个问题很严重Q必d快解冟?/span>
没办法,只好l箋(hu)上阵?jin),战斗C?点,H然灵光一闪,惛_?jin)原因,当时感觉真的心(j)力交瘁了(jin),更加感慨的是其实q个问题真和我没啥关pR。?/span>


原因

   真正Dq次事故的其实是一个小操作:玩家重登?手机玩家断网的时候,服务器会(x)保存?sh)D|间在U状?的时候,有的时候由于其他原因,?x)卡在不能地囄物理?不能行动的点)Q玩家完全不能移动。ؓ(f)?jin)解册个问题,有个同事在玩安d的时候,直接讄?jin)玩家的坐标C个可Ud的点?/span>
q个看似无关紧要的操作,真正D?jin)服务?天多旉内不停的宕机。下面来记录下分析过E吧?/span>
1、玩家在重登录前Q其实是在场景中的,也就是在一个具体的Area里?/span>
2、由于玩家上U后Q直接设|了(jin)坐标Q而我们后l的计算是通过坐标来获取Area对象的,其实q里出现问题(sh)(jin)Q玩家其实是在A格子的链表上Q但是根据坐标计获得的格子是B?/span>
3、玩家移动后Q切换格子,需要从原格子Leave,然后q入新的格子Q但是基于上面的原因Q所以其实涉?qing)到的?个格子,(1)、玩家真实所在的格子链表(A)?2)、通过坐标计算所得的格子(B)Q这个格子对象上?x)调用Leave操作?/span>
  (3)要进入的新格?C)?/span>
4、由于玩家其实在格子A,但是我们调用的是B.Leave(player);C.Enter(player)Q从q里看,肯定是有问题的,但是l看则不?dng)׃玩家对象是记录?jin)前一个和后一个对象,所以B.Leave本nq不?x)破坏B的链表结构,C.Enter看上M没问题,那么Q问题在哪里Q?/span>
5、真正的原因其实是格子A对象被破坏了(jin)QB.Leave(player)上是玩家从它自q链表上删除了(jin)Q链表本w是没有被破坏了(jin)Q关键的原因是如果玩家在格子A是Head,那么实际上在玩家被删除后QHead应该被改变,但是׃操作的是格子BQ所以,A其实被破坏了(jin)Q很奇妙Q这个对象没有操作,却被破坏?jin)。后面的问题q单了(jin)Q如果玩家进入的C是A,则会(x)是一个很明显的死循环Q如果玩家进入的C是一个新的格子,则格子A的对象都不能被感知了(jin)?/span>

 

 

 



feixuwu 2012-07-15 22:13 发表评论
]]>
目开发中的一些思?/title><link>http://www.shnenglu.com/feixuwu/archive/2012/02/16/165779.html</link><dc:creator>feixuwu</dc:creator><author>feixuwu</author><pubDate>Thu, 16 Feb 2012 13:00:00 GMT</pubDate><guid>http://www.shnenglu.com/feixuwu/archive/2012/02/16/165779.html</guid><wfw:comment>http://www.shnenglu.com/feixuwu/comments/165779.html</wfw:comment><comments>http://www.shnenglu.com/feixuwu/archive/2012/02/16/165779.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.shnenglu.com/feixuwu/comments/commentRss/165779.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/feixuwu/services/trackbacks/165779.html</trackback:ping><description><![CDATA[<p><span style="font-size: 12pt">      </span><span style="font-size: 10pt">  2011已经谢幕?jin),现在都流行ȝQ要是让我ȝ2011Q可以用2个词来概括,辛苦、刺Ȁ?/span><br /><span style="font-size: 10pt">辛苦是因?011基本上是加了(jin)一q班Q从q完q开始,?012q过q前最后一周,q一q来Q是我感觉最辛苦的一q_(d)好在最l?/span><br /><span style="font-size: 10pt">目是打了(jin)个翻w仗Q心(j)里ȝ有了(jin)些慰藉?br /><br /></span>       <span style="font-size: 10pt">2011q游戏经历从技术封、内、公到整改、重新内公,一路走来,遇到无数E奇古怪的BugQ?/span><br /><span style="font-size: 10pt">有时候压力大的时候,晚上都睡不着Q脑子里回想着现场的一丝丝蛛丝马迹Q希望能扑ֈbug的原因,l历q无数次l望到重生的喜?zhn)Q也有被猜忌不信ȝ痛苦Q活p是一部部侦探剧情?/span><br /><span style="font-size: 10pt">  没有从事q游戏开发或者游戏没上线的同学很隄?bug有这么难扑֐Q的,如果是简单的I指针宕机,当然是好扄Q用我们的话Q这c问题是个傻子都能解?其实不然Q很多时候直接原因是I指针,</span><br /><span style="font-size: 10pt">真正的原因隐藏很?Q但是更多的是隐藏很q问题Q需要反复的分析现场Q假讑։情才能得到灵感,然后推演Q才可能得到l果Q当?dng)q个和游戏逻辑的复杂度是分不开的?/span><br /><span style="font-size: 10pt">  具体的bugl节不便在此分析Q但是大部分的问题,其实都是因ؓ(f)不正常的设计引v的,所以其实我一直在思考,在Y件开发领域,其实也存在着"?,说通俗点叫客观规律Q不按照道行事,q早是要受到惩罚的?/span><br /><span style="font-size: 10pt">但是在游戏后台开发中Q很多时候存在不同技术方案的矛盾Q难以让人取舍,q些矛盾都是真实在很多项目存在的?/span><br /><br /></p> <h2></h2> <h2><span style="font-size: 24pt">动态内存还是静(rn)态内?/span></h2> <p><font size="3"><span style="font-weight: normal; font-size: 12pt">     </span><span style="font-weight: normal; font-size: 10pt"> 很多开发者由于担?j)内存泄Ԍ在项目中止使用动态内?当然q实际上几乎是做不到?Q用对象池来避免动态内存,是预先创徏预计最大数量的对象Q后l申请和归还的时候,都是操作对象池,<br /></span><span style="font-weight: normal; font-size: 10pt">避免动态new和delete。这L(fng)目q(sh),我见q的好几个。对象池的好处是显而易见的Q基本上可以避免内存泄露。但是实际上Q这U方式是把双刃剑Q个得在游戏目中,q种方式弊大于利?br /></span><span style="font-weight: normal; font-size: 10pt">主要弊端有下面几点:(x)<br /></span><span style="font-weight: normal; font-size: 10pt">1、开发不方便Q导致需要添加很多的对象池管理类Q即使有模板帮忙Q也是非常繁琐的。实际开发中Q几乎不可能对这些小对象c都搞一个对象池理cR?br /><br /></span><span style="font-weight: normal; font-size: 10pt">2、由于采用预先生成对象,一般会(x)预估一个对象可能存在的最大数量,然后按照最大数量来创徏Q浪费内存?br /></span><span style="font-weight: normal; font-size: 10pt">  的确Q你没有内存泄露Q但是你启动的时候就需要好几个G的内存,q个是内存浪费,好在现在server开发基本都?4位,没有地址I间的困C(jin)Q但是,在大部分情况下浪费好几个G的内存,<br /></span><span style="font-weight: normal; font-size: 10pt">光想想都有点?j)疼?br /><br /></span><span style="font-weight: normal; font-size: 10pt">3、引入了(jin)新的风险Q由于采用对象池Q申h对象的时候,只是单的pop一个空闲对象就可以?jin),很容易漏掉对象初始化的工作,在回收对象的时候,大部分开发者也很容易漏掉清理工作,或者初始化?br /></span><span style="font-weight: normal; font-size: 10pt">清理工作q于单,q样Ҏ(gu)D新对象被历史操作影响。曾l遇到过一个新FB所有传送点都打不开的问题,是因ؓ(f)历史对象回收时数据没清理D的?br /></span><span style="font-weight: normal; font-size: 12pt"><br /></span><span style="font-weight: normal; font-size: 10pt">    回头来看对象池的优点Q很多开发者坚持是Z(jin)解决内存片和内存泄霌Ӏ先说内存碎片,暂且不说内存片真的是否有这么严重,退一步,其实内存片已经有很多的成熟解决Ҏ(gu)?jin),自己重蝲smallObjectq是<br /></span><span style="font-weight: normal; font-size: 10pt">采用标准的tcmalloc解决Q都是非常轻杄。至于内存泄Ԍ个h觉得q个问题其实是很好查的,也是c++E序员的基本要求?/span><span style="font-weight: normal; font-size: 14pt"><br /><br /></span></font></p> <h2><span style="font-size: 24pt">分模块针Ҏ(gu)口编E还是一锅粥</span></h2> <p><span style="font-weight: normal; font-size: 10pt">     q个问题单独提出来,几乎所有h都会(x)_(d)当然是分模块针对接口开发了(jin)。和天下所有的事情一P知易行难。由于游戏逻辑目影响的地斚w常多Q比如死亡的时候,既需要判断死亡掉落,又需要处理Q务状态,</span><br /><span style="font-weight: normal; font-size: 10pt">如果在战场和竞技ZQ还要判断基数和得分{等Q这导致很多开发者不假思烦(ch)的把所有的东西都揉在一P你中有我Q我中有你,我改你的代码你改我的?/span><br /><span style="font-weight: normal; font-size: 10pt">一个最单的例子Q我在项目中开发掉落功能,当把物品d到玩家背包后Q发现客L(fng)没有更新背包Q一查,居然q需要掉落的开发者自己构造数据包同步客户端,其实作ؓ(f)其他模块Q根本不兛_(j)背包数据同步的细节?/span><br /><span style="font-weight: normal; font-size: 10pt">q个其实在现实生zM很常见,我委托背包模块添加一个物品,具体的细节是被由被委托(sh)h来负责的。将q多的细节交l其他模块处理,?x)导致复杂度增加Q容易出现问题,对其他h来说Q也是一个精力浪费,如果是一个复?/span><br /><span style="font-weight: normal; font-size: 10pt">模块Q你?x)发现需要了(jin)解太多的l节Q修改太多自׃熟?zhn)的代码,q而导致风险。还有一U观点,认ؓ(f)一锅粥的开发方式有助于?jin)解游戏的各个业务模块,对这U观点,我是不以为然的,每天陷入到繁琐的l节Q真的对熟?zhn)业务有好处吗Q或?dng)R下来玩玩游戏更有帮助Q而且Q这么ؕ的代码,看v来也是非常篏的。分模块开发,具体的办法,游戏~程_a(b)5上有文章写得很好,q里不扩展了(jin)?/span><br /><br /><span style="font-size: 24pt">真的需要禁用STL?/span><br /><br /><span style="font-weight: normal; font-size: 10pt">  不止一ơ在和其他项目交的资料里看到对方很威严的宣U在目里禁止用STL。说实话Q我q真没觉得STL有什么不好。见q太多这c项目自己重复实C个个y脚的排序算法、容器等{?br /></span><span style="font-weight: normal; font-size: 10pt">大部分h一般都?x)根据经验选择使用自己熟?zhn)的技术,q个无可厚非Q但是像q样明着止使用STLQ真不知道如何能理直气壮。其实大部分不用STL的理由,基本上都是不熟?zhn)Q完全没有够的理由止使用?/span><span style="font-weight: normal; font-size: 12pt"><br /><br /></span><span style="font-size: 24pt">游戏开发无技术含量?<br /></span> <br /><span style="font-weight: normal; font-size: 12pt">  </span><span style="font-weight: normal; font-size: 10pt">  曄多次听到行业内的兄弟有此感慨Q确实,游戏逻辑复杂度非帔RQ架构上大部分都是类似的。但是这q不说明游戏后台开发复杂度不高Q如何将游戏开发逻辑复杂剥离开来,做到E_高效开发,其实q是有很?br /></span><span style="font-weight: normal; font-size: 10pt">东西可以探讨的,看看那些目Q大部分都是一锅粥Q需要什么功能就蛮干Q加上去Q这L(fng)实毫无技术含量,都是蛮干。所以,一件事情是否有技术含量,不光是看事情本nQ还要看怎么qԌ蛮干和苦qԌ那是最没有技?br /></span><span style="font-weight: normal; font-size: 10pt">含量的方式了(jin)Q程序员q是要有强烈?#8220;h”意识?/span><br /><br /></p> <p> </p><img src ="http://www.shnenglu.com/feixuwu/aggbug/165779.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/feixuwu/" target="_blank">feixuwu</a> 2012-02-16 21:00 <a href="http://www.shnenglu.com/feixuwu/archive/2012/02/16/165779.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下PageHeaphttp://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.htmlfeixuwufeixuwuSat, 14 May 2011 13:16:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.htmlhttp://www.shnenglu.com/feixuwu/comments/146395.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/05/14/146395.html#Feedback1http://www.shnenglu.com/feixuwu/comments/commentRss/146395.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/146395.html 问题   最q游戏开始技术封了(jin)Q不q刚刚上U?个小ӞServer挂?jin),挂在框架代码里,一个不可能挂的地方?br />从CallStack看,是在获取数据时发送请求包的时候挂的,׃框架部分是其他部门的同事开发的Q所以查问题的时候就拉上他们?jin)?br />大家折腾?天,没有实质性的q展Q服务器q是基本上每3个小时宕Zơ。由于上层逻辑大部分都在我那,所以压力比较大Q宕机的直接原因是hashtable的一个桶的指针异常,
q个hashtable是框架代码的一个内部成员,按道理我们是无从破坏的,只有可能是多U程环境下P代器损坏D的?br />但是框架代码在这个地方确实无懈可击,所以真正的原因应该q是上层代码破坏?jin)堆内存Q很可能是一个memcpy界D的。这毕竟是个猜想Q如何找到证据呢Q这是个问题?br />把所有代码里的memcpy览?jin)一遍,没有发现明显问题?br />

猜测

  一般游戏中比较Ҏ(gu)出现但是不好查的问题很多时候都是脚本(luaQ导致的Q我们的脚本部分是一个同事几q前写的Q在几个产品中都使用q,按道理没q么脆弱Q不q老大q是和最初开发这个模块的部门沟通了(jin)下,
q真发现问题?sh)(jin),赶紧拿?jin)新的版本更新上去。经q一天的观察Q服务器没有宕机?jin),OKQ问题碰巧解决了(jin),背了(jin)q么久的黑锅Q终于放下来?jin)?br />

PageHeap

   假如没有y解决?jin)这个问题,正常的思\该如何解册个问题呢Q这个时候我怀念windows?jin),在windows下有PageHeap来解册cd界的问题。基本思\是每次分配内存的时候,都将内存的结放在页的边~,紧接着q块内存分配一块不能写的内存,q样Q一旦写界Q就?x)写异常Q导致宕机。linux下没有现成的工具Q但是linux提供?jin)mmap功能Q我们可以自己实现这样一个功能,当然Q这一切都不用自己动手?jin),tcmalloc已经包含?br />q个功能?jin),不过在文档里基本没有介绍Q我也是在阅读tcmalloc代码时看到的Q这个功能默认是关闭的,打开q个开关需要改写代码:(x)

q个代码在debugallocation.cc里:(x)

DEFINE_bool(malloc_page_fence,
            EnvToBool("TCMALLOC_PAGE_FENCE", false),
            "Enables putting of memory allocations at page boundaries "
            "with a guard page following the allocation (to catch buffer "
            "overruns right when they happen).");
把falseҎ(gu)true可以了(jin)?br />惌在项目里加入PageHeap功能Q只需要链接的时候加?-ltcmalloc_debug卛_。把它加入项目中Q试着q行下,直接挂了(jin)Q?br />仔细一看,原来是项目中很多成员变量没有初始化导致的Qtcmalloc_debug?x)自动将new 和malloc出来的内存初始化为指定|q样Q一旦变量没有初始化Q很Ҏ(gu)暴露了(jin)?br />修改完这个问题后Q编译,再运行,q是挂,q个是mprotect的时候挂的,错误是内存(sh)够,q怎么可能呢,其实是达C(jin)资源限制?jin)?br />echo 128000 > /proc/sys/vm/max_map_count
把map数量限制加大,再运行,OK?jin)?br />
 
  但是游戏S(chng)erver启动后,发现一个问题,CPU长期处于100%Q导致登陆一个玩安很困难,gdb中断后,info threadQ发现大部分的操作都在mmap和mprotect,最开?br />怀疑我的linux版本有问题,Dq?个AP慢,写了(jin)试E序试了(jin)下,发现其实API不慢Q估计是频繁调用D的?br />所以得换种思\优化下才可以Q其实大部分情况下,我们free的时候,无需页面munmap掉,可以先cacheq来Q下ơ分配的时候,如果有,直接拿来用就可以?jin)?br />最单的cache法是定义一个void* s_pageCache[50000]数组Q页面数相同的内存组成一个链表,挂在一个数l项下,q个很像S(chng)TL的小内存处理Q我们可以将mmap出来的内存的
前面几个字节(一个指针大?用于索引下一个freePage。当然这个过E需要加锁,不能用pthread的锁Q因Z们会(x)调用malloc{内存分配函敎ͼ(j)Q必ȝspinlockQ从linux源码里直接抄一个过来即可?br />static void*   s_pagePool[MAX_PAGE_ALLOC]={0};

malloc的时候,先从pagePool里面获取:
// 先从pagePool?br /> void* pFreePage = NULL;
 spin_lock(&s_pageHeapLock);
 assert(nPageNum < MAX_PAGE_ALLOC);
 if(s_pagePool[nPageNum])
 {
   pFreePage = s_pagePool[nPageNum];
   void* pNextFreePage = *((void**)pFreePage);
   s_pagePool[nPageNum] = pNextFreePage;
 }
 spin_unlock(&s_pageHeapLock);

free内存的时候,直接攑ֈpagePoll?
spin_lock(&s_pageHeapLock);
 assert(nPageNum < MAX_PAGE_ALLOC);
 void* pNextFree = s_pagePool[nPageNum];
 *(void**)pAddress = pNextFree;
 s_pagePool[nPageNum] = pAddress;
 
 spin_unlock(&s_pageHeapLock);

~译、运?OK?jin),CPUq速降下来?jin),I的时候不?%,而且也能辑ֈ(g)写溢出的问题?br />


feixuwu 2011-05-14 21:16 发表评论
]]>
core和CallStackhttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.htmlfeixuwufeixuwuSun, 10 Apr 2011 06:47:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.htmlhttp://www.shnenglu.com/feixuwu/comments/143871.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/04/10/143871.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/143871.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/143871.html  最q项目开始集中测试了(jin)Q服务器E序l常crashQ由于服务器一般情况下都是关闭?jin)core的,所以好几次都只能通过杂ؕ的日志来定位问题?br>当然Q我们可以通过ulimit来打开core开养I不过q可能带来新的问题:(x)我们的服务器E序每个core文g大概?G多,试期间如果频繁crash,没有注意?qing)时清理Q一不小?j)就会(x)把盘写满Q?br>而且core文g毕竟是和q程E序相关的,有时候找相应版本也是个麻?ch)事?br>
能否在程序crash的时候,callStack以及(qing)参数和局部变量都记录到日志里Q?br>q个技术其实在游戏客户端已l用?jin)很多年了(jin),一般游戏客L(fng)crash后,都会(x)弹出一个是否发送错误的选择框,其实是发送的CallStack的日志和MiniDUmp文g?br>要想记录CallStack必然涉?qing)到Stack的遍历,linux下的Stack遍历使用很简单,单的backtrace可以搞定,man backtrace有现成的例子,
q比windows下复杂的头疼的StackWalk好用的多?br>
解决?jin)Stack遍历问题后,q剩下一个问题:(x)如何在程序crash的时候得到通知执行我们自己的dump代码Q?br>在Windwos下有SEH异常来实现这个功能,而linux下可以通过使用信号在进Ecrash的时候执行自q处理代码?br>
好了(jin)Q开始写个简单代码测试下:
首先讄几个主要crash信号的处理函?br>signal(SIGSEGV, &DumpHelper::OnCrash);
signal(SIGABRT, &DumpHelper::OnCrash);
signal(SIGFPE, &DumpHelper::OnCrash);

在OnCrash里我们用前面提到的backtracepd函数Q来记录堆栈:
void* szStackFrame[100];
int nFrameCount = backtrace(szStackFrame, 100);
char** strFrameInfo = backtrace_symbols(szStackFrame, nFrameCount); 
char szDumpFileName[1024] = {0};
snprintf(szDumpFileName, sizeof(szDumpFileName), "dump_%u.log", (unsigned int)time(NULL) );
FILE* pFile = fopen(szDumpFileName, "wb");
if(!pFile) return;
for(int i = 0; i < nFrameCount; i++)
{
    fprintf(pFile, "%s\n", strFrameInfo[i]);
}
fclose(pFile);
free(strFrameInfo);

接着Q设|几个嵌套调用的函数Q?br>void fun()
{
 //assert(0);
 int* p = NULL;
 *p =3;
}

void fun1()
{
 fun();
}

void fun2()
{
 fun1();
}

void fun3()
{
 fun2();
}

最后,我们在main函数里执行fun3,注意~译的时候带?rdynamic 选项?span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px">

q行下,果然可以打印基本的堆栈,不过马上Q发C(jin)新的问题Q这个堆栈信息也太简陋了(jin)Q只有调用函数的名字Q其余的参数、局部变量完全没有,
q个和gdb能看到的callStack差距也太大了(jin)?br>解决q个问题最单的办法是用gdb来打印堆?在这里,gdb和其他程序有区别Q如果你试图通过 echo "bt"|gdb -p XXX>a.txt来获得堆栈,那将?x)非常失望?br>Ҏ(gu)不v作用Qgoogle?jin)下Q基本没什么解军_法?br>不过gdb 可以从文件读入指令,例如 gdb XXX<cmddataQ这l了(jin)我们Z(x)Q?br>system("echo  \"bt full|gcore\">testcmd");
  char dbx[160]={0};
     sprintf(dbx, "gdb -p %d ./main<testcmd >gdbdump_%d.log", getpid(), getpid() );
  system(dbx);

试q行Q发现可以打印详l的堆栈Q不q,要求机器上有gdb.
上面的命令还dump?jin)一个core文gQ不q这个core文g的堆栈信息是错误的,我不知道Z么。。。?br>
多线E环境下使用上述办法Q只能输Z个线E的堆栈Q需要先获取U程数目Q然后逐个U程打印堆栈?br>
最后,Z(jin)避免影响正常的coredump,要在OnCrash的处理函数里信L(fng)处理函数讄为默认?br>如果我一定要有core呢,setrlimit吧,Lcore限制卛_?/span>

feixuwu 2011-04-10 14:47 发表评论
]]>
GCC目~译速度优化http://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.htmlfeixuwufeixuwuSat, 19 Mar 2011 08:39:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.htmlhttp://www.shnenglu.com/feixuwu/comments/142210.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/03/19/142210.html#Feedback5http://www.shnenglu.com/feixuwu/comments/commentRss/142210.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/142210.html执行文gQ基本不考虑~译成动态库Q所有代码的头文件依赖也是一团糟Q随着目的增大,~译速度来慢Q到后来~译一个项?q程同时~译都需?0来分钟?br> 
  其实分析下可以发玎ͼ主要的编译速度损耗在头文件上Q尤其是模板相关的头文g。VC有一个预~译头文件技术,常用的公共头文件放在一P预先~译成pch文gQ这?br>可以加快~译速度。gcc到底有没有类似技术呢Q打开gcc的手册搜索了(jin)precompiledQ发现还真有相关介绍Q用方法也很简单?br> 
主要是以下步骤:(x)
  1、在目下徏立一?stdafx.h的文Ӟ包含?jin)大部分公共头文件。在每个cpp最开始都#include "stdafx.h"。cpp文g包含?jin)这个预~译头文件后Q就可以原来和
stdafx .h 里头文g重复的内容删除了(jin)Q尤其是模板相关的头文gQ另外,非PCH的头文g里尽量少包含其他头文件?nbsp;    
  2、修改makefile文g, 加入OBJ?gch的依?用一个简单的目做示例,一看就明白
   
TARGET=TimerTest
PCH=stdafx.h.gch
PCH_H=stdafx.h
OBJ=stdafx.o TimerManager.o TimerTest.o

%.o:%.cpp
    g++ -Wall -c -g $^ -o $@

$(TARGET):$(OBJ)
    g++ -g  $^ -o $@


pch.d:stdafx.cpp
    g++ -g -MM stdafx.cpp |sed 's/stdafx.o/stdafx.h.gch/'>$@

-include pch.d

$(OBJ):$(PCH)
$(PCH):
    g++ $(PCH_H)

clean:
    rm -f $(OBJ) $(PCH)

    完成以上内容后,make clean,再重新编译,初步估计只需?分钟Q!  整整优化?-5倍?br>      



feixuwu 2011-03-19 16:39 发表评论
]]>
定时器的实现http://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.htmlfeixuwufeixuwuSun, 13 Mar 2011 14:06:00 GMThttp://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.htmlhttp://www.shnenglu.com/feixuwu/comments/141744.htmlhttp://www.shnenglu.com/feixuwu/archive/2011/03/13/141744.html#Feedback0http://www.shnenglu.com/feixuwu/comments/commentRss/141744.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/141744.html没有定时器,所有需要定时的dQ都只能dcMOnUpdate的函敎ͼ在主循环的时候执行。定旉求少的时候,看不出明昄问题Q但是一旦这U需求多?jin),其是很多内部对象有定时需求的时候,
q个问题比较明显了(jin)Q写好了(jin)OnUpdate后,q要建立一条从d@环MainLoop到自wOnUpdate的调用链?br> 
  事g其实是一个广播和订阅的关p,Delegate是实现q样一套机制的利器Q目前Delegate的实C要有2U,一U是CodeProject上的一个FastDelegate实现Q另外一个比较典型的实现是boost?br>实现?jin),无论采取哪种实现?gu)Q实现难度都不算太大?br>  Server当前框架对定时器无Q何支持,只有一个DoMainLoop的函数可以派生来q行自己的定旉辑?br>  我原来都是用的ACE装的组Ӟ用了(jin)一D|间也没发现明N题,不过ACE的定时器不太适合在这个新目用,主要原因有如下几点:(x)
  1、ACE库太大了(jin)Q不想仅仅ؓ(f)?jin)定时器引入一个这么庞大的库?br>  2、ACE的定时器需要额外启动一个定时器U程Q定时Q务是在定时器U程跑的Q而我们的目逻辑其实是在单个U程q行的,如果直接采用ACE定时器,?x)给逻辑带来额外的复杂度。由于整个逻辑U程的框架是公共模块Q手头也没有代码Q所以将定时器线E的d发送到主逻辑U程q行也是不可行的?br>  3、ACE的定时器有很多种QTIMER_QUEUE、TIMER_WHELL、TIMER_HEAP{,个h感觉q些定时器的插入、取消操作都比较耗时Q加以改装放CU程run的带价将?x)很大?br>
其实linux内核有一个比较高性能的定时器Q代码在kernel/Timer.c里, 2.6内核的定时器代码更是z?br>linux的定时Q务都是以jiffie 为单位的Qlinux所有定时Q务分?个阶梯,
struct tvec {
    struct list_head vec[TVN_SIZE];
};

struct tvec_root {
    struct list_head vec[TVR_SIZE];
};

struct tvec_base {
    spinlock_t lock;
    struct timer_list *running_timer;
    unsigned long timer_jiffies;
    struct tvec_root tv1;
    struct tvec tv2;
    struct tvec tv3;
    struct tvec tv4;
    struct tvec tv5;
} ____cacheline_aligned;

对一个新的定时Q务,处理Ҏ(gu)如下:
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
    unsigned long expires = timer->expires;
    unsigned long idx = expires - base->timer_jiffies;
    struct list_head *vec;

    if (idx < TVR_SIZE) {
        int i = expires & TVR_MASK;
        vec = base->tv1.vec + i;
    } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
        int i = (expires >> TVR_BITS) & TVN_MASK;
        vec = base->tv2.vec + i;
    } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
        vec = base->tv3.vec + i;
    } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
        vec = base->tv4.vec + i;
    } else if ((signed long) idx < 0) {
        /*
         * Can happen if you add a timer with expires == jiffies,
         * or you set a timer to go off in the past
         */
        vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
    } else {
        int i;
        /* If the timeout is larger than 0xffffffff on 64-bit
         * architectures then we use the maximum timeout:
         */
        if (idx > 0xffffffffUL) {
            idx = 0xffffffffUL;
            expires = idx + base->timer_jiffies;
        }
        i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
        vec = base->tv5.vec + i;
    }
    /*
     * Timers are FIFO:
     */
    list_add_tail(&timer->entry, vec);
}
从上可以看到Linux对定时器的处理:(x)对即在TVR_SIZE 个jiffies内到辄定时dQ将它挂到第一ltv1 下,具体是挂到expires & TVR_MASK 对应的列表上厅R?br>同一个jiffies到达的定时器是挂在同一个链表的?br>同理Q挂到第二个l的?到期旉于 1 << (TVR_BITS + TVN_BITS) jiffies的?br>挂到W三个组的是 到期旉于1 << (TVR_BITS + 2 * TVN_BITS) jiffies的?br>挂到W四个组的是 到期旉于 1 << (TVR_BITS + 3 * TVN_BITS) jiffies的?br>过1 << (TVR_BITS + 3 * TVN_BITS) 的挂到第五组?br>q样Q所有到期的d都会(x)在第一l。Q何时刻都可以直接通过当前jiffies&TVR_SIZE 来找到需要运行的定时器Q务列表,定时器的插入效率是O(1)?br>
下面是定时器的运行代码:(x)
static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
    /* cascade all the timers from tv up one level */
    struct timer_list *timer, *tmp;
    struct list_head tv_list;

    list_replace_init(tv->vec + index, &tv_list);

    /*
     * We are removing _all_ timers from the list, so we
     * don't have to detach them individually.
     */
    list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
        BUG_ON(tbase_get_base(timer->base) != base);
        internal_add_timer(base, timer);
    }

    return index;
}

#define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

/**
 * __run_timers - run all expired timers (if any) on this CPU.
 * @base: the timer vector to be processed.
 *
 * This function cascades all vectors and executes all expired timer
 * vectors.
 */
static inline void __run_timers(struct tvec_base *base)
{
    struct timer_list *timer;

    spin_lock_irq(&base->lock);
    while (time_after_eq(jiffies, base->timer_jiffies)) {
        struct list_head work_list;
        struct list_head *head = &work_list;
        int index = base->timer_jiffies & TVR_MASK;

        /*
         * Cascade timers:
         */
        if (!index &&
            (!cascade(base, &base->tv2, INDEX(0))) &&
                (!cascade(base, &base->tv3, INDEX(1))) &&
                    !cascade(base, &base->tv4, INDEX(2)))
            cascade(base, &base->tv5, INDEX(3));
        ++base->timer_jiffies;
        list_replace_init(base->tv1.vec + index, &work_list);
        while (!list_empty(head)) {
            void (*fn)(unsigned long);
            unsigned long data;

            timer = list_first_entry(head, struct timer_list,entry);
            fn = timer->function;
            data = timer->data;

            timer_stats_account_timer(timer);

            set_running_timer(base, timer);
            detach_timer(timer, 1);
            spin_unlock_irq(&base->lock);
            {
                int preempt_count = preempt_count();
                fn(data);
                if (preempt_count != preempt_count()) {
                    printk(KERN_ERR "huh, entered %p "
                           "with preempt_count %08x, exited"
                           " with %08x?\n",
                           fn, preempt_count,
                           preempt_count());
                    BUG();
                }
            }
            spin_lock_irq(&base->lock);
        }
    }
    set_running_timer(base, NULL);
    spin_unlock_irq(&base->lock);
}
当第一l运行完一轮后Q需要将tv2的一l新的定时Q务加到第一l。这好比时钟的指针Q秒针运行一圈后Q分针步q一|后箋(hu)的调整都是类伹{?
cascade 是负责下一l的定时dd到前面的d阶梯。只有当W一轮的定时d全部q行完毕后,才会(x)需要从W二轮调入新的Q务,只有W二U别的Q务都调入完毕后,才需要从W三轮的定时d调入新的dQ?br> if (!index &&
            (!cascade(base, &base->tv2, INDEX(0))) &&
                (!cascade(base, &base->tv3, INDEX(1))) &&
                    !cascade(base, &base->tv4, INDEX(2)))
            cascade(base, &base->tv5, INDEX(3));

q就是负责调整的代码Q相当的z?br>参照上述代码实现一个定时器后,加入4000个定时Q务:(x)
    for(int i = 1; i < 4000; i++)
    {
        g_TimerHandle[i] = g_timerManager.setTimer(&tmpSink1, i, i*10, "ss");
    }
?0毫秒?000*10毫秒,q行后,试下性能Q?br>函数?nbsp;                                   执行ơ数    最时?nbsp;    q_旉       最大时?br>TimerManager::runTimer    2170566        10              10               3046   
可以看到Q除?jin)个别时间是因?f)U程切换D数据比较大外,q_每次q行runTimer的时间是10微秒?br>q个旉q包括每个定时器的执行消耗,效率q是不错的?br>

feixuwu 2011-03-13 22:06 发表评论
]]>
Ogre初体?/title><link>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html</link><dc:creator>feixuwu</dc:creator><author>feixuwu</author><pubDate>Sat, 25 Sep 2010 13:44:00 GMT</pubDate><guid>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html</guid><wfw:comment>http://www.shnenglu.com/feixuwu/comments/127669.html</wfw:comment><comments>http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.shnenglu.com/feixuwu/comments/commentRss/127669.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/feixuwu/services/trackbacks/127669.html</trackback:ping><description><![CDATA[  最q游戏又要封了(jin)Q工作比较紧张,晚上下班?jin)比较篏Q回家懒得写代码?jin),不过Z倒是l箋(hu)完成?jin)?新剑侠情~(和月׃说的资源格式相同Q的资源逆向。完成了(jin)资源逆向后,H然兴致来了(jin)Q写?jin)个单的地图查看器,到目前ؓ(f)止,一切运行正常。后来做?jin)个单的DemoQ实C(jin)基本的寻路和技能动L放,其实新剑侠情~原本的技能效果以今天的眼光看h也还可以Q即便如此,我还是集成了(jin)hge的粒子系l进去,试了(jin)下效果,q是挺奇怪的?br>做完?jin)这些之后,本想为我的PSP山寨一个新剑侠情缘。不料后来连l加?jin)好几天班,加?jin)几天班之后,Z懒了(jin)Q山寨游戏的事情也就无疾而终?jin)?br>前面写过几篇逆向工程的文章,前几天翻出来看了(jin)下,感觉像是另一个h写的天书Q我自己看自q文章且如此Q别人就更不用说?jin),其实对大部分言Q关?j)的只是逆向的成果。对新剑侠情~的资源和相x(chng)染感兴趣的朋友可以单独Email我?<br>  开始阅读Ogre代码正是在这百无聊赖的状态下开始的QOgre推出来很多年?jin),貌?5q就听说朋友说vq这个项目,不过我一向是专注服务端开发,对客L(fng)开发经验不是很多,?D领域完全是的新手了(jin)Q所以一直也没仔l研I。这几天拿v原来下蝲的一个版本,单读?jin)下代码?br>Ogre的结构还是很清晰的,和手册上说的一P主要是那几个对象,Demo大部分也很简单,代码量不多,看v来很振奋人心(j)?br>但是Ҏ(gu)q样的新手来_(d)首先想了(jin)解的当然是渲染流E?Ogre的渲染流E确实会(x)?D新手不适应Q它是从RenderTarget开始的Q一个RenderTarget可以有几个ViewPortQ每个ViewPort都有一个独立的摄像机,q可以实现同屏幕多个渲染?br>通过ViewPort对象的update调用<br> mCamera->_renderScene(this, mShowOverlays); <br>来执行场景渲染,而场景渲染里Q最重要的要_findVisibleObjects?jin)?br>q个函数可见的物体d到渲染队列里Q这个函数非常的l,里面q用C(jin)Vistor,_不好Ҏ(gu)被绕晕,好在我挺住了(jin)Q熬q来?jin)?br>熟?zhn)了(jin)大致的渲染程后,我觉得该写点东西来实战?sh)(jin)?br>3D教程的开始一般会(x)教大家画三角形,所以我也想用OgreM三角形玩玩,<br>一开始,我也想从像那些Demo一样从ExampleApplicationl承Q不q我发现q样启动太慢?jin),而且我不需要加载那么多的材质,<br>所以自己手动Configure?jin),代码如?<br>Ogre::LogManager* pLogManager = new Ogre::LogManager;<br>    Ogre::Log* pLog = pLogManager->createLog("ogreLearn1.log");<br>    pLog->setDebugOutputEnabled(true);<br><br>    Ogre::Root* pRootObject = new Ogre::Root;<br>    pRootObject->loadPlugin("RenderSystem_Direct3D9_d.dll");<br>    pRootObject->loadPlugin("Plugin_OctreeSceneManager_d.dll");<br>    <br>    Ogre::RenderSystem* pRenderSystem = pRootObject->getRenderSystemByName("Direct3D9 Rendering Subsystem");<br>    pRenderSystem->setConfigOption("Full Screen", "False");<br>    pRootObject->setRenderSystem(pRenderSystem);    <br>    Ogre::RenderWindow* pRenderWindow = pRootObject->initialise(true); <br><br>~译试?jin)下Q可以正常运行,不过发现屏幕是花的,我还没有创徏场景呢,l箋(hu)d摄像机和ViewPort以及(qing)场景<br>// 创徏场景和摄像机以及(qing)ViewPort<br>    Ogre::SceneManager* pSceneManager = pRootObject->createSceneManager(Ogre::ST_GENERIC, "OgreLearn1");<br>    Ogre::Camera* pCamera = pSceneManager->createCamera("MainCamara");<br>    pCamera->setPosition(0.0, 0.0, -20.0);<br>    pCamera->lookAt(0, 0, 0);<br>    pCamera->setNearClipDistance(2);<br><br>    Ogre::Viewport* pViewPort = pRenderWindow->addViewport(pCamera);<br>    pViewPort->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 1.0f) );<br>    pCamera->setAspectRatio(pViewPort->getActualWidth()/pViewPort->getActualHeight() ); <br><br>最后加上pRootObject->startRendering(); <br> ~译q行Q一切正常,屏幕颜色也变成了(jin)惌的黑Ԍ恩,下一步该d三角形了(jin)Q我不太喜欢用OgreManualObjectQ一堆的J琐操作。这里用自定义的Mesh来绘?角Ş?br>pSceneManager->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2) );<br>        Ogre::MeshPtr pMeshData = Ogre::MeshManager::getSingleton().createManual("Learn", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);<br>        Ogre::SubMesh* pSubMesh = pMeshData->createSubMesh();<br>        pSubMesh->useSharedVertices = false;<br>        pSubMesh->vertexData = new Ogre::VertexData;<br>        pSubMesh->vertexData->vertexStart = 0;<br>        pSubMesh->vertexData->vertexCount = 3;<br><br> 先设|了(jin)环境?其实没啥用,我后面会(x)止)Q然后创Z(jin)一个自定义的Mesh,<br>紧接着的是创徏一个SubMeshQ要知道Ogre中最的|格是SubMesh,创徏好SubMesh后,要填充网格结构了(jin)Q?br>创徏?jin)一个VertexData,讄点数目?Q也是一个三角ŞQ,下面该定义顶Ҏ(gu)式了(jin)Q?br>Ogre::VertexDeclaration* pDecle = pSubMesh->vertexData->vertexDeclaration;<br>        size_t sOffset = 0;<br>        pDecle->addElement(0, sOffset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);<br>        sOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);<br>        pDecle->addElement(0, sOffset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);<br>        sOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR); <br><br>上述代码定义?jin)顶?gu)式,只有基本的坐标和颜色?br>下一步将是申h存,填充点l构?br>Ogre::HardwareVertexBufferSharedPtr vBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(sOffset, 3, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);<br>        float* pReal = static_cast<float*>(vBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));<br>        Ogre::RGBA* pColor = NULL;<br><br>        *pReal++ = -2.0f;<br>        *pReal++ = 0.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(1.0f, 0.0, 0, 0.0f), pColor);<br>        pReal = (float*)(pColor+1);<br>        <br>        *pReal++ = 0.0f;<br>        *pReal++ = 2.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(0.0f, 0, 1.0, 1.0f), pColor);<br>        pReal = (float*)(pColor+1);<br><br><br>        *pReal++ = 2.0f;<br>        *pReal++ = 0.0f;<br>        *pReal++ = 0.0f;<br>        pColor = (Ogre::RGBA*)pReal;<br>        pRenderSystem->convertColourValue(Ogre::ColourValue(1.0f, 0, 0, 1.0f), pColor);<br>        pReal = (float*)(pColor+1);<br>        vBuf->unlock();<br>        pSubMesh->vertexData->vertexBufferBinding->setBinding(0, vBuf);<br>        <br>        pMeshData->load();<br>        pMeshData->_setBounds(Ogre::AxisAlignedBox(-2, 0, -1, 2, 2, 1) ); <br>填充点后,讄|格包围盒,q样一个自定义的网格就创徏好了(jin)Q接下来要创Z个用该|格的实体了(jin)<br>    Ogre::Entity* pEntity = pSceneManager->createEntity("TestEntity", "Learn");<br>        pEntity->setMaterialName("BaseWhiteNoLighting");<br><br>        pSceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(pEntity);<br>        pEntity->getParentNode()->setPosition(3, 0, 0);<br>        pEntity->getParentNode()->rotate(Ogre::Quaternion(1.0f, 1.0f, 0, 1.0f) ); <br><br>好了(jin)Q这样实体也创徏好了(jin)Q接下来执行渲染吧:(x)<br>pRootObject->startRendering(); <br><br> <h2>遇到的问?/h2>   上述代码是运行正常的Q但是一开始,我执行的l果是看不到M东西Q跟t了(jin)下,发现实体每次都被摄像剪了(jin)Q才发觉自定义Mesh要自p|包围盒子,<br>讄可包围盒子?br> 讄?jin)包围盒后,数据已经q入?jin)D3D的渲染管道,但是q是没看C角ŞQ仔l观察,原来摄像机对着的是三角形的背面。。?br>调整摄像机后Q终于能看到一个三角Ş?jin),不过是白色的。。?br>从这个症状看Q应该是没有关闭光照D的,但是我明明主动调用RenderSystem关闭光照?jin)啊Q仔l跟t了(jin)下原来是材质在捣乱,<br>默认的材质是开启了(jin)光照的,所以在渲染前的SceneManager::_setPass 的时候,开启了(jin)光照?br>q好办,d讄?jin)关闭光照的材?BaseWhiteNoLighting" 后,l于看到?jin)彩色三角Ş了(jin)?br><br><br> <img src ="http://www.shnenglu.com/feixuwu/aggbug/127669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/feixuwu/" target="_blank">feixuwu</a> 2010-09-25 21:44 <a href="http://www.shnenglu.com/feixuwu/archive/2010/09/25/127669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BOOR读pdf内存问题解决http://www.shnenglu.com/feixuwu/archive/2010/08/04/122227.htmlfeixuwufeixuwuWed, 04 Aug 2010 09:41:00 GMThttp://www.shnenglu.com/feixuwu/archive/2010/08/04/122227.htmlhttp://www.shnenglu.com/feixuwu/comments/122227.htmlhttp://www.shnenglu.com/feixuwu/archive/2010/08/04/122227.html#Feedback2http://www.shnenglu.com/feixuwu/comments/commentRss/122227.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/122227.html支持中文pdf和中文txt,能正显CZ文目录?br>


问题

不过Q有的朋友反映打开大的pdf文gӞҎ(gu)L?br>q几天看?jin)下Q果然发现问题(sh)(jin)?Z(jin)加快dpdf面速度Qbookr一开始就加蝲?jin)所有的pageTree到内存(sh)Q?br>q样昄特定面的时候,无需查找该页面的PageObject?jin),q在PCZ一般没什么问题,PC机有虚拟内存Q即使pdf文g很大Q无非是加蝲pageTree慢点Q?br>不过在PSP上就不行?jin),PSP的内存是有限的,而且没有虚拟内存Q所以如果PageTree很大Q那么很可能?x)导致内存?sh)够,直接L?jin)。。。?br>


解决办法

解决办法其实也很单,bookr启动的时候不加蝲PageTreeQ而是在每ơ显C的时候,从Root开始便利PageTree查询PageObject对象QQ意一时刻Q内存(sh)只有一个PageObject对象?br>q样就基本解决?jin)内存问题。下一个问题是查询效率的问题,q个问题其实不那么严重,PageTree本来是一个树(wi)形结构,pdf的PageObject查询可以优化成一个树(wi)查询Q这样应该会(x)很快Q实际编码测试,
Ҏ(gu)感觉不到修改前后页速度有明昑֏化?br>修改后用 金庸全集三联?pdf(48.6M)试OK?br>

下蝲

  1、下载http://download.csdn.net/source/2578241  
      解压到psp/game目录下?br>  2、从http://www.shnenglu.com/Files/feixuwu/EBOOT.rar 下蝲Q解压后Q覆盖原来bookr目录下的EBOOT.PBP文g卛_?br>



feixuwu 2010-08-04 17:41 发表评论
]]>
PSP版本Bookr修改支持中文http://www.shnenglu.com/feixuwu/archive/2010/07/26/121324.htmlfeixuwufeixuwuMon, 26 Jul 2010 13:41:00 GMThttp://www.shnenglu.com/feixuwu/archive/2010/07/26/121324.htmlhttp://www.shnenglu.com/feixuwu/comments/121324.htmlhttp://www.shnenglu.com/feixuwu/archive/2010/07/26/121324.html#Feedback5http://www.shnenglu.com/feixuwu/comments/commentRss/121324.htmlhttp://www.shnenglu.com/feixuwu/services/trackbacks/121324.htmlq确实很让h不爽?br>q年前闲着没事Q顺便解决了(jin)bookr中文问题Q本文记录了(jin)那段旉的工作:(x)如何从官方版bookr修改Q解决pdf中文q问题Q支持txt中文、中文目录显C的问题Q抛砖引玉和大家分n下整个的思\
和问题的解决方式Q解册E比较丑陋,希望高手多多指点?br>貌似q里不能贴超q?M的附Ӟq里׃贴出修改后的bookr的发布文件了(jin)Q在PSP2000试通过Q最q也一直在?需要的同学可以email问我要?br>注:(x)已经上传到csdn:http://download.csdn.net/source/2578241
内置?jin)少量字体,大家可以自行扩展字体?br>


准备工作

  1、搭建PSP开发环境。sourceforge上集成的安装包,下蝲下来直接安装卛_Q这里不多说?jin)?br>  2、下载bookr源码Q我下蝲的是0.7.1版本。可以用SVN下蝲最新的Q也可以在sourceForge下蝲打包的源码包?br>

解决PDF中文问题

  bookr的代码结果很清晰Q由于是用c++开发的Q所以代码很好理解,BKLayer 是基的显C类QBKDocument是基的文档处理类?br>bookr支持pdf、txt、html?PalmDoc(我没用过q种)Q分别由从BKDocument的派生类来处理?br>pdf是由BKPDF来处理的。解决pdf中文问题的关键就在BKPDFcM(jin)?br>

   1、如何调?/h2>        PSP开发首先遇到的问题是调试,PSPq不能实时调试,q确实是个问题。好在Bookr源码U支持了(jin)跨^収ͼ在windows下,使用Makefile.cygwin 可以编译一个windows版本
的Bookr?jin)。一般来_(d)在windows下bookrq行正常Q大致在PSP上的版本也是正常的,当然Q在l节上其实是有差别的。另外,q可以通过日志来实现跟t?br>

   2、解军_体问?/h2>       实际上,有些中文pdf用bookr打开时是正常的,比如Programming_In_Lua.pdf,但是在打开l常温习(fn)的金庸全集的时候,出Cؕ码了(jin)?br>注意到Bookr的pdf昄其实是主要是mudpf来实现的。那么bookr中文昄问题到底是bookr自n的还是mupdf的呢Q?br>Z(jin)认q个问题Q先从sourceforge下蝲mupdfQ徏立一个vs2005的项目,~译QOKQ可以运行了(jin)Q打开金庸全集一看,q是qQ这下基本可以确认,q个
问题是从mupdf存在了(jin)。要定位中文昄q的问题,自然需要了(jin)解pdf的内部格式,从adobe的官|下载了(jin)最新的pdf手册Q打开一看,一?00多页。。?br>好在我们不需要从头开始阅读,只要?xi)关键的地方看就可以了(jin),中文问题(sh)般是因ؓ(f)字体引v的?br>
所以我们可以直接挑(xi)Text的字体相关部分看?br>mupdf的字体加载在pdf_loadfont里,从pdf手册可知Qpdf支持?jin)若q种字体Q反正我是没什么耐心(j)看下去,直接打开金庸全集单步调试?jin)下Q?br> 发现问题在TrueType字体的加载里QTrueType字体的加载是loadsimplefont来处理的Q通过跟踪可知Q在获取字体的FontDescriptor的时候失败了(jin)Q然后就是用内置的默认字体来处理?jin)?br>默认字体都不支持中文Q所以自然就昄成ؕ码了(jin)?br>最单的办法是只要是TrueType字体Q不具体是什么字体,都强制从盘加蝲一U指定字体(例如黑体Q,当然Q这样会(x)D我们看到的pdf和实际应该显C的样子有差别,只有一U字体了(jin)?br>让我们先q样试试吧:(x)在发现pdf_loadfontdescriptor 加蝲p|后,强制用loadCustomFont 加蝲盘指定字体"font/simhei.ttf",q样字体加蝲的问题貌D决了(jin)?br>~译Q运行,发现q是有问题,q次的问题在文字~码?jin)?br>

   3、编码问?/h2>     一般显CZ正常的中文pdf都是GBK~码的,mupdf的显C是通过如下两个步骤来做的:(x)
   首先解码Q将文字内容全部转化成cidQ然后将要显C的cid全部pushC个队列,然后遍历cid,cid转化成gid(对trueType是转成unicode)Q接着昄?br>mupdf本n有一个比较优雅的办法来解码,通过pdf_lookupcmap来得到unicodeQ我用了(jin)比较W的办法Q自己暴力做GBK到unicode的{换?br>一般在windows和linux下都有库或者API来完成编码{换问题。不q在PSP下却没有q样的API,只好自己做一个编码{换了(jin)Q?br>在http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT 扑ֈ?jin){换表Q复制粘贴到txt文本Q用lua脚本处理下,生成一?c文gQ分别将GBK和unicode值存储到2个数l里Q?br>现在你一定知道怎么转换?jin)?x)二分查找到指定GBK值在GBK数组的下标,然后直接在unicode数组用这个下标,可以得到对应的unicode倹{?br>~译试QOK?jin),l于能正常显CZ文了(jin)?br>不过到现在ؓ(f)止,整个面只有一U字体,要解册个问题,我们可以Ҏ(gu)名字匚w来找到指定的字体Q名字匹配不到的Q用默认的字体Q我是用的simsun.ttfQ?br>

 txt中文问题

   相对来说txt的中文问题比较好解决?jin),基本都是些常规开发,从FzFont.cpp代码可知Qtxt昄不了(jin)中文主要是字体加载的时候,只加载了(jin)前面?56个字形。那么我们只要做2件事情就可以昄中文?jin)?x)
  1、文字解码,现在大部分的txt?sh)子书都是gbk~码的,q样比较省空_(d)解码法前面已经提过?jin)?br>  2、中文文字纹理管理和效率问题。一般在PC游戏中,中文字体一般都是将多个q箋(hu)的汉字按照存储到一?4X64的纹理中Q这样可以节省显存,降低渲染Ҏ(gu)(3D菜鸟的简单推??br>不过如果在psp也这样做Q会(x)发现昄面是在太慢Q最后发玎ͼ最单的办法居然是每个汉字一个纹理,当然要实际用到的时候才生成该汉字纹理?br>  3、显C页分割。bookr阅读txt的时候,?x)自己将电(sh)子书分割成若干面Qƈ且支持书{֊能,因此Q不可避免的涉及(qing)到页面分割问题,引入中文昄后,q里E微有点不同Q要注意不能拆分一个汉字,
当然Q还有其他细节需要处理,q里不多说了(jin)?br>

目录中文问题

  必须承认Q这是我遇到的最痛苦的问题(sh)(jin)Q原因是从API上就有问题(sh)(jin)Q读取出来的目录名居然是Shift-jis~码的,谁让PSP是烦(ch)g的呢Q我试q将PSP的语a讄、时间等本地化设|改成中文、中国等?br>发现d到的目录名还是shift-jis~码。我先将目录名从shift-jis转回gb2312Q然后显C,l果发现很多汉字丢失Q因Zgb2312转到shift-jis的时候已l失真了(jin)Q{回来昄很多字体显CZ?jin)?jin)?br>看上去这个问题无法解决了(jin)Q其实不?dng)PSP的API提供?jin)打开记忆卡设备的功能Q这P我们自己做一个FAT32驱动Q叫驱动不合适,其实是自己dFAT32文gpȝ理文gQ就可以?jin),FAT32的文?br>到处都是Qlinux下也有vfat文gpȝ的实玎ͼ不过我偷懒了(jin)Q我直接从PMP Player的代码里拯?jin)FAT32相关文gQ直接移植过来,修改?jin)FzScreenPsp.cpp文g里目录相关的目录d函数Q?br>然后修改目录相关昄代码后,~码昄Q一切终于解决了(jin)。。?br>
 




feixuwu 2010-07-26 21:41 发表评论
]]>
þۺϸۺϾþ| þùֻоƷ| þñþۺ| һֻƴƬ99þ| 97Ʒ˾þþô߽97| þ97þ97Ʒӿϼ | ݺݾƷþþĻ| 鶹˾þþƷ| | ޾Ʒtvþþþþþþþ| þþþۺϹŷһ | ˾Ʒþ| 18պҹþó| Ļþ| ޹˾þۺ3d| Ʒþþþþþ| þ99Ʒ| þ91ƷۺϹҳ| þ97þ97Ʒӿ| þ޸ۺ| þAV| ۺ޾þһƷ| ɫþþۺ| þһձɫۺϾþ| 777þµַ| þùƷþþƷ| 91Ʒ91þþþþ| 99þֻоƷ| þþþAVרվ| ҹƷþþþþӰ777| 97Ƶþþ| ݺɫۺþö | ղþǿѵĿ| þ޾Ʒϵַ| 99Ʒ99þþþþ97| AVɫۺϾþAVɫۺ| ˾Ʒһþþ| ޳avƬþ| þպƷһ| þþƷ| ɫþùƷ12p|