時間:2015-06-28 00:00:00 來源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評論(0)
Linux采用虛擬內(nèi)存技術(shù),系統(tǒng)中的所有進程之間以虛擬方式共享內(nèi)存。對每個進程來說,它們好像都可以訪問整個系統(tǒng)的所有物理內(nèi)存。更重要的是,即使單獨一個進程,它擁有的地址空間也可以遠遠大于系統(tǒng)物理內(nèi)存。
進程地址空間由每個進程中的線性地址區(qū)組成,每個進程都有一個32位或64位的平坦(flat) 空間,空間的具體大小取決于體系結(jié)構(gòu)。"平坦"指地址空間范圍是一個獨立的連續(xù)區(qū)間。通常情況下,每個進程都有唯一的這種平坦空間,而且每個進程的地址空 間之間彼此互不相干。兩個不同的進程可以在它們各自地址空間的相同地址內(nèi)存存放不同的數(shù)據(jù)。但是進程之間也可以選擇共享地址空間,我們稱這樣的進程為線程。
在地址空間中,我們更為關(guān)心的是進程有權(quán)訪問的虛擬內(nèi)存地址區(qū)間,比如08048000~0804c000。這些可被訪問的合法地址區(qū)間被成為內(nèi)存區(qū)域(memory area),通過內(nèi)核,進程可以給自己的地址空間動態(tài)地添加或減少內(nèi)存區(qū)域。
進程只能訪問有效范圍內(nèi)的內(nèi)存地址。每個內(nèi)存區(qū)域也具有相應(yīng)進程必須遵循的特定訪問屬性,如只讀、只寫、可執(zhí)行等屬性。如果一個進程訪問了不在有效范圍中的地址,或以不正確的方式訪問有效地址,那么內(nèi)核就會終止該進程,并返回"段錯誤"信息。
?? 內(nèi)存區(qū)域可以包含各種內(nèi)存對象,如下:
?? 可執(zhí)行文件代碼的內(nèi)存映射,成為代碼段(text section)。
?? 可執(zhí)行文件的已初始化全局變量的內(nèi)存映射,成為數(shù)據(jù)段(data section)。
?? 包含未初始化全局變量的零頁(也就是bss段)的內(nèi)存映射。零頁是指頁面中的數(shù)據(jù)全部為0。
?? 用于進程用戶空間棧的零頁的內(nèi)存映射。
?? 每一個諸如C庫或動態(tài)鏈接程序等共享庫的代碼段、數(shù)據(jù)段和bss也會被載入進程的地址空間。
?? 任何內(nèi)存映射文件。
?? 任何共享內(nèi)存段。
?? 任何匿名的內(nèi)存映射,比如由malloc()分配的內(nèi)存。
進程地址空間的任何有效地址都只能位于唯一的區(qū)域,這些內(nèi)存區(qū)域不能相互覆蓋。可以看到,在執(zhí)行的進程中,每個不同的內(nèi)存片斷都對應(yīng)一個獨立的內(nèi)存區(qū)域:棧、對象代碼、全局變量、被映射的文件等等。
內(nèi)核使用內(nèi)存描述符表示進程的地址空間。內(nèi)存描述符由mm_struct結(jié)構(gòu)體表示,定義在文件<linux/sched.h>中,該結(jié)構(gòu)包含了和進程地址空間有關(guān)的全部信息。
VMA
內(nèi)存區(qū)域由vm_area_struct結(jié)構(gòu)體描述,定義在文件<linux/mm.h>中,內(nèi)存區(qū)域在內(nèi)核中也經(jīng)常被稱作虛擬內(nèi)存區(qū)域或者VMA。
VMA標志是一種位標志,它定義在vm_area_struct結(jié)構(gòu)中(該結(jié)構(gòu)中的vm_flags子域)。和物理頁的訪問權(quán)限不同,VMA標志反映了內(nèi)核處理頁面索需要遵守的行為準則,而不是硬件要求。VM_IO標志內(nèi)存區(qū)域中包含對設(shè)備I/O空間的映射。該標志通常在設(shè)備驅(qū)動程序執(zhí)行mmap()函數(shù)進行I/O空間映射時才被設(shè)置,同時該標志也表示該內(nèi)存區(qū)域不能被包含在任何進程的存放轉(zhuǎn)存(core dump)中。VM_RESERVED標志內(nèi)存區(qū)域不能被換出,它也是在設(shè)備驅(qū)動程序進行映射時被設(shè)置。
vm_area_struct結(jié)構(gòu)體中的vm_ops域指向與指定內(nèi)存區(qū)域相關(guān)的操作函數(shù)表,內(nèi)核使用表中的方法操作VMA。
mmap()和do_mmap():創(chuàng)建地址區(qū)間
內(nèi)核使用do_mmap()函數(shù)創(chuàng)建一個新的線性地址區(qū)間。但是說給函數(shù)創(chuàng)建一個新VMA并不非常準確,因為如果創(chuàng)建的地址區(qū)間和一個已經(jīng)存在的地址區(qū)間相鄰,并且它們具有相同的訪問權(quán)限的話,那么兩個區(qū)間將合并為一個。如果不能合并,那么就確實需要創(chuàng)建一個新的VMA了。但無論哪種情況,do_mmap()函數(shù)都會將一個地址區(qū)間加入到進程的地址空間中——無論是擴展已經(jīng)存在的內(nèi)存區(qū)域還是創(chuàng)建一個新的區(qū)域。
do_mmap()函數(shù)聲明在文件
unsigned long do_mmap(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flag, unsigned long offset)
在用戶空間可以通過mmap()函數(shù)調(diào)用獲取內(nèi)核函數(shù)do_mmap()的功能。mmap()系統(tǒng)調(diào)用原型如下:
void *mmap2(void *start, size_t length,
int prot, int flags,
int fd, off_t pgoff)
do_munmap()函數(shù)從特定的進程地址空間中刪除指定地址區(qū)間,該函數(shù)在文件
int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
系統(tǒng)調(diào)用munmap()給用戶空間程序提供了一種從自身地址空間中刪除指定地址區(qū)間的方法,它和系統(tǒng)調(diào)用mmap()的作用相反:
int munmap(void *start, size_t length)
mmap設(shè)備操作
對于驅(qū)動程序來說,內(nèi)存映射可以提供給用戶程序直接訪問設(shè)備內(nèi)存的能力。映射一個設(shè)備,意味著使用戶空間的一段地址關(guān)聯(lián)到設(shè)備內(nèi)存上。無論何時,只要程序在分配的地址范圍內(nèi)進行讀取或者寫入,實際上就是對設(shè)備的訪問。
并不是所有的設(shè)備都能進行mmap抽象。例如,串口設(shè)備和其他面向流的設(shè)備就無法實現(xiàn)這種抽象。mmap的另一個限制是映射都是以PAGE_SIZE為單位的。內(nèi)核只能在頁表一級處理虛擬地址;因此,被映射的區(qū)域必須是PAGE_SIZE的整數(shù)倍,而且必須位于起始于PAGE_SIZE整數(shù)倍地址的物理內(nèi)存內(nèi)。如果區(qū)域的大小不是頁大小的整數(shù)倍,內(nèi)核就通過生成一個稍微大一些的區(qū)域來容納它。
mmap方法是file_operations結(jié)構(gòu)中的一員,并且在執(zhí)行mmap系統(tǒng)調(diào)用時就會調(diào)用該方法。在調(diào)用實際方法之前,內(nèi)核會完成很多工作,而且該方法的原型與系統(tǒng)調(diào)用的原型由很大區(qū)別。
文件操作聲明如下:
int (*mmap) (struct file * filp, struct vm_area_struct *vma);
其中vma參數(shù)包含了用于訪問設(shè)備的虛擬地址區(qū)間的信息。大部分工作已經(jīng)由內(nèi)核完成了,要實現(xiàn)mmap,驅(qū)動程序只要為這一地址范圍構(gòu)造合適的頁表即可,如果需要的話,就用一個新的操作集替換vma->vm_ops。
有兩種建立頁表的方法:使用remap_page_range函數(shù)可一次建立所有的頁表,或者通過nopage VMA方法每次建立一個頁表。
#p#副標題#e#
構(gòu)造用于映射一段物理地址的新頁表的工作是由remap_page_range完成的,它的原型如下:
int remap_page_range(unsigned long virt_add, unsigned long phys_add,
unsigned long size, pgprot_t prot);
nopage方法具有如下原型:
struct page (*nopage) (struct vm_area_struct *vma, unsigned long address, int write_access);
當用戶進程訪問當前不在內(nèi)存中的VMA頁面時,就會調(diào)用關(guān)聯(lián)的VMA操作集中的nopage函數(shù)。參數(shù)address包含導致失效的虛擬地址,該地址向下取整到所在頁的起始地址。函數(shù)nopage必須定位并返回指向用戶所期望的頁的struct page指針。這個函數(shù)還要調(diào)用get_page宏,以增加它所返回的頁面的使用計數(shù):get_page(struct page *pageptr)。
nopage方法在mremap系統(tǒng)調(diào)用中非常有用。應(yīng)用程序使用mremap來改變應(yīng)該映射區(qū)域的邊界地址。mremap系統(tǒng)調(diào)用的原型如下:
void? * mremap(void? *old_address,? size_t old_size , size_t new_size, unsigned long flags);
Linux的mremap實現(xiàn)沒有通知驅(qū)動程序被映射區(qū)域的變化。事實上,當區(qū)域減小時,它會通過unmap方法通知驅(qū)動程序;但區(qū)域變大時,卻沒有相應(yīng)的回調(diào)函數(shù)可以利用。
換句話說,在映射區(qū)域增長時驅(qū)動程序不會得到通知,因為nopage方法會在將來完成相應(yīng)的工作,從而不必在真正需要之前使用內(nèi)存。因此,如果我們需要支持mremap系統(tǒng)調(diào)用,就必須實現(xiàn)nopage方法。
nopage方法必要的實現(xiàn)步驟如下:
首先計算想得到的物理地址,然后用__va()將它轉(zhuǎn)換為邏輯地址,最后用virt_to_page將邏輯地址轉(zhuǎn)成一個struct page, 返回指向這個struct page的指針。一般來說,直接從物理地址獲得struct page是可能的,但是這樣的代碼很難在不同的體系結(jié)構(gòu)之間移植。
remap_page_range的一個有意思的限制是,它只能對保留頁和物理內(nèi)存之外的物理地址給予訪問。也就是說,remap_page_range不會允許你重映射常規(guī)地址(常規(guī)物理內(nèi)存對應(yīng)的地址),相反,它會改為映射到零頁。因此,如果需要映射RAM的話,我們只能使用nopage方法。
下面是對simple.c(ldd2的樣例代碼)得一個簡單的分析:
/*
* Simple - REALLY simple memory mapping
關(guān)鍵詞標簽:Linux,MMAP映射
相關(guān)閱讀
熱門文章 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 Tomcat9.0如何安裝_Tomcat9.0環(huán)境變量配置方法 多種操作系統(tǒng)NTP客戶端配置 Linux操作系統(tǒng)修改IP
人氣排行 Linux下獲取CPUID、硬盤序列號與MAC地址 dmidecode命令查看內(nèi)存型號 linux tc實現(xiàn)ip流量限制 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 linux下解壓rar文件 lcx.exe、nc.exe、sc.exe入侵中的使用方法 Ubuntu linux 關(guān)機、重啟、注銷 命令 查看linux服務(wù)器硬盤IO讀寫負載