一、物理地址空間
- 物理地址是處理器在系統總線上看到的地址。使用RISC的處理器通常只實現一個物理地址空間,外圍設備和物理內存使用統一的物理地址空間。有些處理器架構把分配給外圍設備的物理地址區域稱為設備內存。
- 處理器通過外圍設備控制器的寄存器訪問外圍設備,寄存器分為控制器,狀態寄存器和數據寄存器三大類。外圍設備的寄存器通常被連續地編址,處理器對外圍設備寄存編址方式分為:i/o映射方式(i/o-mapped),內存映射方式(memory-mapped)。
- 應用程序只能通過虛擬地址訪問外設寄存器,內核提供API函數來把外設寄存器的物理地址映射到虛擬地址空間。
- ARM64(物理地址寬度最大支持48位)架構分為兩種內存類型:
- 正常內存(Noramal Memory):包括物理內存和只讀存儲器(ROM);
- 設備內存(Device Memory):指分配給外圍設備寄存器的物理地址區域;
- 設備內存共享屬性總是外部共享,緩存屬性總是不可緩存(必須繞過處理器的緩存)
- 二、內存映射原理
“內存映射即在進程的虛擬地址空間中創建一個映射,分為兩種:
”
- 文件映射:文件支持的內存映射,把文件的一個區間映射到進程的虛擬地址空間,數據源是存儲設備上的文件。
- 匿名映射:沒有文件支持的內存映射,把物理內存映射到進程的虛擬地址空間,沒有數據源。
【原理】:創建內存映射時,在進程的用戶虛擬地址空間中分配一個虛擬內存區域。內核采用延遲分配物理內存的策略,在進程第一次訪問虛擬頁的時候,產生缺頁異常。==如果是文件映射,那么分配物理頁,把文件指定區間的數據讀到物理頁中,然后在頁表中把虛擬頁映射到物理頁。如果是匿名映射,就分配物理頁,然后在頁表中把虛擬頁映射到物理頁。==(1)兩個進程可以使用共享的文件映射實現共享內存。匿名映射通常是私有映射,共享的匿名映射只可能出現父進程和子進程之間。在進程的虛擬地址空間中,代碼段和數據段是私有的文件映射,未初始化數據段、堆棧是私有的匿名映射。(2)修改過的臟頁面不會立即更新到文件中,可以調用msync來強制同步寫入文件。
flowchartLR
task_struct-->mm_struct-->vm_area_struct
三、虛擬內存源碼分析
3.1 相關數據結構
structvm_area_struct{
/*ThefirstcachelinehastheinfoforVMAtreewalking.*/
//這兩個成員分別用來保存該虛擬內存空間的首地址和末地址后第一個字節的地址
unsignedlongvm_start;/*Ourstartaddresswithinvm_mm.*/
unsignedlongvm_end;/*Thefirstbyteafterourendaddresswithinvm_mm.*/
/*linkedlistofVMareaspertask,sortedbyaddress*/
structvm_area_struct*vm_next,*vm_prev;
//如果采用鏈表組織化,會影響它搜索速度問題,解決此問題采用紅黑樹(每個進程結構體mm_struct中都
//創建一顆紅黑樹,將VMA作為一個節點加入紅黑樹給中,這樣可以提升搜索速度)
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;/*指向內存的私有數據*/
#ifndefCONFIG_MMU
structvm_region*vm_region;/*NOMMUmappingregion*/
#endif
#ifdefCONFIG_NUMA
structmempolicy*vm_policy;/*NUMApolicyfortheVMA*/
#endif
structvm_userfaultfd_ctxvm_userfaultfd_ctx;
};
3.2 虛擬內存操作集合
structvm_operations_struct{
void(*open)(structvm_area_struct*area);//在創建虛擬內存區域時調用open方法
void(*close)(structvm_area_struct*area);//在刪除虛擬內存區域時調用close方法
int(*mremap)(structvm_area_struct*area);//使用系統調用mremap移動虛擬內存區域時調用
int(*fault)(structvm_area_struct*vma,structvm_fault*vmf);//訪問文件映射的虛擬頁時,如果沒有映射到物理頁,生成
//缺頁異常,異常處理程序調用fault方法來把文件的數據讀到文件頁緩存當中
int(*pmd_fault)(structvm_area_struct*,unsignedlongaddress,
pmd_t*,unsignedintflags);//與fault類似,區別是該方法針對使用透明巨型頁的文件映射
/*讀文件映射的虛擬頁時,如果沒有映射到物理頁,生成缺頁異常,異常處理程序除了讀入正在訪問的文件頁
還會預讀后續文件頁,調用map_pages方法在文件的頁緩存中分配物理頁*/
void(*map_pages)(structvm_area_struct*vma,structvm_fault*vmf);
/*notificationthatapreviouslyread-onlypageisabouttobecome
*writable,ifanerrorisreturneditwillcauseaSIGBUS*/
/*第一次寫私有的文件映射時,生成頁錯誤異常,異常處理程序執行寫時復制,調用page_mkwrite方法以
通知文件系統頁即將變成可寫,以便文件系統檢查是否允許寫,或者等待頁進入合適的狀態*/
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);
};
四、系統調用
“”
- 應用程序通常使用C標準庫提供的函數malloc()申請內存。glibc庫的內存分配器ptmalloc使用brk或mmap向內核以頁為單位申請虛擬內存,然后把頁劃分成小內存塊分配給應用程序。默認的閾值是128kb,如果應用程序申請的內存長度小于閾值,ptmalloc分配器使用brk向內核申請虛擬內存,否則ptmalloc分配器使用mmap向內核申請虛擬內存。
- 應用程序可以直接使用mmap向內核申請虛擬內存。
【回顧mmap內存映射原理三個階段】:
- 進程啟動映射過程,并且在虛擬地址空間中為映射創建虛擬映射區域;
- 調用內核空間的系統調用函數mmap(不同于用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關系;
- 進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝。
內存管理子系統提供以下常用系統調用函數:
- mmap() ---->創建內存映射
“#include
”void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- 系統調用mmap():進程創建匿名的內存映射,把內存的物理頁映射到進程的虛擬地址空間。進程把文件映射到進程的虛擬地址空間,可以像訪問內存一樣訪問文件,不需要調用系統調用read()/write()訪問文件,從而避免用戶模式和內核模式之間的切換,提高讀寫文件速度。兩個進程針對同一個文件創建共享的內存映射,實現共享內存。
- munmap() ---->刪除內存映射
“#include
”int munmap(void *addr, size_t len);
代碼實踐
#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;
}
運行結果
審核編輯:陳陳
-
Linux
+關注
關注
87文章
11292瀏覽量
209332 -
內存映射
+關注
關注
0文章
14瀏覽量
7415
原文標題:Linux內核 | 內存映射
文章出處:【微信號:嵌入式開發AIoT,微信公眾號:嵌入式開發AIoT】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論