亚洲精品一二区_国产黄色片网站_99久久久成人国产精品_蜜臀网_国产精品一区二区三区免费_成人av中文字幕_91精品国产欧美一区二区成人

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > Linux的mmap文件內存映射機制

Linux的mmap文件內存映射機制 時間:2018-09-27      來源:未知

Linux的mmap文件內存映射機制

在講述文件映射的概念時, 不可避免的要牽涉到虛存(SVR 4的VM). 實際上, 文件映射是虛存的中心概念, 文件映射一方面給用戶提供了一組措施, 好似用戶將文件映射到自己地址空間的某個部分, 使用簡單的內存訪問指令讀寫文件;另一方面, 它也可以用于內核的基本組織模式, 在這種模式種, 內核將整個地址空間視為諸如文件之類的一組不同對象的映射. 中的傳統文件訪問方式是, 首先用open系統調用打開文件, 然后使用read, write以及lseek等調用進行順序或者隨即的I/O. 這種方式是非常低效的, 每一次I/O操作都需要一次系統調用. 另外, 如果若干個進程訪問同一個文件, 每個進程都要在自己的地址空間維護一個副本, 浪費了內存空間. 而如果能夠通過一定的機制將頁面映射到進程的地址空間中, 也就是說首先通過簡單的產生某些內存管理數據結構完成映射的創建. 當進程訪問頁面時產生一個缺頁中斷, 內核將頁面讀入內存并且更新頁表指向該頁面. 而且這種方式非常方便于同一副本的共享.

VM是面向對象的方法設計的, 這里的對象是指內存對象: 內存對象是一個軟件抽象的概念, 它描述內存區與后備存儲之間的映射. 系統可以使用多種類型的后備存儲, 比如交換空 間, 本地或者遠程文件以及幀緩存等等. VM系統對它們統一處理, 采用同一操作集操作, 比如讀取頁面或者回寫頁面等. 每種不同的后備存儲都可以用不同的方法實現這些操作. 這樣, 系統定義了一套統一的接口, 每種后備存儲給出自己的實現方法. 這樣, 進程的地址空間就被視為一組映射到不同數據對象上的的映射組成. 所有的有效地址就是那些映射到數據對象上的地址. 這些對象為映射它的頁面提供了持久性的后備存儲. 映射使得用戶可以直接尋址這些對象.

值得提出的是, VM體系結構獨立于Unix系統, 所有的Unix系統語義, 如正文, 數據及堆棧區都可以建構在基本VM系統之上. 同時, VM體系結構也是獨立于存儲管理的, 存儲管理是由操作系統實施的, 如: 究竟采取什么樣的對換和請求調頁算法, 究竟是采取分段還是分頁機制進行存儲管理, 究竟是如何將虛擬地址轉換成為物理地址等等(Linux中是一種叫Three Level Page Table的機制), 這些都與內存對象的概念無關.

下面介紹Linux中 VM的實現.

一個進程應該包括一個mm_struct(memory manage struct), 該結構是進程虛擬地址空間的抽象描述, 里面包括了進程虛擬空間的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息. 另外, 也有一個指向進程虛存區表(vm_area_struct: virtual memory area)的指針, 該鏈是按照虛擬地址的增長順序排列的. 在Linux進程的地址空間被分作許多區(vma), 每個區(vma)都對應虛擬地址空間上一段連續的區域, vma是可以被共享和保護的獨立實體, 這里的vma就是前面提到的內存對象. 下面是vm_area_struct的結構, 其中, 前半部分是公共的, 與類型無關的一些數據成員, 如: 指向mm_struct的指針, 地址范圍等等, 后半部分則是與類型相關的成員, 其中重要的是一個指向vm_operation_struct向量表的指針vm_ops, vm_pos向量表是一組虛函數, 定義了與vma類型無關的接口. 每一個特定的子類, 即每種vma類型都必須在向量表中實現這些操作. 這里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout這些操作.

