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ù)器上存在著不同的用戶,而多數(shù)用戶對(duì)內(nèi)存的使用情況是一種間斷性的使用。也就是說(shuō)用戶對(duì)內(nèi)存的使用率并不是很高。在服務(wù)器這種多用戶的場(chǎng)景中,如果很多個(gè)用戶對(duì)于內(nèi)存的使用率都不高的話,那么會(huì)存在服務(wù)器實(shí)際占用的內(nèi)存并不飽滿這樣一種情況。實(shí)際上各個(gè)用戶使用內(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)求類型確定是“充氣”還是”放氣“,然后再?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)注
13文章
9784瀏覽量
87863 -
虛擬機(jī)
+關(guān)注
關(guān)注
1文章
966瀏覽量
29316 -
數(shù)據(jù)安全
+關(guān)注
關(guān)注
2文章
724瀏覽量
30426
原文標(biāo)題:StratoVirt 基于 Rust 的 balloon 功能實(shí)踐
文章出處:【微信號(hào):openEulercommunity,微信公眾號(hào):openEuler】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
SNMP協(xié)議在設(shè)備監(jiān)控中的使用

YAGEO與Pulse產(chǎn)品在物聯(lián)網(wǎng)設(shè)備中的應(yīng)用
不同設(shè)備中電源濾波器接線方式的差異

電源設(shè)備中噪聲濾波器的作用介紹
芯科科技Z-Wave設(shè)備測(cè)試工具介紹

電源管理技術(shù)在移動(dòng)設(shè)備中的應(yīng)用
工控主板在數(shù)控機(jī)床設(shè)備中的應(yīng)用
IO模塊助力PLC,全面提升中水處理設(shè)備能效

大型設(shè)備中的小型電機(jī)

思科設(shè)備常用的巡檢命令介紹
Juniper設(shè)備常用的巡檢命令介紹
華為設(shè)備中的OSPF命令詳解
華為設(shè)備中的BGP命令詳解
藍(lán)牙模塊在無(wú)線游戲設(shè)備中的應(yīng)用
工控主板在環(huán)保智能設(shè)備中的應(yīng)用

評(píng)論