• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 14,  comments - 57,  trackbacks - 0

             問(wèn)題

               最近游戲開始技術(shù)封測(cè)了,不過(guò)剛剛上線3個(gè)小時(shí),Server就掛了,掛在框架代碼里,一個(gè)不可能掛的地方。
            從CallStack看,是在獲取數(shù)據(jù)時(shí)發(fā)送請(qǐng)求包的時(shí)候掛的,由于框架部分是其他部門的同事開發(fā)的,所以查問(wèn)題的時(shí)候就拉上他們了,
            大家折騰了2天,沒(méi)有實(shí)質(zhì)性的進(jìn)展,服務(wù)器還是基本上每3個(gè)小時(shí)宕機(jī)一次。由于上層邏輯大部分都在我那,所以壓力比較大,宕機(jī)的直接原因是hashtable的一個(gè)桶的指針異常,
            這個(gè)hashtable是框架代碼的一個(gè)內(nèi)部成員,按道理我們是無(wú)從破壞的,只有可能是多線程環(huán)境下迭代器損壞導(dǎo)致的。
            但是框架代碼在這個(gè)地方確實(shí)無(wú)懈可擊,所以真正的原因應(yīng)該還是上層代碼破壞了堆內(nèi)存,很可能是一個(gè)memcpy越界導(dǎo)致的。這畢竟是個(gè)猜想,如何找到證據(jù)呢,這是個(gè)問(wèn)題。
            把所有代碼里的memcpy瀏覽了一遍,沒(méi)有發(fā)現(xiàn)明顯問(wèn)題。

            猜測(cè)

              一般游戲中比較容易出現(xiàn)但是不好查的問(wèn)題很多時(shí)候都是腳本(lua)導(dǎo)致的,我們的腳本部分是一個(gè)同事幾年前寫的,在幾個(gè)產(chǎn)品中都使用過(guò),按道理沒(méi)這么脆弱,不過(guò)老大還是和最初開發(fā)這個(gè)模塊的部門溝通了下,
            還真發(fā)現(xiàn)問(wèn)題了,趕緊拿了新的版本更新上去。經(jīng)過(guò)一天的觀察,服務(wù)器沒(méi)有宕機(jī)了,OK,問(wèn)題碰巧解決了,背了這么久的黑鍋,終于放下來(lái)了。

            PageHeap

               假如沒(méi)有碰巧解決了這個(gè)問(wèn)題,正常的思路該如何解決這個(gè)問(wèn)題呢,這個(gè)時(shí)候我懷念windows了,在windows下有PageHeap來(lái)解決這類寫越界的問(wèn)題。基本思路就是每次分配內(nèi)存的時(shí)候,都將內(nèi)存的結(jié)尾放在頁(yè)的邊緣,緊接著這塊內(nèi)存分配一塊不能寫的內(nèi)存,這樣,一旦寫越界,就會(huì)寫異常,導(dǎo)致宕機(jī)。linux下沒(méi)有現(xiàn)成的工具,但是linux提供了mmap功能,我們可以自己實(shí)現(xiàn)這樣一個(gè)功能,當(dāng)然,這一切都不用自己動(dòng)手了,tcmalloc已經(jīng)包含了
            這個(gè)功能了,不過(guò)在文檔里基本沒(méi)有介紹,我也是在閱讀tcmalloc代碼時(shí)看到的,這個(gè)功能默認(rèn)是關(guān)閉的,打開這個(gè)開關(guān)需要改寫代碼:

            這個(gè)代碼在debugallocation.cc里:

            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改成true就可以了。
            想要在項(xiàng)目里加入PageHeap功能,只需要鏈接的時(shí)候加上 -ltcmalloc_debug即可。把它加入項(xiàng)目中,試著運(yùn)行下,直接掛了,
            仔細(xì)一看,原來(lái)是項(xiàng)目中很多成員變量沒(méi)有初始化導(dǎo)致的,tcmalloc_debug會(huì)自動(dòng)將new 和malloc出來(lái)的內(nèi)存初始化為指定值,這樣,一旦變量沒(méi)有初始化,很容易就暴露了。
            修改完這個(gè)問(wèn)題后,編譯,再運(yùn)行,還是掛,這個(gè)是mprotect的時(shí)候掛的,錯(cuò)誤是內(nèi)存不夠,這怎么可能呢,其實(shí)是達(dá)到了資源限制了。
            echo 128000 > /proc/sys/vm/max_map_count
            把map數(shù)量限制加大,再運(yùn)行,OK了!
             
              但是游戲Server啟動(dòng)后,發(fā)現(xiàn)一個(gè)問(wèn)題,CPU長(zhǎng)期處于100%,導(dǎo)致登陸一個(gè)玩家都很困難,gdb中斷后,info thread,發(fā)現(xiàn)大部分的操作都在mmap和mprotect,最開始
            懷疑我的linux版本有問(wèn)題,導(dǎo)致這2個(gè)AP慢,寫了測(cè)試程序試了下,發(fā)現(xiàn)其實(shí)API不慢,估計(jì)是頻繁調(diào)用導(dǎo)致的。
            所以得換種思路優(yōu)化下才可以,其實(shí)大部分情況下,我們free的時(shí)候,無(wú)需將頁(yè)面munmap掉,可以先cache進(jìn)來(lái),下次分配的時(shí)候,如果有,直接拿來(lái)用就可以了。
            最簡(jiǎn)單的cache算法就是定義一個(gè)void* s_pageCache[50000]數(shù)組,頁(yè)面數(shù)相同的內(nèi)存組成一個(gè)鏈表,掛在一個(gè)數(shù)組項(xiàng)下,這個(gè)很像STL的小內(nèi)存處理,我們可以將mmap出來(lái)的內(nèi)存的
            前面幾個(gè)字節(jié)(一個(gè)指針大小)用于索引下一個(gè)freePage。當(dāng)然這個(gè)過(guò)程需要加鎖,不能用pthread的鎖(因?yàn)樗麄儠?huì)調(diào)用malloc等內(nèi)存分配函數(shù)),必須用spinlock,從linux源碼里直接抄一個(gè)過(guò)來(lái)即可。
            static void*   s_pagePool[MAX_PAGE_ALLOC]={0};

            malloc的時(shí)候,先從pagePool里面獲取:
            // 先從pagePool找
             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內(nèi)存的時(shí)候,直接放到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);

            編譯、運(yùn)行,OK了,CPU迅速降下來(lái)了,空載的時(shí)候不到1%,而且也能達(dá)到檢測(cè)寫溢出的問(wèn)題。

            posted on 2011-05-14 21:16 feixuwu 閱讀(2059) 評(píng)論(1)  編輯 收藏 引用 所屬分類: 游戲開發(fā)

            FeedBack:
            # re: linux下PageHeap
            2014-06-20 16:25 | thinkstream
            博主經(jīng)驗(yàn)好豐富,現(xiàn)在過(guò)了3年了。看前輩的文章,抽絲剝繭。正好昨天發(fā)現(xiàn)page heap不知道為什么?謝了。  回復(fù)  更多評(píng)論
              
            <2013年11月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            1234567

            文章轉(zhuǎn)載請(qǐng)注明出處

            常用鏈接

            留言簿(11)

            隨筆分類

            隨筆檔案

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            午夜精品久久久久久中宇| 香蕉久久夜色精品国产小说| 久久AV高潮AV无码AV| 国产成人精品白浆久久69| 国产精品日韩深夜福利久久| 久久无码中文字幕东京热| 好久久免费视频高清| 久久免费视频1| 久久精品国产亚洲av水果派| 国内精品久久久久久不卡影院| 亚洲精品乱码久久久久久蜜桃| 国产成人综合久久综合| 2021国内精品久久久久久影院| 久久噜噜电影你懂的| 99精品国产综合久久久久五月天| 99久久综合狠狠综合久久| 久久精品国产亚洲av高清漫画 | 精品久久久久久国产| 日本久久中文字幕| 97精品伊人久久久大香线蕉| 欧美一区二区三区久久综合| 亚洲精品乱码久久久久久蜜桃不卡 | 久久精品国产亚洲av麻豆图片 | 精品国产一区二区三区久久| 亚洲午夜久久久久久久久电影网| 久久青青国产| 久久久精品波多野结衣| 伊人久久综在合线亚洲2019| 国产精品对白刺激久久久| 午夜欧美精品久久久久久久| 久久婷婷五月综合国产尤物app| 精品久久综合1区2区3区激情| 久久精品国产秦先生| 日本福利片国产午夜久久| 久久久青草久久久青草| 国产精久久一区二区三区| 很黄很污的网站久久mimi色 | 国産精品久久久久久久| 久久人人爽人爽人人爽av| 久久婷婷午色综合夜啪| 亚洲va久久久噜噜噜久久狠狠|