struct vm_area_struct {

/*公共的, 與vma類型無關的 */

struct mm_struct * vm_mm;

unsigned long vm_start;

unsigned long vm_end;

struct vm_area_struct *vm_next;

pgprot_t vm_page_prot;

unsigned long vm_flags;

short vm_avl_height;

struct vm_area_struct * vm_avl_left;

struct vm_area_struct * vm_avl_right;

struct vm_area_struct *vm_next_share;

struct vm_area_struct **vm_pprev_share;

/* 與類型相關的 */

struct vm_operations_struct * vm_ops;

unsigned long vm_pgoff;

struct file * vm_file;

unsigned long vm_raend;

void * vm_private_data;

};

vm_ops: open, close, no_page, swapin, swapout……

介紹完VM的基本概念后, 我們可以講述mmap和munmap系統調用了. mmap調用實際上就是一個內存對象vma的創建過程, mmap的調用格式是:

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

其中start是映射地址, length是映射長度, 如果flags的MAP_FIXED不被置位, 則該參數通常被忽略, 而查找進程地址空間中第一個長度符合的空閑區域;Fd是映射文件的文件句柄, offset是映射文件中的偏移地址;prot是映射保護權限, 可以是PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE, flags則是指映射類型, 可以是MAP_FIXED, MAP_PRIVATE, MAP_SHARED, 該參數必須被指定為MAP_PRIVATE和MAP_SHARED其中之一, MAP_PRIVATE 是創建一個寫時拷貝映射(copy-on-write), 也就是說如果有多個進程同時映射到一個文件上, 映射建立時只是共享同樣的存儲頁面, 但是某進程企圖修改頁面內容, 則復制一個副本給該進程私用, 它的任何修改對其它進程都不可見. 而MAP_SHARED則無論修改與否都使用同一副本, 任何進程對頁面的修改對其它進程都是可見的.

mmap系統調用的實現過程是:

1.先通過文件系統定位要映射的文件;

2.權限檢查, 映射的權限不會超過文件打開的方式, 也就是說如果文件是以只讀方式打開, 那么則不允許建立一個可寫映射;

3.創建一個vma對象, 并對之進行初始化;

4.調用映射文件的mmap函數, 其主要工作是給vm_ops向量表賦值;

5.把該vma鏈入該進程的vma鏈表中, 如果可以和前后的vma合并則合并;

6.如果是要求VM_LOCKED(映射區不被換出)方式映射, 則發出缺頁請求, 把映射頁面讀入內存中.

munmap(void * start, size_t length):

該調用可以看作是 mmap的一個逆過程. 它將進程中從start開始length長度的一段區域的映射關閉, 如果該區域不是恰好對應一個vma, 則有可能會分割幾個或幾個vma.

msync(void * start, size_t length, int flags):

把映射區域的修改回寫到后備存儲中. 因為munmap時并不保證頁面回寫, 如果不調用msync, 那么有可能在munmap后丟失對映射區的修改. 其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE, MS_SYNC要求回寫完成后才返回, MS_ASYNC發出回寫請求后立即返回, MS_INVALIDATE使用回寫的內容更新該文件的其它映射. 該系統調用是通過調用映射文件的sync函數來完成工作的.

brk(void * end_data_segement):

將進程的數據段擴展到 end_data_segement指定的地址, 該系統調用和mmap的實現方式十分相似, 同樣是產生一個vma, 然后指定其屬性. 不過在此之前需要做一些合法性檢查, 比如該地址是否大于mm->end_code, end_data_segement和mm->brk之間是否還存在其它vma等等. 通過brk產生的vma映射的文件為空, 這和匿名映射產生的vma相似, 關于匿名映射不做進一步介紹. 庫函數malloc就是通過brk實現的.

Linux提供了內存映射函數mmap, 它把文件內容映射到一段內存上(準確說是虛擬內存上), 通過對這段內存的讀取和修改, 實現對文件的讀取和修改, 先來看一下mmap的函數聲明:

頭文件:

原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);

返回值: 成功則返回映射區起始地址, 失敗則返回MAP_FAILED(-1).

參數:

addr: 指定映射的起始地址, 通常設為NULL, 由系統指定.

length: 將文件的多大長度映射到內存.

prot: 映射區的保護方式, 可以是:

PROT_EXEC: 映射區可被執行.

PROT_READ: 映射區可被讀取.

PROT_WRITE: 映射區可被寫入.

PROT_NONE: 映射區不能存取.

