日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一种以动态库的方式使用资源表的方案

發(fā)布時(shí)間:2025/5/22 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一种以动态库的方式使用资源表的方案 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這段時(shí)間研究了一下資源表的優(yōu)化方案,算是有了一些成果,在此記錄下來。

先交代一下背景吧:我們的服務(wù)器把資源表放在共享內(nèi)存上。這么做的原因主要是,進(jìn)程core掉后再拉起時(shí)不需要重新再構(gòu)建一遍資源表(構(gòu)建資源表主要就是構(gòu)建索引查詢的數(shù)據(jù)結(jié)構(gòu),比如構(gòu)建一個(gè)哈希表用于根據(jù)HeroID查詢英雄配置這種)。然后,考慮到同一個(gè)機(jī)器上可能部署多個(gè)進(jìn)程,于是自然就想到,能否有一種機(jī)制能夠讓一個(gè)機(jī)器上的多個(gè)進(jìn)程共享同一塊資源表的內(nèi)存?

一個(gè)比較直觀的想法就是,讓多個(gè)進(jìn)程掛在同一塊資源表共享內(nèi)存。這樣做確實(shí)是可以達(dá)到內(nèi)存共享的目的,但是需要考慮資源表reload的情況。reload時(shí)會(huì)對這塊內(nèi)存做寫操作,而我們知道,一寫多讀是會(huì)有并發(fā)問題。因此這種方案還需要再引入一種機(jī)制來處理并發(fā)問題。常見的比如是雙緩沖區(qū),構(gòu)建好后再一次性切換到新的資源表內(nèi)存上。了解到了一些項(xiàng)目組確實(shí)也是這么做的,具體就不細(xì)說了。

而我想到的方法是這樣的:使用代碼生成技術(shù),將資源表完全的導(dǎo)成c++的代碼。所有用到于查詢的數(shù)據(jù)結(jié)構(gòu)都是通過自動(dòng)代碼生成的,并且在編譯期就被靜態(tài)構(gòu)建好了。進(jìn)程運(yùn)行時(shí)直接載入這塊內(nèi)存即可,不需要像以前那樣在起服時(shí)跑資源表構(gòu)建的流程。因此,這塊內(nèi)存也不需要再手動(dòng)放共享內(nèi)存上了。core之后再拉起進(jìn)程,最多就是資源表的內(nèi)存被卸載了,并且會(huì)到下次用到時(shí)通過觸發(fā)缺頁中斷被再次載入進(jìn)內(nèi)存。為了做同一機(jī)器上的內(nèi)存共享,可以把資源表打包成一個(gè)動(dòng)態(tài)庫。于是共享內(nèi)存的這塊工作自然而然就推給了操作系統(tǒng)去做。

這種做法的優(yōu)點(diǎn)有幾個(gè)。而其中我覺得最重要的是,這是一種一次性解決問題的做法(這也是我在前一篇博客中所提到的:P)。基本上資源表的大部分代碼都被以是自動(dòng)生成。并且當(dāng)某個(gè)進(jìn)程要使用某些資源表時(shí),直接加載這個(gè)動(dòng)態(tài)庫即可。甚至,加載動(dòng)態(tài)庫的代碼都可以寫在框架層,默認(rèn)為每個(gè)進(jìn)程都加載資源表。在以前我們在其它進(jìn)程(除了gamesvr之外的其它進(jìn)程,如場景進(jìn)程)中如果要使用資源表是很麻煩的:要在一個(gè)指定的文件中定義要使用到了哪些資源表,并且如果資源表之間有依賴關(guān)系,也需要把依賴的資源表也配置進(jìn)來。而現(xiàn)在,我們可以為所有進(jìn)程都載入資源表,并且還不需要擔(dān)心帶來內(nèi)存增長的風(fēng)險(xiǎn)。

其次,不需要再為資源表的內(nèi)存預(yù)估一個(gè)上限的經(jīng)驗(yàn)值。比如說定義了一個(gè)長度為1000的數(shù)組來存裝備配置,當(dāng)策劃同學(xué)配了超過1000個(gè)裝備時(shí),就要手動(dòng)修改這代碼修改這個(gè)經(jīng)驗(yàn)值(因?yàn)橹笆前奄Y源表放共享內(nèi)存,因此需要內(nèi)存預(yù)分配)。因?yàn)橘Y源表都是被靜態(tài)構(gòu)建的,數(shù)組大小完全可以通過自動(dòng)代碼生成來做。再者,也不需要自己手寫建索引的代碼,全部都通過工具自動(dòng)生成。比如技能配置表可能會(huì)有根據(jù)ID和Level查詢的需求,現(xiàn)在只需要在導(dǎo)表時(shí)指定主鍵,便可以自動(dòng)的生成這些索引的代碼。

