0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)存映射的原理

嵌入式開發(fā)AIoT ? 來源:嵌入式開發(fā)AIoT ? 2023-01-15 09:55 ? 次閱讀

一、物理地址空間

  1. 物理地址是處理器在系統(tǒng)總線上看到的地址。使用RISC的處理器通常只實(shí)現(xiàn)一個(gè)物理地址空間,外圍設(shè)備和物理內(nèi)存使用統(tǒng)一的物理地址空間。有些處理器架構(gòu)把分配給外圍設(shè)備的物理地址區(qū)域稱為設(shè)備內(nèi)存。
  2. 處理器通過外圍設(shè)備控制器寄存器訪問外圍設(shè)備,寄存器分為控制器,狀態(tài)寄存器和數(shù)據(jù)寄存器三大類。外圍設(shè)備的寄存器通常被連續(xù)地編址,處理器對外圍設(shè)備寄存編址方式分為:i/o映射方式(i/o-mapped),內(nèi)存映射方式(memory-mapped)。
  3. 應(yīng)用程序只能通過虛擬地址訪問外設(shè)寄存器,內(nèi)核提供API函數(shù)來把外設(shè)寄存器的物理地址映射到虛擬地址空間。
  4. 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è)映射,分為兩種:

  1. 文件映射:文件支持的內(nèi)存映射,把文件的一個(gè)區(qū)間映射到進(jìn)程的虛擬地址空間,數(shù)據(jù)源是存儲(chǔ)設(shè)備上的文件。
  2. 匿名映射:沒有文件支持的內(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è)階段】:
  1. 進(jìn)程啟動(dòng)映射過程,并且在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域;
  2. 調(diào)用內(nèi)核空間的系統(tǒng)調(diào)用函數(shù)mmap(不同于用戶空間函數(shù)),實(shí)現(xiàn)文件物理地址和進(jìn)程虛擬地址的一一映射關(guān)系;
  3. 進(jìn)程發(fā)起對這片映射空間的訪問,引發(fā)缺頁異常,實(shí)現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝。
內(nèi)存管理子系統(tǒng)提供以下常用系統(tǒng)調(diào)用函數(shù):
  1. 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)存。
  1. 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é)果

f5d13690-942f-11ed-bfe3-dac502259ad0.png

審核編輯:陳陳


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 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)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    從史前文明到女媧補(bǔ)天:Linux內(nèi)存逆向映射(reverse mapping)技術(shù)的前世今生

    關(guān)于Linux內(nèi)存管理逆向映射技術(shù)的歷史和現(xiàn)在的分析,投稿標(biāo)題《逆向映射的演進(jìn)》,后經(jīng)過小編與郭大俠商議改為《Linux
    的頭像 發(fā)表于 09-06 15:45 ?1w次閱讀
    從史前文明到女媧補(bǔ)天:<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)存</b>逆向<b class='flag-5'>映射</b>(reverse mapping)技術(shù)的前世今生

    Linux內(nèi)核之內(nèi)存映射原理分析

    Linux 內(nèi)核采用延遲分配物理內(nèi)存的策略,在進(jìn)程第一次訪問虛擬頁的時(shí)候,產(chǎn)生缺頁異常。如果是文件映射,那么分配物理頁,把文件指定區(qū)間的數(shù)據(jù)讀到物理頁中,然后在頁表中把虛擬頁映射到物理
    發(fā)表于 07-21 17:06 ?2336次閱讀

    Linux內(nèi)存映射與頁表詳解

    我們通常所說的內(nèi)存容量,指的是物理內(nèi)存,只有內(nèi)核才可以直接訪問物理內(nèi)存,進(jìn)程并不可以。
    發(fā)表于 08-18 12:30 ?1104次閱讀

    關(guān)于Linux內(nèi)存管理的詳細(xì)介紹

    Linux內(nèi)存管理是指對系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮等一系列操作的管理。在Linux中,
    發(fā)表于 03-06 09:28 ?1053次閱讀

    拆解mmap內(nèi)存映射的本質(zhì)!

    mmap 內(nèi)存映射里所謂的內(nèi)存其實(shí)指的是虛擬內(nèi)存,在調(diào)用 mmap 進(jìn)行匿名映射的時(shí)候(比如進(jìn)行堆內(nèi)存
    的頭像 發(fā)表于 01-24 14:30 ?1371次閱讀
    拆解mmap<b class='flag-5'>內(nèi)存</b><b class='flag-5'>映射</b>的本質(zhì)!

    Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解

    的數(shù)據(jù)可能不在內(nèi)存中。 Linux內(nèi)核地址映射模型 x86 CPU采用了段頁式地址映射模型。進(jìn)程代碼中的地址為邏輯地址,經(jīng)過段頁式地址映射
    發(fā)表于 05-08 10:33 ?3436次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核地址<b class='flag-5'>映射</b>模型與<b class='flag-5'>Linux</b>內(nèi)核高端<b class='flag-5'>內(nèi)存</b>詳解

    [4.5.1]--4.5動(dòng)手實(shí)踐-Linux內(nèi)存映射基礎(chǔ)(上)

    Linux
    jf_75936199
    發(fā)布于 :2023年02月25日 01:56:27

    [4.7.1]--4.7動(dòng)手實(shí)踐-Linux內(nèi)存映射測試(下)

    Linux
    jf_75936199
    發(fā)布于 :2023年02月25日 02:05:04

    Linux的mmap文件內(nèi)存映射機(jī)制

    Linux的mmap文件內(nèi)存映射機(jī)制在講述文件映射的概念時(shí), 不可避免的要牽涉到虛存(SVR 4的VM). 實(shí)際上, 文件映射是虛存的中心概
    發(fā)表于 03-08 09:54

    RTOS和Linux中的內(nèi)存映射及移植方法

    映射到相應(yīng)得用戶空間去。同樣重要的是,在I/O調(diào)用密集的嵌入式程序中怎么樣把RTOS的硬件接口代碼移植到更加規(guī)范的Linux設(shè)備驅(qū)動(dòng)程序中去。 本文把概述幾種常用的經(jīng)常出現(xiàn)于現(xiàn)有嵌入式應(yīng)用中的內(nèi)存
    發(fā)表于 07-03 07:43

    Linux的mmap文件內(nèi)存映射機(jī)制

    的. Linux提供了內(nèi)存映射函數(shù)mmap, 它把文件內(nèi)容映射到一段內(nèi)存上(準(zhǔn)確說是虛擬內(nèi)存上)
    發(fā)表于 04-02 14:35 ?420次閱讀

    淺析linux內(nèi)存映射原理

    內(nèi)存映射,簡而言之就是將用戶空間的一段內(nèi)存區(qū)域映射到內(nèi)核空間,映射成功后,用戶對這段內(nèi)存區(qū)域的修
    發(fā)表于 08-24 09:35 ?1600次閱讀
    淺析<b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>映射</b>原理

    Linux 內(nèi)存管理總結(jié)

    一、Linux內(nèi)存管理概述 Linux內(nèi)存管理是指對系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮
    的頭像 發(fā)表于 11-10 14:58 ?486次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b>管理總結(jié)