<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          完全分析 Linux mmap 原理

          共 11347字,需瀏覽 23分鐘

           ·

          2022-02-17 15:53

          內(nèi)存映射是一個很有趣的思想,我們都知道操作系統(tǒng)分為用戶態(tài)和內(nèi)核態(tài),用戶態(tài)是不能直接和物理設(shè)備打交道,如果我們用戶空間想訪問硬盤的一塊區(qū)域數(shù)據(jù),則需要兩次拷貝(硬盤->內(nèi)核->用戶),但是內(nèi)存映射的設(shè)計只需要發(fā)生一次拷貝,大大提高了讀取數(shù)據(jù)的效率。那么內(nèi)存映射的原理和內(nèi)核是如何實現(xiàn)的呢?

          1. 內(nèi)存映射概念

          內(nèi)存映射,簡而言之就是將用戶空間的一段內(nèi)存區(qū)域映射到內(nèi)核空間,映射成功后,用戶對這段內(nèi)存區(qū)域的修改可以直接反映到內(nèi)核空間,同樣,內(nèi)核空間對這段區(qū)域的修改也直接反映給用戶空間,對于用戶空間和內(nèi)核空間兩者之間需要進行大量數(shù)據(jù)傳輸?shù)炔僮鞯脑捫适欠浅8叩?。如下圖所示

          實現(xiàn)這樣的映射后,進程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁到對應(yīng)的文件磁盤上,就可以完成對于文件的操作,而不需要再調(diào)用read/write等系統(tǒng)調(diào)用函數(shù)。同時,內(nèi)核空間對于這段區(qū)域的修改也可以直接反映到用戶空間,從而可以實現(xiàn)不同進程間的文件共享。

          mmap/munmap?接口是常用的內(nèi)存映射的系統(tǒng)調(diào)用接口,無論是在用戶空間分配內(nèi)存、讀寫大文件、連接動態(tài)庫文件,還是多進程間共享內(nèi)存,都可以看到其身影,其聲明如下:

          #include?
          void*?mmap(void*?start,?size_t?length,?int?prot,?int?flags,?int?fd,?off_t?offset);
          int?munmap(void?*addr,?size_t?length);

          條件:

          mmap()必須以PAGE_SIZE為單位進行映射,而內(nèi)存也只能以頁為單位進行映射,若要映射非PAGE_SIZE整數(shù)倍的地址范圍,要先進行內(nèi)存對齊,強行以PAGE_SIZE的倍數(shù)大小進行映射。

          參數(shù)說明:

          • start:映射區(qū)的開始地址,設(shè)置為0時表示由系統(tǒng)決定映射區(qū)的起始地址。
          • length:映射區(qū)的長度。//長度單位是 以字節(jié)為單位,不足一內(nèi)存頁按一內(nèi)存頁處理
          • prot:期望的內(nèi)存保護標(biāo)志,不能與文件的打開模式?jīng)_突。是以下的某個值,可以通過or運算合理地組合在一起
            • PROT_EXEC: 表示映射的頁面是可以執(zhí)行的
            • PROT_READ:表示映射的頁面是可以讀取的
            • PROT_WRITE :表示映射的頁面是可以寫入的
            • PROT_NONE :表示映射的頁面是不可訪問的
          • flags:指定映射對象的類型,映射選項和映射頁是否可以共享。它的值可以是一個或者多個以下位的組合體
            • MAP_SHARED:創(chuàng)建一個共享映射的區(qū)域,多個進程可以通過共享映射的方式來映射一個文件,這樣其他進程也可以看到映射內(nèi)容的改變,修改后的內(nèi)容會同步到磁盤文件
            • MAP_PRIVATE:創(chuàng)建一個私有的寫時復(fù)制的映射,多個進程可以通過私有映射方式來映射一個文件,其他的進程不會看到映射文件內(nèi)容的改變,修改后也不會同步到磁盤中
            • MAP_ANONYMOUS:創(chuàng)建一個匿名映射,即沒有關(guān)聯(lián)到文件的映射
            • MAP_FIXED:
            • MAP_POPULATE:提前遇到文件內(nèi)容到映射區(qū)
          • fd:mmap映射釋放和文件相關(guān)聯(lián),可以分為匿名映射和文件映射
            • 文件映射:將一個普通文件的全部或者一部分映射到進程的虛擬內(nèi)存中。映射后,進程就可以直接在對應(yīng)的內(nèi)存區(qū)域操作文件內(nèi)容!
            • 匿名映射:匿名映射沒有對應(yīng)的文件或者對應(yīng)的文件時虛擬文件(如:/dev/zero),映射后會把內(nèi)存分頁全部初始化為0。
          • offset:被映射對象內(nèi)容的起點

          返回說明:

          成功執(zhí)行時,mmap()返回被映射區(qū)的指針,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。

          根據(jù)文件關(guān)聯(lián)性和映射區(qū)域示范共享等屬性,其分為


          私有映射共享映射
          匿名映射私有匿名映射(通常用于內(nèi)存分配),當(dāng)使用大于128K內(nèi)存時 fd = -1 且 flags = MAP_ANONYMOUS|MAP_PRIVATE共享匿名映射(通常用于父子進程間共享) fd = -1 且 flags = MAP_ANONYMOUS|MAP_SHARED
          文件映射私有文件映射(通常用于動態(tài)庫加載)共享文件映射(通常用于內(nèi)存映射IO、進程間通信)

          2. 源碼分析

          查看?mmap?的系統(tǒng)調(diào)用的代碼實現(xiàn),其流程為?sys_mmp_pg_off(),最終會調(diào)用達(dá)到?do_mmap_pgoff,該函數(shù)使一個體系結(jié)構(gòu)無關(guān)的代碼,定義在?mm/mmap.c?中,

          首先我們來看看?do_mmap(),是整個?mmap()?的具體操作函數(shù)

          unsigned?long?do_mmap(struct?file?*file,?unsigned?long?addr,
          ???unsigned?long?len,?unsigned?long?prot,
          ???unsigned?long?flags,?vm_flags_t?vm_flags,
          ???unsigned?long?pgoff,?unsigned?long?*populate)
          {
          ?struct?mm_struct?*mm?=?current->mm;????????//獲取該進程的memory?descriptor
          ?int?pkey?=?0;

          ?*populate?=?0;
          ?//函數(shù)對傳入的參數(shù)進行一系列檢查,?假如任一參數(shù)出錯,都會返回一個errno
          ?if?(!len)
          ??return?-EINVAL;

          ?/*
          ??*?Does?the?application?expect?PROT_READ?to?imply?PROT_EXEC?
          ??*
          ??*?(the?exception?is?when?the?underlying?filesystem?is?noexec
          ??*??mounted,?in?which?case?we?dont?add?PROT_EXEC.)
          ??*/

          ?if?((prot?&?PROT_READ)?&&?(current->personality?&?READ_IMPLIES_EXEC))
          ??if?(!(file?&&?path_noexec(&file->f_path)))
          ???prot?|=?PROT_EXEC;
          ?//假如沒有設(shè)置MAP_FIXED標(biāo)志,且addr小于mmap_min_addr,?因為可以修改addr,?所以就需要將addr設(shè)為mmap_min_addr的頁對齊后的地址
          ?if?(!(flags?&?MAP_FIXED))
          ??addr?=?round_hint_to_min(addr);

          ?/*?Careful?about?overflows..?*/
          ?len?=?PAGE_ALIGN(len);?????????//進行Page大小的對齊
          ?if?(!len)
          ??return?-ENOMEM;

          ?/*?offset?overflow??*/
          ?if?((pgoff?+?(len?>>?PAGE_SHIFT))???return?-EOVERFLOW;

          ?/*?Too?many?mappings??*/
          ?if?(mm->map_count?>?sysctl_max_map_count)????//判斷該進程的地址空間的虛擬區(qū)間數(shù)量是否超過了限制
          ??return?-ENOMEM;

          ?//get_unmapped_area從當(dāng)前進程的用戶空間獲取一個未被映射區(qū)間的起始地址
          ?addr?=?get_unmapped_area(file,?addr,?len,?pgoff,?flags);
          ?if?(offset_in_page(addr))????????//檢查addr是否有效
          ??return?addr;

          ?if?(prot?==?PROT_EXEC)?{
          ??pkey?=?execute_only_pkey(mm);
          ??if?(pkey?0)
          ???pkey?=?0;
          ?}

          ?/*?Do?simple?checking?here?so?the?lower-level?routines?won't?have
          ??*?to.?we?assume?access?permissions?have?been?handled?by?the?open
          ??*?of?the?memory?object,?so?we?don't?do?any?here.
          ??*/

          ?vm_flags?|=?calc_vm_prot_bits(prot,?pkey)?|?calc_vm_flag_bits(flags)?|
          ???mm->def_flags?|?VM_MAYREAD?|?VM_MAYWRITE?|?VM_MAYEXEC;
          ?//假如flags設(shè)置MAP_LOCKED,即類似于mlock()將申請的地址空間鎖定在內(nèi)存中,?檢查是否可以進行l(wèi)ock
          ?if?(flags?&?MAP_LOCKED)
          ??if?(!can_do_mlock())
          ???return?-EPERM;

          ?if?(mlock_future_check(mm,?vm_flags,?len))
          ??return?-EAGAIN;

          ?if?(file)?{??????????//?file指針不為nullptr,?即從文件到虛擬空間的映射
          ??struct?inode?*inode?=?file_inode(file);???//獲取文件的inode

          ??switch?(flags?&?MAP_TYPE)?{???????//根據(jù)標(biāo)志指定的map種類,把為文件設(shè)置的訪問權(quán)考慮進去
          ??case?MAP_SHARED:
          ???if?((prot&PROT_WRITE)?&&?!(file->f_mode&FMODE_WRITE))
          ????return?-EACCES;

          ???/*
          ????*?Make?sure?we?don't?allow?writing?to?an?append-only
          ????*?file..
          ????*/

          ???if?(IS_APPEND(inode)?&&?(file->f_mode?&?FMODE_WRITE))
          ????return?-EACCES;

          ???/*
          ????*?Make?sure?there?are?no?mandatory?locks?on?the?file.
          ????*/

          ???if?(locks_verify_locked(file))
          ????return?-EAGAIN;

          ???vm_flags?|=?VM_SHARED?|?VM_MAYSHARE;
          ???if?(!(file->f_mode?&?FMODE_WRITE))
          ????vm_flags?&=?~(VM_MAYWRITE?|?VM_SHARED);

          ???/*?fall?through?*/
          ??case?MAP_PRIVATE:
          ???if?(!(file->f_mode?&?FMODE_READ))
          ????return?-EACCES;
          ???if?(path_noexec(&file->f_path))?{
          ????if?(vm_flags?&?VM_EXEC)
          ?????return?-EPERM;
          ????vm_flags?&=?~VM_MAYEXEC;
          ???}

          ???if?(!file->f_op->mmap)
          ????return?-ENODEV;
          ???if?(vm_flags?&?(VM_GROWSDOWN|VM_GROWSUP))
          ????return?-EINVAL;
          ???break;

          ??default:
          ???return?-EINVAL;
          ??}
          ?}?else?{
          ??switch?(flags?&?MAP_TYPE)?{
          ??case?MAP_SHARED:
          ???if?(vm_flags?&?(VM_GROWSDOWN|VM_GROWSUP))
          ????return?-EINVAL;
          ???/*
          ????*?Ignore?pgoff.
          ????*/

          ???pgoff?=?0;
          ???vm_flags?|=?VM_SHARED?|?VM_MAYSHARE;
          ???break;
          ??case?MAP_PRIVATE:
          ???/*
          ????*?Set?pgoff?according?to?addr?for?anon_vma.
          ????*/

          ???pgoff?=?addr?>>?PAGE_SHIFT;
          ???break;
          ??default:
          ???return?-EINVAL;
          ??}
          ?}

          ?/*
          ??*?Set?'VM_NORESERVE'?if?we?should?not?account?for?the
          ??*?memory?use?of?this?mapping.
          ??*/

          ?if?(flags?&?MAP_NORESERVE)?{
          ??/*?We?honor?MAP_NORESERVE?if?allowed?to?overcommit?*/
          ??if?(sysctl_overcommit_memory?!=?OVERCOMMIT_NEVER)
          ???vm_flags?|=?VM_NORESERVE;

          ??/*?hugetlb?applies?strict?overcommit?unless?MAP_NORESERVE?*/
          ??if?(file?&&?is_file_hugepages(file))
          ???vm_flags?|=?VM_NORESERVE;
          ?}
          ?//一頓檢查和配置,調(diào)用核心的代碼mmap_region
          ?addr?=?mmap_region(file,?addr,?len,?vm_flags,?pgoff);
          ?if?(!IS_ERR_VALUE(addr)?&&
          ?????((vm_flags?&?VM_LOCKED)?||
          ??????(flags?&?(MAP_POPULATE?|?MAP_NONBLOCK))?==?MAP_POPULATE))
          ??*populate?=?len;
          ?return?addr;
          }

          do_mmap()?根據(jù)用戶傳入的參數(shù)做了一系列的檢查,然后根據(jù)參數(shù)初始化?vm_area_struct?的標(biāo)志?vm_flags,vma->vm_file = get_file(file)?建立文件與vma的映射,?mmap_region()?負(fù)責(zé)創(chuàng)建虛擬內(nèi)存區(qū)域:

          unsigned?long?mmap_region(struct?file?*file,?unsigned?long?addr,
          ??unsigned?long?len,?vm_flags_t?vm_flags,?unsigned?long?pgoff)
          {
          ?struct?mm_struct?*mm?=?current->mm;?????//獲取該進程的memory?descriptor
          ?struct?vm_area_struct?*vma,?*prev;
          ?int?error;
          ?struct?rb_node?**rb_link,?*rb_parent;
          ?unsigned?long?charged?=?0;

          ?/*?檢查申請的虛擬內(nèi)存空間是否超過了限制?*/
          ?if?(!may_expand_vm(mm,?vm_flags,?len?>>?PAGE_SHIFT))?{
          ??unsigned?long?nr_pages;

          ??/*
          ???*?MAP_FIXED?may?remove?pages?of?mappings?that?intersects?with
          ???*?requested?mapping.?Account?for?the?pages?it?would?unmap.
          ???*/

          ??nr_pages?=?count_vma_pages_range(mm,?addr,?addr?+?len);

          ??if?(!may_expand_vm(mm,?vm_flags,
          ?????(len?>>?PAGE_SHIFT)?-?nr_pages))
          ???return?-ENOMEM;
          ?}

          ?/*?檢查[addr,?addr+len)的區(qū)間是否存在映射空間,假如存在重合的映射空間需要munmap??*/
          ?while?(find_vma_links(mm,?addr,?addr?+?len,?&prev,?&rb_link,
          ?????????&rb_parent))?{
          ??if?(do_munmap(mm,?addr,?len))
          ???return?-ENOMEM;
          ?}

          ?/*
          ??*?Private?writable?mapping:?check?memory?availability
          ??*/

          ?if?(accountable_mapping(file,?vm_flags))?{
          ??charged?=?len?>>?PAGE_SHIFT;
          ??if?(security_vm_enough_memory_mm(mm,?charged))
          ???return?-ENOMEM;
          ??vm_flags?|=?VM_ACCOUNT;
          ?}

          ?//檢查是否可以合并[addr,?addr+len)區(qū)間內(nèi)的虛擬地址空間vma
          ?vma?=?vma_merge(mm,?prev,?addr,?addr?+?len,?vm_flags,
          ???NULL,?file,?pgoff,?NULL,?NULL_VM_UFFD_CTX);
          ?if?(vma)???????//假如合并成功,即使用合并后的vma,?并跳轉(zhuǎn)至out
          ??goto?out;
          ?//如果不能和已有的虛擬內(nèi)存區(qū)域合并,通過Memory?Descriptor來申請一個vma
          ?vma?=?kmem_cache_zalloc(vm_area_cachep,?GFP_KERNEL);
          ?if?(!vma)?{
          ??error?=?-ENOMEM;
          ??goto?unacct_error;
          ?}
          ?//初始化vma
          ?vma->vm_mm?=?mm;
          ?vma->vm_start?=?addr;
          ?vma->vm_end?=?addr?+?len;
          ?vma->vm_flags?=?vm_flags;
          ?vma->vm_page_prot?=?vm_get_page_prot(vm_flags);
          ?vma->vm_pgoff?=?pgoff;
          ?INIT_LIST_HEAD(&vma->anon_vma_chain);

          ?if?(file)?{?//假如指定了文件映射?
          ??if?(vm_flags?&?VM_DENYWRITE)?{???//映射的文件不允許寫入,調(diào)用deny_write_accsess(file)排斥常規(guī)的文件操作
          ???error?=?deny_write_access(file);
          ???if?(error)
          ????goto?free_vma;
          ??}
          ??if?(vm_flags?&?VM_SHARED)?{???//映射的文件允許其他進程可見,?標(biāo)記文件為可寫
          ???error?=?mapping_map_writable(file->f_mapping);
          ???if?(error)
          ????goto?allow_write_and_free_vma;
          ??}

          ??//遞增File的引用次數(shù),返回File賦給vma
          ??vma->vm_file?=?get_file(file);
          ??error?=?file->f_op->mmap(file,?vma);??//調(diào)用文件系統(tǒng)指定的mmap函數(shù)
          ??if?(error)
          ???goto?unmap_and_free_vma;

          ??/*?Can?addr?have?changed??
          ???*
          ???*?Answer:?Yes,?several?device?drivers?can?do?it?in?their
          ???*?????????f_op->mmap?method.?-DaveM
          ???*?Bug:?If?addr?is?changed,?prev,?rb_link,?rb_parent?should
          ???*??????be?updated?for?vma_link()
          ???*/

          ??WARN_ON_ONCE(addr?!=?vma->vm_start);

          ??addr?=?vma->vm_start;
          ??vm_flags?=?vma->vm_flags;
          ?}?else?if?(vm_flags?&?VM_SHARED)?{
          ??error?=?shmem_zero_setup(vma);??//假如標(biāo)志為VM_SHARED,但沒有指定映射文件,需要調(diào)用shmem_zero_setup(),實際映射的文件是dev/zero
          ??if?(error)
          ???goto?free_vma;
          ?}
          ?//將申請的新vma加入mm中的vma鏈表
          ?vma_link(mm,?vma,?prev,?rb_link,?rb_parent);
          ?/*?Once?vma?denies?write,?undo?our?temporary?denial?count?*/
          ?if?(file)?{
          ??if?(vm_flags?&?VM_SHARED)
          ???mapping_unmap_writable(file->f_mapping);
          ??if?(vm_flags?&?VM_DENYWRITE)
          ???allow_write_access(file);
          ?}
          ?file?=?vma->vm_file;
          out:
          ?perf_event_mmap(vma);
          ?//更新進程的虛擬地址空間mm
          ?vm_stat_account(mm,?vm_flags,?len?>>?PAGE_SHIFT);
          ?if?(vm_flags?&?VM_LOCKED)?{
          ??if?(!((vm_flags?&?VM_SPECIAL)?||?is_vm_hugetlb_page(vma)?||
          ?????vma?==?get_gate_vma(current->mm)))
          ???mm->locked_vm?+=?(len?>>?PAGE_SHIFT);
          ??else
          ???vma->vm_flags?&=?VM_LOCKED_CLEAR_MASK;
          ?}

          ?if?(file)
          ??uprobe_mmap(vma);

          ?/*
          ??*?New?(or?expanded)?vma?always?get?soft?dirty?status.
          ??*?Otherwise?user-space?soft-dirty?page?tracker?won't
          ??*?be?able?to?distinguish?situation?when?vma?area?unmapped,
          ??*?then?new?mapped?in-place?(which?must?be?aimed?as
          ??*?a?completely?new?data?area).
          ??*/

          ?vma->vm_flags?|=?VM_SOFTDIRTY;

          ?vma_set_page_prot(vma);

          ?return?addr;

          unmap_and_free_vma:
          ?vma->vm_file?=?NULL;
          ?fput(file);

          ?/*?Undo?any?partial?mapping?done?by?a?device?driver.?*/
          ?unmap_region(mm,?vma,?prev,?vma->vm_start,?vma->vm_end);
          ?charged?=?0;
          ?if?(vm_flags?&?VM_SHARED)
          ??mapping_unmap_writable(file->f_mapping);
          allow_write_and_free_vma:
          ?if?(vm_flags?&?VM_DENYWRITE)
          ??allow_write_access(file);
          free_vma:
          ?kmem_cache_free(vm_area_cachep,?vma);
          unacct_error:
          ?if?(charged)
          ??vm_unacct_memory(charged);
          ?return?error;
          }

          mmap_region()?調(diào)用了?call_mmap(file, vma),call_mmap?根據(jù)文件系統(tǒng)的類型選擇適配的?mmap()?函數(shù),我們選擇目前常用的ext4,ext4_file_mmap()?是ext4對應(yīng)的mmap, 功能非常簡單,更新了file的修改時間(file_accessed(flie)),將對應(yīng)的operation賦給?vma->vm_flags,后面的文件系統(tǒng)章節(jié)在學(xué)習(xí)這塊。

          通過分析mmap的源碼我們發(fā)現(xiàn)在調(diào)用?mmap()?的時候僅僅申請一個?vm_area_struct?來建立文件與虛擬內(nèi)存的映射,并沒有建立虛擬內(nèi)存與物理內(nèi)存的映射。假如沒有設(shè)置?MAP_POPULATE?標(biāo)志位,Linux并不在調(diào)用?mmap()?時就為進程分配物理內(nèi)存空間,直到下次真正訪問地址空間時發(fā)現(xiàn)數(shù)據(jù)不存在于物理內(nèi)存空間時,觸發(fā)?Page Fault?即缺頁中斷,Linux才會將缺失的Page換入內(nèi)存空間。其代碼流程圖如下所示

          3. 應(yīng)用場景

          對于傳統(tǒng)的linux系統(tǒng)文件操作是如何的呢?首選我們來看看工作流是如何的,其流程如下圖所示

          其特點為

          • 使用頁緩存機制,提高讀寫效率和保護磁盤
          • 讀文件時,先將文件從磁盤拷貝到緩存,由于頁緩存區(qū)是在內(nèi)核空間,不能被用戶空間直接訪問,所以需要將頁緩存區(qū)數(shù)據(jù)再次拷貝到用戶空間,有2次文件拷貝工作

          下面來看看使用內(nèi)存映射文件讀/寫的流程,其流程圖如下圖所示

          其特點為:

          • 用戶空間與內(nèi)核空間的交互式通過映射的區(qū)域直接交互,用內(nèi)存的讀取代替I/O讀寫,文件讀寫效率高
          • 數(shù)據(jù)拷貝次數(shù)少,對文件的讀取操作跨過頁緩存,減少了數(shù)據(jù)拷貝一次,效率提高
          • 可實現(xiàn)高效的大規(guī)模數(shù)據(jù)傳輸

          在Linux系統(tǒng)中,根據(jù)內(nèi)存映射的本質(zhì)和特點,其應(yīng)用場景在于

          • 1.實現(xiàn)內(nèi)存共享,如跨進程通信

          • 2.提高數(shù)據(jù)讀/寫效率:如讀寫操作

          對于進程間的通信,其工作流程如下圖所示

          • 創(chuàng)建一塊共享的接收區(qū),實現(xiàn)地址映射關(guān)系

          • 發(fā)送進程數(shù)據(jù)到自身的虛擬內(nèi)存區(qū)域,數(shù)據(jù)拷貝1次

          • 由于發(fā)送進程的虛擬地址空間與接收進程的虛擬內(nèi)存地址存在映射關(guān)系,所以發(fā)送到的數(shù)據(jù)也存放到接收進程的虛擬內(nèi)存中,即實現(xiàn)了跨進程間通信

          4. 總結(jié)

          內(nèi)存映射的讀寫操作主要的過程如下:

          • 創(chuàng)建虛擬映射區(qū)域,其在當(dāng)前進程的虛擬地址空間中,尋找一段滿足大小要求的虛擬地址,并且為此虛擬地址分配一個虛擬內(nèi)存區(qū)域(vm_area_struct結(jié)構(gòu)),初始化該虛擬內(nèi)存區(qū)域,插入到進程虛擬地址區(qū)域的鏈表和紅黑樹中
          • 實現(xiàn)地址映射關(guān)系,建立頁表,該過程在mmap函數(shù)中并未實現(xiàn),此時只是創(chuàng)建了映射關(guān)系,并不將任何文件數(shù)據(jù)拷貝至主存中,真正的數(shù)據(jù)拷貝是通過進程發(fā)起讀寫操作時
          • 進程訪問該映射空間,實現(xiàn)文件內(nèi)容到物理內(nèi)存的數(shù)據(jù)拷貝,當(dāng)進程讀寫訪問該映射地址時,如果進程寫操作改變了內(nèi)容,并不會立即更新,而是一定時間后系統(tǒng)會自動會寫臟數(shù)據(jù)到對應(yīng)硬盤的地址空間

          使用mmap來創(chuàng)建文件映射,由于只建立了進程地址空間VMA,并沒有馬上分配page cache和建立映射關(guān)系。那么就會導(dǎo)致一個問題,當(dāng)創(chuàng)建一個很大的VMA,會頻繁發(fā)生缺頁中斷。

          內(nèi)存映射機制mmap是POSIX標(biāo)準(zhǔn)的系統(tǒng)調(diào)用,有匿名映射和文件映射兩種。

          • 匿名映射使用進程的虛擬內(nèi)存空間,它和malloc(3)類似,實際上有些malloc實現(xiàn)會使用mmap匿名映射分配內(nèi)存,不過匿名映射不是POSIX標(biāo)準(zhǔn)中規(guī)定的。
          • 文件映射有MAP_PRIVATE和MAP_SHARED兩種。前者使用COW的方式,把文件映射到當(dāng)前的進程空間,修改操作不會改動源文件。后者直接把文件映射到當(dāng)前的進程空間,所有的修改會直接反應(yīng)到文件的page cache,然后由內(nèi)核自動同步到映射文件上。

          相比于IO函數(shù)調(diào)用,基于文件的mmap的一大優(yōu)點是把文件映射到進程的地址空間,避免了數(shù)據(jù)從用戶緩沖區(qū)到內(nèi)核page cache緩沖區(qū)的復(fù)制過程;當(dāng)然還有一個優(yōu)點就是不需要頻繁的read/write系統(tǒng)調(diào)用。

          原文:?https://blog.csdn.net/u012489236/article/details/109709724

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  五月丁香夫妻自拍偷拍 | www.色播 | 日皮太爽了我要看免费视频 | 爱爱打炮网 | 欧美熟妇视频在线 |