StratoVirt 是計(jì)算產(chǎn)業(yè)中面向云數(shù)據(jù)中心的企業(yè)級(jí)虛擬化 VMM,實(shí)現(xiàn)了一套架構(gòu)統(tǒng)一支持虛擬機(jī)、容器、Serverless 三種場(chǎng)景。StratoVirt 在輕量低噪、軟硬協(xié)同、Rust 語(yǔ)言級(jí)安全等方面具備關(guān)鍵技術(shù)競(jìng)爭(zhēng)優(yōu)勢(shì)。
背景介紹:
通常,在同一臺(tái)服務(wù)器上存在著不同的用戶(hù),而多數(shù)用戶(hù)對(duì)內(nèi)存的使用情況是一種間斷性的使用。也就是說(shuō)用戶(hù)對(duì)內(nèi)存的使用率并不是很高。在服務(wù)器這種多用戶(hù)的場(chǎng)景中,如果很多個(gè)用戶(hù)對(duì)于內(nèi)存的使用率都不高的話(huà),那么會(huì)存在服務(wù)器實(shí)際占用的內(nèi)存并不飽滿(mǎn)這樣一種情況。實(shí)際上各個(gè)用戶(hù)使用內(nèi)存的分布圖可能如下圖所示(黃色部分表示 used 部分,綠色部分表示 free 的部分)。
解決方案:
為了解決上述服務(wù)器上內(nèi)存使用率低的問(wèn)題,可以將虛擬機(jī)中暫時(shí)不用的內(nèi)存回收回來(lái)給其他虛擬機(jī)使用。而當(dāng)被回收內(nèi)存的虛擬機(jī)需要內(nèi)存時(shí),由 host 再將內(nèi)存歸還回去。有了這樣的內(nèi)存伸縮能力,服務(wù)器便可以有效提高內(nèi)存的使用率。在 StratoVirt 中,我們使用 balloon 設(shè)備來(lái)對(duì)虛擬機(jī)中的空閑內(nèi)存進(jìn)行回收和釋放。下面詳細(xì)了解一下 StratoVirt 中的 balloon 設(shè)備。
balloon 設(shè)備簡(jiǎn)介:
由于 StratoVirt 只是負(fù)責(zé)為虛擬機(jī)分配內(nèi)存,只能感知到每個(gè)虛擬機(jī)總的內(nèi)存大小。但是在每個(gè)虛擬機(jī)中如何使用內(nèi)存,內(nèi)存剩余多少。StratoVirt 是無(wú)法感知的,也就無(wú)法得知該從虛擬機(jī)中回收多少內(nèi)存了。為此,需要在虛擬機(jī)中放置一個(gè)“氣球(balloon)”設(shè)備。該設(shè)備通過(guò) virtio 半虛擬化框架來(lái)實(shí)現(xiàn)前后端通信。當(dāng) Host 端需要回收虛擬機(jī)內(nèi)部的空閑內(nèi)存時(shí),balloon 設(shè)備“充氣”膨脹,占用虛擬機(jī)內(nèi)部?jī)?nèi)存。而將占用的內(nèi)存交給 Host 使用。如果虛擬機(jī)的空閑內(nèi)存被回收后,虛擬機(jī)內(nèi)部由于業(yè)務(wù)要求突然需要內(nèi)存時(shí)。位于虛擬機(jī)內(nèi)部的 balloon 設(shè)備可以選擇“放氣”縮小。釋放出更多的內(nèi)存空間給虛擬機(jī)使用。
balloon 實(shí)現(xiàn):
balloon 的具體代碼實(shí)現(xiàn)位于 StratoVirt 項(xiàng)目的/virtio/src/balloon.rs 文件中,相關(guān)細(xì)節(jié)可閱讀代碼理解。代碼架構(gòu)如下:
virtio ├──Cargo.toml └──src ├──balloon.rs ├──block.rs ├──console.rs ├──lib.rs ├──net.rs ├──queue.rs ├──rng.rs ├──vhost │├──kernel ││├──mod.rs ││├──net.rs ││└──vsock.rs │└──mod.rs ├──virtio_mmio.rs └──virtio_pci.rs
由于 balloon 是一個(gè) virtio 設(shè)備,所以在前后端通信時(shí)也使用了 virtio 框架提供的 virtio queue。當(dāng)前 StratoVirt 支持兩個(gè)隊(duì)列:inflate virtio queue(ivq)和 deflate virtio queue(dvq)。這兩個(gè)隊(duì)列分別負(fù)責(zé) balloon 設(shè)備的“充氣”和“放氣”。
氣球的充放氣時(shí),前后端的信息是通過(guò)一個(gè)結(jié)構(gòu)體來(lái)傳遞。
structVirtioBalloonConfig{ ///NumberofpageshostwantsGuesttogiveup. pubnum_pages:u32, ///Numberofpageswe'veactuallygotinballoon. pubactual:u32, }
因此后端向前端要內(nèi)存的時(shí)候,只需要修改這個(gè)結(jié)構(gòu)體中的 num_pages 的數(shù)值,然后通知前端。前端讀取配置結(jié)構(gòu)體中的 num_pages 成員。并與本身結(jié)構(gòu)體中的 actual 對(duì)比,判斷是進(jìn)行 inflate 還是 deflate。
inflate
如果是 inflate,那么虛擬機(jī)以 4k 頁(yè)為單位去申請(qǐng)?zhí)摂M機(jī)內(nèi)存,并將申請(qǐng)到的內(nèi)存地址保存在隊(duì)列中。然后通過(guò) ivq 將保存了分配好的頁(yè)面地址的數(shù)組分批發(fā)往后端處理(virtio queue 隊(duì)列長(zhǎng)度最大 256,也就是一次最多只能傳輸 1M 內(nèi)存信息,對(duì)于大于 1M 的內(nèi)存只能分批傳輸)。后端通過(guò)得到信息后,找到相應(yīng)的 MemoryRegion,將對(duì)應(yīng)的 page 標(biāo)記為”WILLNEED“。然后通知前端,完成配置。
deflate
如果是 deflate 則從保存申請(qǐng)到的內(nèi)存地址隊(duì)列中彈出一部分內(nèi)存的地址。通過(guò) dvq 分批次傳輸給后端處理。后端將 page 標(biāo)記為“DONTNEED"。
下面結(jié)合代碼進(jìn)行說(shuō)明:
定義 BalloonIoHandler 結(jié)構(gòu)體作為處理 balloon 事件的主體。
structBalloonIoHandler{
///Thefeaturesofdriver.
driver_features:u64,
///Addressspace.
mem_space:Arc,
///Inflatequeue.
inf_queue:Arc>,
///InflateEventFd.
inf_evt:EventFd,
///Deflatequeue.
def_queue:Arc>,
///DeflateEventFd.
def_evt:EventFd,
/*省略*/
}
其中包含上述的兩個(gè) virtio 隊(duì)列inf_queue和def_queue,以及對(duì)應(yīng)的觸發(fā)事件描述符(EventFd)inf_evt和def_evt。兩個(gè)隊(duì)列均使用了Mutex鎖,保證了隊(duì)列在同一時(shí)刻只有一個(gè)使用者對(duì)該隊(duì)列進(jìn)行操作。保證了多線程共享的數(shù)據(jù)安全。
fnprocess_balloon_queue(&mutself,req_type:bool)->Result<()>{
letqueue=ifreq_type{
&mutself.inf_queue
}else{
&mutself.def_queue
};//獲得對(duì)應(yīng)的隊(duì)列
letmutunlocked_queue=queue.lock().unwrap();
whileletOk(elem)=unlocked_queue
.vring
.pop_avail(&self.mem_space,self.driver_features)
{
matchRequest::parse(&elem){
Ok(req)=>{
if!self.mem_info.has_huge_page(){
//進(jìn)行內(nèi)存標(biāo)記
req.mark_balloon_page(req_type,&self.mem_space,&self.mem_info);
}
/*省略*/
}
Err(e)=>{
/*省略錯(cuò)誤處理*/
}
}
}
/*省略*/
}
當(dāng)相應(yīng)的EventFd被觸發(fā)后process_balloon_queue函數(shù)將會(huì)被調(diào)用。通過(guò)判斷請(qǐng)求類(lèi)型確定是“充氣”還是”放氣“,然后再?gòu)南鄳?yīng)的隊(duì)列中取數(shù)據(jù)進(jìn)行內(nèi)存標(biāo)記。其中while let是 Rust 語(yǔ)言提供的一種循環(huán)模式匹配機(jī)制。借助該語(yǔ)法可以將隊(duì)列中 pop 出來(lái)的所有數(shù)據(jù)遍歷取出到elem中。
內(nèi)存標(biāo)記及優(yōu)化:
標(biāo)記內(nèi)存在mark_balloon_page函數(shù)中進(jìn)行實(shí)現(xiàn),起初的實(shí)現(xiàn)思路為:將虛擬機(jī)傳送過(guò)來(lái)的地址逐個(gè)進(jìn)行標(biāo)記。即,從隊(duì)列中取出一個(gè)元素,轉(zhuǎn)化為地址后立即進(jìn)行標(biāo)記。后來(lái)經(jīng)過(guò)測(cè)試發(fā)現(xiàn):balloon 設(shè)備在對(duì)頁(yè)地址進(jìn)行一頁(yè)一頁(yè)標(biāo)記內(nèi)存時(shí)花費(fèi)時(shí)間巨大。而同時(shí)也發(fā)現(xiàn)通過(guò)虛擬機(jī)傳回來(lái)的地址中有大段的連續(xù)內(nèi)存段。于是通過(guò)改變標(biāo)記方法:由原來(lái)的一頁(yè)一頁(yè)標(biāo)記改為將這些連續(xù)的內(nèi)存統(tǒng)一標(biāo)記。大大節(jié)省了標(biāo)記時(shí)間。下面代碼為具體實(shí)現(xiàn):
fnmark_balloon_page( &self, req_type:bool, address_space:&Arc, mem:&BlnMemInfo, ){ letadvice=ifreq_type{ libc::MADV_DONTNEED }else{ libc::MADV_WILLNEED }; /*略*/ foriovinself.iovec.iter(){ letmutoffset=0; letmuthvaset=Vec::new(); whileletSome(pfn)=iov_to_buf:: (address_space,iov,offset){ offset+=std:: ()asu64; letgpa:GuestAddress=GuestAddress((pfnasu64)<addr, None=>{ /*略*/ } }; //將hva地址保存在hvaset的vec中 hvaset.push(hva); } //對(duì)hvaset進(jìn)行從小到大排序。 hvaset.sort_by_key(|&b|Reverse(b)); /*略*/ //將hvaset中連續(xù)的內(nèi)存段進(jìn)行標(biāo)記 whileletSome(hva)=hvaset.pop(){ iflast_addr==0{ free_len+=1; start_addr=hva; }elseifhva==last_addr+BALLOON_PAGE_SIZE{ free_len+=1; }else{ memory_advise( start_addras*constlibc::c_voidas*mut_, (free_len*BALLOON_PAGE_SIZE)asusize, advice, ); free_len=1; start_addr=hva; } ifcount_iov==iov.iov_len{ memory_advise( start_addras*constlibc::c_voidas*mut_, (free_len*BALLOON_PAGE_SIZE)asusize, advice, ); } count_iov+=std:: ()asu64; last_addr=hva; } /*略*/ } } }
首先將 virtio 隊(duì)列中的地址全部取出,并保存在 vec 中,然后將該 vec 進(jìn)行從小到大的排序。有利于快速找出連續(xù)的內(nèi)存段并進(jìn)行標(biāo)記。由于 hvaset 中的地址是按照從小到大排列的,因此可以從頭開(kāi)始遍歷 hvaset,遇到不連續(xù)的地址后將前面的連續(xù)段進(jìn)行標(biāo)記。這樣就完成了由原來(lái)逐頁(yè)標(biāo)記到連續(xù)內(nèi)存段統(tǒng)一標(biāo)記的優(yōu)化。
經(jīng)過(guò)測(cè)試,StratoVirt 的 balloon 速度也有了極大的提高。
原文標(biāo)題:StratoVirt 基于 Rust 的 balloon 功能實(shí)踐
文章出處:【微信公眾號(hào):openEuler】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
服務(wù)器
+關(guān)注
關(guān)注
14文章
10308瀏覽量
91613 -
虛擬機(jī)
+關(guān)注
關(guān)注
1文章
973瀏覽量
30590 -
數(shù)據(jù)安全
+關(guān)注
關(guān)注
2文章
775瀏覽量
30873
原文標(biāo)題:StratoVirt 基于 Rust 的 balloon 功能實(shí)踐
文章出處:【微信號(hào):openEulercommunity,微信公眾號(hào):openEuler】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
initrd 中沒(méi)有帶有自編譯內(nèi)核的 nvme 設(shè)備?
半導(dǎo)體制造中晶圓清洗設(shè)備介紹
太誘產(chǎn)品在通用電子設(shè)備中的使用指南及技術(shù)剖析
如何在Linux中列出USB設(shè)備
SP系列連接器在呼吸機(jī)與麻醉設(shè)備中的應(yīng)用
SNMP協(xié)議在設(shè)備監(jiān)控中的使用
CAN轉(zhuǎn)PROFINET網(wǎng)關(guān)設(shè)備基本功能介紹
智能設(shè)備中Leadway電源模塊的應(yīng)用案例
振動(dòng)馬達(dá)在VR設(shè)備中的應(yīng)用優(yōu)點(diǎn)分析
高校智慧教室多媒體教學(xué)設(shè)備介紹
YAGEO與Pulse產(chǎn)品在物聯(lián)網(wǎng)設(shè)備中的應(yīng)用
不同設(shè)備中電源濾波器接線方式的差異
StratoVirt 中的 balloon 設(shè)備介紹
評(píng)論