一、物理地址空間
- 物理地址是處理器在系統(tǒng)總線上看到的地址。使用RISC的處理器通常只實(shí)現(xiàn)一個(gè)物理地址空間,外圍設(shè)備和物理內(nèi)存使用統(tǒng)一的物理地址空間。有些處理器架構(gòu)把分配給外圍設(shè)備的物理地址區(qū)域稱為設(shè)備內(nèi)存。
- 處理器通過外圍設(shè)備控制器的寄存器訪問外圍設(shè)備,寄存器分為控制器,狀態(tài)寄存器和數(shù)據(jù)寄存器三大類。外圍設(shè)備的寄存器通常被連續(xù)地編址,處理器對外圍設(shè)備寄存編址方式分為:i/o映射方式(i/o-mapped),內(nèi)存映射方式(memory-mapped)。
- 應(yīng)用程序只能通過虛擬地址訪問外設(shè)寄存器,內(nèi)核提供API函數(shù)來把外設(shè)寄存器的物理地址映射到虛擬地址空間。
- ARM64(物理地址寬度最大支持48位)架構(gòu)分為兩種內(nèi)存類型:
- 正常內(nèi)存(Noramal Memory):包括物理內(nèi)存和只讀存儲(chǔ)器(ROM);
- 設(shè)備內(nèi)存(Device Memory):指分配給外圍設(shè)備寄存器的物理地址區(qū)域;
- 設(shè)備內(nèi)存共享屬性總是外部共享,緩存屬性總是不可緩存(必須繞過處理器的緩存)
- 二、內(nèi)存映射原理
“內(nèi)存映射即在進(jìn)程的虛擬地址空間中創(chuàng)建一個(gè)映射,分為兩種:
”
- 文件映射:文件支持的內(nèi)存映射,把文件的一個(gè)區(qū)間映射到進(jìn)程的虛擬地址空間,數(shù)據(jù)源是存儲(chǔ)設(shè)備上的文件。
- 匿名映射:沒有文件支持的內(nèi)存映射,把物理內(nèi)存映射到進(jìn)程的虛擬地址空間,沒有數(shù)據(jù)源。
【原理】:創(chuàng)建內(nèi)存映射時(shí),在進(jìn)程的用戶虛擬地址空間中分配一個(gè)虛擬內(nèi)存區(qū)域。內(nèi)核采用延遲分配物理內(nèi)存的策略,在進(jìn)程第一次訪問虛擬頁的時(shí)候,產(chǎn)生缺頁異常。==如果是文件映射,那么分配物理頁,把文件指定區(qū)間的數(shù)據(jù)讀到物理頁中,然后在頁表中把虛擬頁映射到物理頁。如果是匿名映射,就分配物理頁,然后在頁表中把虛擬頁映射到物理頁。==(1)兩個(gè)進(jìn)程可以使用共享的文件映射實(shí)現(xiàn)共享內(nèi)存。匿名映射通常是私有映射,共享的匿名映射只可能出現(xiàn)父進(jìn)程和子進(jìn)程之間。在進(jìn)程的虛擬地址空間中,代碼段和數(shù)據(jù)段是私有的文件映射,未初始化數(shù)據(jù)段、堆棧是私有的匿名映射。(2)修改過的臟頁面不會(huì)立即更新到文件中,可以調(diào)用msync來強(qiáng)制同步寫入文件。
flowchartLR
task_struct-->mm_struct-->vm_area_struct
三、虛擬內(nèi)存源碼分析
3.1 相關(guān)數(shù)據(jù)結(jié)構(gòu)
structvm_area_struct{
/*ThefirstcachelinehastheinfoforVMAtreewalking.*/
//這兩個(gè)成員分別用來保存該虛擬內(nèi)存空間的首地址和末地址后第一個(gè)字節(jié)的地址
unsignedlongvm_start;/*Ourstartaddresswithinvm_mm.*/
unsignedlongvm_end;/*Thefirstbyteafterourendaddresswithinvm_mm.*/
/*linkedlistofVMareaspertask,sortedbyaddress*/
structvm_area_struct*vm_next,*vm_prev;
//如果采用鏈表組織化,會(huì)影響它搜索速度問題,解決此問題采用紅黑樹(每個(gè)進(jìn)程結(jié)構(gòu)體mm_struct中都
//創(chuàng)建一顆紅黑樹,將VMA作為一個(gè)節(jié)點(diǎn)加入紅黑樹給中,這樣可以提升搜索速度)
structrb_nodevm_rb;
/*
*LargestfreememorygapinbytestotheleftofthisVMA.
*EitherbetweenthisVMAandvma->vm_prev,orbetweenoneofthe
*VMAsbelowusintheVMArbtreeandits->vm_prev.Thishelps
*get_unmapped_areafindafreeareaoftherightsize.
*/
unsignedlongrb_subtree_gap;
/*Secondcachelinestartshere.*/
structmm_struct*vm_mm;/*Theaddressspacewebelongto.*/
pgprot_tvm_page_prot;/*AccesspermissionsofthisVMA.*/
unsignedlongvm_flags;/*Flags,seemm.h.*/
/*
*Forareaswithanaddressspaceandbackingstore,
*linkageintotheaddress_space->i_mmapintervaltree.
*/
struct{
structrb_noderb;
unsignedlongrb_subtree_last;
}shared;
/*
*Afile'sMAP_PRIVATEvmacanbeinbothi_mmaptreeandanon_vma
*list,afteraCOWofoneofthefilepages.AMAP_SHAREDvma
*canonlybeinthei_mmaptree.AnanonymousMAP_PRIVATE,stack
*orbrkvma(withNULLfile)canonlybeinananon_vmalist.
*/
structlist_headanon_vma_chain;/*Serializedbymmap_sem&
*page_table_lock*/
structanon_vma*anon_vma;/*Serializedbypage_table_lock*/
/*Functionpointerstodealwiththisstruct.*/
conststructvm_operations_struct*vm_ops;
/*Informationaboutourbackingstore:*/
unsignedlongvm_pgoff;/*Offset(withinvm_file)inPAGE_SIZEunits,*not*PAGE_CACHE_SIZE*/
structfile*vm_file;//文件,如果是私有的匿名映射,該成員為空指針
void*vm_private_data;/*指向內(nèi)存的私有數(shù)據(jù)*/
#ifndefCONFIG_MMU
structvm_region*vm_region;/*NOMMUmappingregion*/
#endif
#ifdefCONFIG_NUMA
structmempolicy*vm_policy;/*NUMApolicyfortheVMA*/
#endif
structvm_userfaultfd_ctxvm_userfaultfd_ctx;
};
3.2 虛擬內(nèi)存操作集合
structvm_operations_struct{
void(*open)(structvm_area_struct*area);//在創(chuàng)建虛擬內(nèi)存區(qū)域時(shí)調(diào)用open方法
void(*close)(structvm_area_struct*area);//在刪除虛擬內(nèi)存區(qū)域時(shí)調(diào)用close方法
int(*mremap)(structvm_area_struct*area);//使用系統(tǒng)調(diào)用mremap移動(dòng)虛擬內(nèi)存區(qū)域時(shí)調(diào)用
int(*fault)(structvm_area_struct*vma,structvm_fault*vmf);//訪問文件映射的虛擬頁時(shí),如果沒有映射到物理頁,生成
//缺頁異常,異常處理程序調(diào)用fault方法來把文件的數(shù)據(jù)讀到文件頁緩存當(dāng)中
int(*pmd_fault)(structvm_area_struct*,unsignedlongaddress,
pmd_t*,unsignedintflags);//與fault類似,區(qū)別是該方法針對使用透明巨型頁的文件映射
/*讀文件映射的虛擬頁時(shí),如果沒有映射到物理頁,生成缺頁異常,異常處理程序除了讀入正在訪問的文件頁
還會(huì)預(yù)讀后續(xù)文件頁,調(diào)用map_pages方法在文件的頁緩存中分配物理頁*/
void(*map_pages)(structvm_area_struct*vma,structvm_fault*vmf);
/*notificationthatapreviouslyread-onlypageisabouttobecome
*writable,ifanerrorisreturneditwillcauseaSIGBUS*/
/*第一次寫私有的文件映射時(shí),生成頁錯(cuò)誤異常,異常處理程序執(zhí)行寫時(shí)復(fù)制,調(diào)用page_mkwrite方法以
通知文件系統(tǒng)頁即將變成可寫,以便文件系統(tǒng)檢查是否允許寫,或者等待頁進(jìn)入合適的狀態(tài)*/
int(*page_mkwrite)(structvm_area_struct*vma,structvm_fault*vmf);
/*sameaspage_mkwritewhenusingVM_PFNMAP|VM_MIXEDMAP*/
int(*pfn_mkwrite)(structvm_area_struct*vma,structvm_fault*vmf);
/*calledbyaccess_process_vmwhenget_user_pages()fails,typically
*forusebyspecialVMAsthatcanswitchbetweenmemoryandhardware
*/
int(*access)(structvm_area_struct*vma,unsignedlongaddr,
void*buf,intlen,intwrite);
/*Calledbythe/proc/PID/mapscodetoaskthevmawhetherit
*hasaspecialname.Returningnon-NULLwillalsocausethis
*vmatobedumpedunconditionally.*/
constchar*(*name)(structvm_area_struct*vma);
#ifdefCONFIG_NUMA
/*
*set_policy()opmustaddareferencetoanynon-NULL@newmempolicy
*toholdthepolicyuponreturn.CallershouldpassNULL@newto
*removeapolicyandfallbacktosurroundingcontext--i.e.donot
*installaMPOL_DEFAULTpolicy,northetaskorsystemdefault
*mempolicy.
*/
int(*set_policy)(structvm_area_struct*vma,structmempolicy*new);
/*
*get_policy()opmustaddreference[mpol_get()]toanypolicyat
*(vma,addr)markedasMPOL_SHARED.Thesharedpolicyinfrastructure
*inmm/mempolicy.cwilldothisautomatically.
*get_policy()mustNOTaddarefifthepolicyat(vma,addr)isnot
*markedasMPOL_SHARED.vmapoliciesareprotectedbythemmap_sem.
*Ifno[shared/vma]mempolicyexistsattheaddr,get_policy()op
*mustreturnNULL--i.e.,donot"fallback"totaskorsystemdefault
*policy.
*/
structmempolicy*(*get_policy)(structvm_area_struct*vma,
unsignedlongaddr);
#endif
/*
*Calledbyvm_normal_page()forspecialPTEstofindthe
*pagefor@addr.Thisisusefulifthedefaultbehavior
*(usingpte_page())wouldnotfindthecorrectpage.
*/
structpage*(*find_special_page)(structvm_area_struct*vma,
unsignedlongaddr);
};
四、系統(tǒng)調(diào)用
“”
- 應(yīng)用程序通常使用C標(biāo)準(zhǔn)庫提供的函數(shù)malloc()申請內(nèi)存。glibc庫的內(nèi)存分配器ptmalloc使用brk或mmap向內(nèi)核以頁為單位申請?zhí)摂M內(nèi)存,然后把頁劃分成小內(nèi)存塊分配給應(yīng)用程序。默認(rèn)的閾值是128kb,如果應(yīng)用程序申請的內(nèi)存長度小于閾值,ptmalloc分配器使用brk向內(nèi)核申請?zhí)摂M內(nèi)存,否則ptmalloc分配器使用mmap向內(nèi)核申請?zhí)摂M內(nèi)存。
- 應(yīng)用程序可以直接使用mmap向內(nèi)核申請?zhí)摂M內(nèi)存。
【回顧mmap內(nèi)存映射原理三個(gè)階段】:
- 進(jìn)程啟動(dòng)映射過程,并且在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域;
- 調(diào)用內(nèi)核空間的系統(tǒng)調(diào)用函數(shù)mmap(不同于用戶空間函數(shù)),實(shí)現(xiàn)文件物理地址和進(jìn)程虛擬地址的一一映射關(guān)系;
- 進(jìn)程發(fā)起對這片映射空間的訪問,引發(fā)缺頁異常,實(shí)現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝。
內(nèi)存管理子系統(tǒng)提供以下常用系統(tǒng)調(diào)用函數(shù):
- mmap() ---->創(chuàng)建內(nèi)存映射
“#include
”void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- 系統(tǒng)調(diào)用mmap():進(jìn)程創(chuàng)建匿名的內(nèi)存映射,把內(nèi)存的物理頁映射到進(jìn)程的虛擬地址空間。進(jìn)程把文件映射到進(jìn)程的虛擬地址空間,可以像訪問內(nèi)存一樣訪問文件,不需要調(diào)用系統(tǒng)調(diào)用read()/write()訪問文件,從而避免用戶模式和內(nèi)核模式之間的切換,提高讀寫文件速度。兩個(gè)進(jìn)程針對同一個(gè)文件創(chuàng)建共享的內(nèi)存映射,實(shí)現(xiàn)共享內(nèi)存。
- munmap() ---->刪除內(nèi)存映射
“#include
”int munmap(void *addr, size_t len);
代碼實(shí)踐
#include
#include
#include
#include
#include
#include
#include
typedefstruct
{
/*data*/
charname[6];
intage;
}people;
intmain(intargc,char**argv)
{
intfd,i;
people*p_map;
chartemp;
fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1);
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p_map==(void*)-1)
{
fprintf(stderr,"mmap:%s
",strerror(errno));
return-1;
}
temp='A';
close(fd);
for(i=0;i10;i++)
{
temp=temp+1;
(*(p_map+i)).name[1]='';
memcpy((*(p_map+i)).name,&temp,1);
(*(p_map+i)).age=30+i;
}
printf("Initialize.
");
sleep(15);
munmap(p_map,sizeof(people)*10);
printf("UMAOK.
");
return0;
}
#include
#include
#include
#include
#include
#include
#include
typedefstruct
{
/*data*/
charname[6];
intage;
}people;
intmain(intargc,char**argv)
{
intfd,i;
people*p_map;
fd=open(argv[1],O_CREAT|O_RDWR,00777);
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p_map==(void*)-1)
{
fprintf(stderr,"mmap:%s
",strerror(errno));
return-1;
}
for(i=0;i10;i++)
{
printf("name:%sage:%d
",(*(p_map+i)).name,(*(p_map+i)).age);
}
munmap(p_map,sizeof(people)*10);
return0;
}
運(yùn)行結(jié)果
審核編輯:陳陳
-
Linux
+關(guān)注
關(guān)注
87文章
11209瀏覽量
208721 -
內(nèi)存映射
+關(guān)注
關(guān)注
0文章
14瀏覽量
7410
原文標(biāo)題:Linux內(nèi)核 | 內(nèi)存映射
文章出處:【微信號(hào):嵌入式開發(fā)AIoT,微信公眾號(hào):嵌入式開發(fā)AIoT】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論