flags: 映射區的特性, 可以是:

MAP_SHARED: 對映射區域的寫入數據會復制回文件, 且允許其他映射該文件的進程共享.

MAP_PRIVATE: 對映射區域的寫入操作會產生一個映射的復制(copy-on-write), 對此區域所做的修改不會寫回原文件.

此外還有其他幾個flags不很常用, 具體查看linux C函數說明.

fd: 由open返回的文件描述符, 代表要映射的文件.

offset: 以文件開始處的偏移量, 必須是分頁大小的整數倍, 通常為0, 表示從文件頭開始映射.

下面說一下內存映射的步驟:

用open系統調用打開文件, 并返回描述符fd.

用mmap建立內存映射, 并返回映射首地址指針start.

對映射(文件)進行各種操作, 顯示(printf), 修改(sprintf).

用munmap(void *start, size_t lenght)關閉內存映射.

用close系統調用關閉文件fd.

注意事項:

在修改映射的文件時, 只能在原長度上修改, 不能增加文件長度, 因為內存是已經分配好的.

Linux-mmap函數介紹

mmap函數是unix/linux下的系統調用,來看《Unix Netword programming》卷二12.2節對mmap的介紹:

The mmap function maps either a file or a Posix shared memory object into the address space of a process.We use this function for three purposes:

1. with a regular file to provide memory-mapped I/O

2. with special files to provide anonymous memory mappings

3. with shm_open to provide Posix shared memory between unrelated processes

mmap系統調用并不是完全為了用于共享內存而設計的。它本身提供了不同于一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而 Posix或系統V的共享內存IPC則純粹用于共享目的,當然mmap()實現共享內存也是其主要應用之一。

mmap系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間后,進程可以像訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。

我們的程序中大量運用了mmap,用到的正是mmap的這種“像訪問普通內存一樣對文件進行訪問”的功能。實踐證明,當要對一個文件頻繁的進行訪問,并且指針來回移動時,調用mmap比用常規的方法快很多。

來看看mmap的定義:

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

參數fd為即將映射到進程空間的文件描述字,一般由open()返回,同時,fd可以指定為-1,此時須指定flags參數中的MAP_ANON,表明進行的是匿名映射(不涉及具體的文件名,避免了文件的創建及打開,很顯然只能用于具有親緣關系的進程間通信)。

len是映射到調用進程地址空間的字節數,它從被映射文件開頭offset個字節開始算起。

prot參數指定共享內存的訪問權限。可取如下幾個值的或:PROT_READ(可讀),PROT_WRITE(可寫),PROT_EXEC(可執行),PROT_NONE(不可訪問)。

flags由以下幾個常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED。其中,MAP_SHARED,MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用。

  如果指定為MAP_SHARED,則對映射的內存所做的修改同樣影響到文件。如果是MAP_PRIVATE,則對映射的內存所做的修改僅對該進程可見,對文件沒有影響。

offset參數一般設為0,表示從文件頭開始映射。

參數addr指定文件應被映射到進程空間的起始地址,一般被指定一個空指針,此時選擇起始地址的任務留給內核來完成。函數的返回值為后文件映射到進程空間的地址,進程可直接操作起始地址為該值的有效地址。

后,舉個例子來結束本節。4.2節說過,Fileinformation數組是以二進制的形式寫進一個叫inforindex的文件中。那么,當要訪問 Fileinformation數組時,代碼類似這樣:

struct stat st;

char buffer=” inforindex”;

Fileinformation *_fileinfoIndexptr = NULL;

if(stat(buffer,&st)<0)

{

fprintf(stderr,"error to stat %s\n",buffer);

exit(-1);

}

// mmap the inforindex to _fileinfoIndexptr

int fd=open(buffer, O_RDONLY);

if(fd<0)

{

printf("error to open %s\n",buffer);

exit(-1);

}

_fileinfoIndexptr = (Fileinformation*)mmap(NULL,st.st_size, PROT_READ,MAP_SHARED,fd,0);

if(MAP_FAILED == _fileinfoIndexptr)

{

printf("error to mmap %s\n",buffer);

close(fd);

exit(-1);

}

close(fd);