靜態(tài)構(gòu)建數(shù)據(jù)結(jié)構(gòu)還有一個(gè)優(yōu)勢是,理論上可以使用一種更優(yōu)的內(nèi)存結(jié)構(gòu)來存儲(chǔ)。一般來說,資源表內(nèi)存再載入后就不會(huì)再作修改了,針對這種場景我們可以設(shè)計(jì)一種比哈希表更快、更省內(nèi)存的數(shù)據(jù)結(jié)構(gòu)來做索引。比如通過minima?perfect hash方式為每個(gè)主鍵生成不會(huì)沖突的哈希函數(shù)。并且由于不會(huì)動(dòng)態(tài)擴(kuò)容,因此也不需要使用稀疏的哈希表來存儲(chǔ)(一個(gè)緊湊的數(shù)組即可)。基本思路就是把計(jì)算由運(yùn)行期推到編譯期,甚至可以是推到生成代碼的時(shí)候。

還有就是reload過程非常簡單,卸載舊的動(dòng)態(tài)庫加載新的動(dòng)態(tài)庫即可。由于資源表不放共享內(nèi)存,因此也不需要之前那樣為新增的內(nèi)容預(yù)留一些內(nèi)存空間,也不需要考慮共享內(nèi)存上的結(jié)構(gòu)體兼容性問題。

最后一點(diǎn)就是實(shí)現(xiàn)非常簡單。所有代碼加起來還不足1000行,包括了一個(gè)用python寫的代碼靜態(tài)代碼生成工具,和c++的資源表管理器代碼等。

?

在實(shí)現(xiàn)過程中也遇到了幾個(gè)有意思的問題,在這里也一并記錄一下。

(1)首先一點(diǎn),linux動(dòng)態(tài)庫是只會(huì)把.text段的內(nèi)存共享。而我們這里要共享的主要是數(shù)據(jù),怎么辦呢?首先要明白的是,動(dòng)態(tài)庫之所以不共享.data段的內(nèi)存,是因?yàn)檫@部分?jǐn)?shù)據(jù)可能在被載入進(jìn)程后修改(包括可寫的數(shù)據(jù)、以及重定位表等結(jié)構(gòu)),因此必須是每個(gè)進(jìn)程維護(hù)一份私有的內(nèi)存。但我們這里沒有修改數(shù)據(jù)的情況,因此是完全可以被共享內(nèi)存的。解決方法很簡單:用const來修飾資源表數(shù)組即可。這樣做這塊內(nèi)存會(huì)被放到.rodata中。并且在鏈接階段,linker會(huì)合并所有.rodata到.text段。

P.S. 通過readelf -l?命令查看elf文件的program?header就能看到哪些section是被合并到.text段,哪些被合并到.data段

?

(2)另外,還有一個(gè)問題就是關(guān)于如何熱更新一個(gè)動(dòng)態(tài)庫。之前我一直以為直接把目錄下的庫的文件替換一下是沒問題的(由于有文件的引用計(jì)數(shù))。但是老大指出,在linux下用cp命令覆蓋實(shí)際上是會(huì)修改原來文件的內(nèi)容的,并不是等價(jià)于在目錄下刪掉原來的目錄項(xiàng),再創(chuàng)建一個(gè)新的目錄項(xiàng)指向新文件內(nèi)容。原理就是覆蓋時(shí)cp會(huì)修改原來文件的inode,并不是創(chuàng)建一個(gè)新的inode(詳情可以看這里)。不過我感覺此處更像是cp命令的一個(gè)bug:在拷貝時(shí)應(yīng)該考慮是否有進(jìn)程正在引用原來的文件,如果有則應(yīng)該考慮創(chuàng)建一個(gè)新的inode,而不是復(fù)寫原來的inode(類似于寫時(shí)拷貝)。不過這個(gè)問題感覺可以再深入研究一下,是不是有其它什么顧慮?

?

轉(zhuǎn)載于:https://www.cnblogs.com/adinosaur/p/11545948.html

總結(jié)

以上是生活随笔為你收集整理的一种以动态库的方式使用资源表的方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。