下面這個例子顯示了把文件映射到內存的方法

源代碼是:

*********************************************************************/

#include /* for mmap and munmap */

#include /* for open */

#include /* for open */

#include /* for open */

#include /* for lseek and write */

#include

int main(int argc, char **argv)

{

int fd;

char *mapped_mem, * p;

int flength = 1024;

void * start_addr = 0;

fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

flength = lseek(fd, 1, SEEK_END);

write(fd, "\0", 1); /* 在文件后添加一個空字符,以便下面printf正常工作 */

lseek(fd, 0, SEEK_SET);

mapped_mem = mmap(start_addr, flength, PROT_READ, //允許讀

MAP_PRIVATE, //不允許其它進程訪問此內存區域

fd, 0);

/* 使用映射區域. */

printf("%s\n", mapped_mem); /* 為了保證這里工作正常,參數傳遞的文件名好是一個文本文件 */

close(fd);

munmap(mapped_mem, flength);

return 0;

}

編譯運行此程序:

gcc -Wall mmap.c

./a.out text_filename

上面的方法因為用了PROT_READ,所以只能讀取文件里的內容,不能修改,如果換成PROT_WRITE就可以修改文件的內容了。又由于 用了MAAP_PRIVATE所以只能此進程使用此內存區域,如果換成MAP_SHARED,則可以被其它進程訪問,比如下面的:

#include /* for mmap and munmap */

#include /* for open */

#include /* for open */

#include /* for open */

#include /* for lseek and write */

#include

#include /* for memcpy */

int main(int argc, char **argv)

{

int fd;

char *mapped_mem, * p;

int flength = 1024;

void * start_addr = 0;

fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

flength = lseek(fd, 1, SEEK_END);

write(fd, "\0", 1); /* 在文件后添加一個空字符,以便下面printf正常工作 */

lseek(fd, 0, SEEK_SET);

start_addr = 0x80000;

mapped_mem = mmap(start_addr, flength, PROT_READ|PROT_WRITE, //允許寫入

MAP_SHARED, //允許其它進程訪問此內存區域

fd, 0);

/* 使用映射區域. */

printf("%s\n", mapped_mem); /* 為了保證這里工作正常,參數傳遞的文件名好是一個文本文 */

while((p = strstr(mapped_mem, "Hello"))) { /* 此處來修改文件 內容 */

memcpy(p, "Linux", 5);

p += 5;

}

close(fd);

munmap(mapped_mem, flength);

return 0;

}

man -a mmap 看更詳細的信息

上一篇:PHP中Smarty模板的使用

下一篇:Android自定義ViewGroup

熱點文章推薦
華清學員就業榜單
高薪學員經驗分享
熱點新聞推薦
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,京ICP備16055225號-5京公海網安備11010802025203號

回到頂部

主站蜘蛛池模板: 日本高清视频www夜色资源网 | 亚洲国产网| 男人用嘴添女人下身免费视频 | 亚洲一区二区三区高清网 | 日本黄页免费 | 国产欧美精品一区二区三区–老狼 | 国产免费不卡 | aa视频在线 | 欧美日穴 | 欧美一级久久久久久久大片动画 | 色之综合天天综合色天天棕色 | 好吊妞这里只有精品 | 黄色一级欧美 | 欧美一级色| 国产午夜一级鲁丝片 | 免费特级黄毛片在线成人观看 | 日本高清中文字幕在线观穿线视频 | 欧美成人精品一级高清片 | 国产一区亚洲 | 日本人jizz | 久久艹在线观看 | 激情免费视频 | 久久久精品久久久久特色影视 | 91成人爽a毛片一区二区 | 性做久久久久久坡多野结衣 | 天天碰人人| 久久亚洲国产视频 | 一级毛片全部免费播放 | 欧美激情一区二区三区中文字幕 | 好吊妞在线成人免费 | 99视频精品全部在线播放 | 另类侏儒freesex | 九九影院理论片 | 狠狠色噜噜狠狠狠狠98 | 免费香蕉依人在线视频久 | 日本狠狠操| 久久人 | 欧美精品在线一区二区三区 | 黄网站色视频免费观看45分钟 | jizz亚洲| 奇米色777欧美一区二区 |