<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【轉】內核中的內存申請:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages

      轉自:http://www.rzrgm.cn/yfz0/p/5829443.html

      在內核模塊中申請分配內存需要使用內核中的專用API:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages;當然,設備驅動程序也不例外;
      對于提供了MMU功能的處理器而言,Linux提供了復雜的內存管理系統,使得進程所能訪問到的地址空間可以達到4GB;而這4GB的空間又被劃分為兩個部分:0GB~3GB(PAGE_OFFSET,x86中的值是0xC0000000)的區域被用作進程的用戶空間,3GB~4GB的區域被用作內核空間;
      在內核空間中,從3GB到vmalloc_start之間的這段地址區域作為物理內存映射區使用,該段映射區域內包含了內核鏡像、物理頁框表mem_map等等,比如,我們使用的系統物理內存為160MB,那么,3GB~3GB+vmalloc_start之間的區域就應該是映射的物理內存;在物理內存映射區域之后,就是虛擬內存vmalloc區域;對于160MB的系統而言,vmalloc_start的位置就應該在3GB+160MB位置附近(在物理內存映射區與vmalloc_start位置之間還存在一個8M的gap來防止越界),vmalloc_end的位置接近4GB的位置(系統會在最后的位置處保留一片128KB大小的區域專用于頁面映射);
      一、kmalloc
      #include <linux/slab.h>
      static inline void *kmalloc(size_t size, gfp_t flags);
      參數:size:指定要分配的塊的大小,單位是字節;flags:指定分配內存時的控制方式;
      該函數用于在內核空間中分配內存使用,它的返回速度快(除非被阻塞),并且對其分配的內存不進行任何初始化(清零)操作,分配的內存區域仍然保留有他原有的內容;
      kmalloc申請得到的是物理內存,位于物理內存映射區,而且在物理地址上是連續的;但是kmalloc返回的內存地址卻是虛擬地址(線性地址),返回的這個虛擬地址(線性地址)與真實的物理地址之間僅僅相差一個固定的偏移值;因此,kmalloc申請得到的物理內存塊的首地址與其返回的虛擬地址之間存在著比較簡單的轉換關系;通過內核提供的函數virt_to_phys()可以實現該虛擬地址到真實的內核物理地址之間的轉換:
      #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
      static inline unsigned long virt_to_phys(volatile void* address)
      {
       return __pa(address);
      }
      參數address是kmalloc返回的一個虛擬地址;該轉換過程就是虛擬地址減去3GB(PAGE_OFFSET=0xC0000000);
      一般情況下,PAGE_OFFSET=3*1024*1024*1024=0xC0000000(3G);
      與之對應的函數就是phys_to_virt()用于把內核物理地址轉換為虛擬地址:
      #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
      static inline void * phys_to_virt(unsigned long address)
      {
       return __va(address);
      }
      這兩個函數都定義在include/asm-i386/io.h中;
      kmalloc()函數用于小塊內存的申請,最小可以申請的內存是32字節或64字節,最大可以申請的內存是128KB-16,其中,被減掉的16個字節用于存儲頁描述符結構;這些都依賴于體系架構所使用的頁面大小;kmalloc申請的內存在物理地址上是連續的,這對于要進行DMA傳輸的設備來說,是非常重要的;
      kmalloc()的內存分配是基于slab機制實現的,slab機制是為分配小內存而提供的一種高效的機制;但是slab機制也不是獨立的,它本身也是在頁分配器的基礎上來劃分更細粒度的內存供調用者使用;也就是說,系統先使用頁分配器分配以頁為最小單位的連續物理地址,然后,kmalloc()再在這個基礎上根據調用者的需要進行切分的;另外,slab機制分配的內存在物理地址和虛擬地址(線性地址/邏輯地址)上都是連續的;
      對于kmalloc()申請的內存,需要使用kfree()函數來釋放;
      備注:kmalloc是基于slab機制實現的;
      二、get_free_pages
      #include <asm/pages.h>
      fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
      {
       struct page * page;
       page = alloc_pages(gfp_mask, order);
       if (!page)
        return 0;
       return (unsigned long) page_address(page);
      }
      參數gfp_mask用于指定申請內存時的控制方式,order用于指定申請的頁數;它申請的內存位于(PAGE_OFFSET,HIGH_MEMORY)之間;
      __get_free_pages()函數是頁面分配器提供給調用者的最底層的內存分配函數,它申請的內存也是連續的物理內存,同樣位于物理內存映射區;它是基于buddy機制實現的;在使用buddy機制實現的物理內存管理系統中,最小的分配粒度(單位)也是以頁為單位的;在__get_free_pages()內部通過調用alloc_pages()來分配物理內存頁;
      __get_free_page()函數分配的是連續的物理內存,處理的是連續的物理地址,但是返回的也是虛擬地址(線性地址);如果想要得到正確的物理地址,也需要使用virt_to_phys()可進行轉換;
      對于__get_free_pages()函數申請的內存,需要使用__free_pages()函數來釋放;
      備注:__get_free_pages是基于buddy機制實現的;
      三、vmalloc
      #include <linux/vmalloc.h>
      void* vmalloc(unsigned long size)
      {
       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
      }
      void* __vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
      {
       return __vmalloc_node(size, gfp_mask, prot, -1);
      }
      void* __vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, int node)
      {
       struct vm_struct *area;
       size = PAGE_ALIGN(size);
       if(!size || (size >> PAGE_SHIFT) > num_physpages)
         return NULL;
       area = get_vm_area_node(size, VM_ALLOC, node);
       if(!area)
         return NULL;
       return __vmalloc_area_node(area, gfp_mask, prot, node);
      }
      void* __vmalloc_area_node(struct vm_struct* area, gfp_t gfp_mask, pgprot_t prot, int node);
      void* __vmalloc_area(struct vm_struct* area, gfp_t gfp_mask, pgprot_t prot)
      {
       return __vmalloc_area_node(area, gfp_mask, prot, -1);
      }
      vmalloc()函數也是用于申請內存的,但是它申請的內存是位于vmalloc_start到vmalloc_end之間的虛擬內存;它申請的內存在虛擬地址(線性地址/邏輯地址)上是連續的,但是并不要求在物理地址上連續,并且返回的地址與物理地址之間沒有簡單的轉換關系;
      vmalloc()函數適用于大塊內存的申請環境中;但是它申請的內存不能直接用于DMA傳輸;因為DMA傳輸需要使用物理地址連續的內存塊;
      對于vmalloc()申請的內存,需要使用vfree()函數來釋放;
      備注:vmalloc是基于slab機制實現的;
      四、比較
      1).kmalloc/__get_free_pages申請的內存塊都在物理內存映射區,即在(PAGE_OFFSET,HIGH_MEMORY)之間,處理的都是物理地址,且保證在物理地址空間上是連續的;二者返回的都是虛擬地址,如果需要得到正確的物理地址,需要使用virt_to_phys()進行轉換;但是,kmalloc和vmalloc都是以字節為單位進行申請,而__get_free_pages()則是以頁為單位進行申請;
      2).vmalloc函數申請的內存塊位于虛擬內存映射區,即在(VMALLOC_START,VMALLOC_END)之間,處理的都是虛擬內存,且保證在虛擬地址空間上是連續的,但是在物理地址空間上不要求連續;一般作為交換區、模塊的內存使用;
      3).kmalloc和vmalloc都是基于slab機制實現的,但是kmalloc的速度比vmalloc的速度快;__get_free_pages是基于buddy機制實現的,速度也較快;
      4).kmalloc用于小塊內存的申請,通常,一次所能申請的內存塊的大小在(32/64字節,128KB-16)之間;而vmalloc可以用于分配大塊內存的場合;
      5).kmalloc申請的內存塊在物理地址空間上是連續的,所以它申請的內存塊可以直接用于DMA傳輸;vmalloc申請的內存塊在虛擬地址空間上連續,但是在物理地址空間上不要求連續,所以它申請的內存塊不能直接用于DMA傳輸;
      6).kmalloc申請的內存塊用kfree釋放;vmalloc申請的內存塊用vfree釋放;__get_free_pages申請的內存頁用__free_pages釋放;
      7).kmalloc申請得到的地址稱為內核邏輯地址,vmalloc申請得到的地址稱為內核虛擬地址;
      五、其它函數
      1).static inline void *kzalloc(size_t size, gfp_t flags);
       該函數比kmalloc多了一個功能,就是會把申請得到的內存塊初始化為0;
      2).static inline void* kcalloc(size_t n, size_t size, gfp_t flags)
         {
           if(n != 0 && size > ULONG_MAX / n)
              return NULL;
           return kzalloc(n * size, flags);
         }
         該函數用于申請一個數組的內存空間,并把申請得到的內存都初始化為0;
      六、GFP標記
      kmalloc、kzalloc、kcalloc、vmalloc、get_free_pages函數在調用時都有一個gfp_t類型的控制標記flags;這個標記用于控制申請內存時的內存分配控制方式; #include <linux/gfp.h>
      GFP的標記有兩種:帶雙下劃線前綴的和不帶雙下劃線前綴的;
      不帶雙下劃線前綴的GFP標志:
      GFP_ATOMIC:用于在中斷上下文和進程上下文之外的其它代碼中分配內存;從不睡眠;
      GFP_KERNEL:內核正常分配內存;可能睡眠;
      GFP_USER  :用于為用戶空間頁分配內存;可能睡眠;
      GFP_HIGHUSER:如同GFP_USER,但它是從高端內存中申請;
      GFP_NOIO和GFP_NOFS:功能如同GFP_KERNEL,但是它倆增加限制到內核能做的來滿足請求;GFP_NOFS分配不允許進行任何文件系統調用,而GFP_NOIO分配根本不允許進行任何IO初始化;它倆主要用于文件系統和虛擬內存代碼,那里允許一個分配睡眠,但是遞歸的文件系統調用會是個壞主意;
      帶有雙下劃線前綴的GFP標志:
      __GFP_DMA:這個標志要求分配的內存在能夠進行DMA的內存區;平臺依賴的;
      __GFP_HIGHMEM:這個標志指示分配的內存可以位于高端內存區;平臺依賴的;
      __GFP_COLD:正常地,內存分配器盡力返回"緩沖熱"的頁---可能在處理器緩沖中找到的頁;相反,這個標志請求一個"冷"頁---在一段時間內沒被使用的頁;它對分配頁做DMA讀是很有用的,此時在處理器緩沖中出現是沒用的;
      __GFP_NOWARN:這個標志用于分配內存時阻止內核發出警告,當一個分配請求無法滿足時;
      __GFP_HIGH:這個標志標識了一個高優先級請求,它被允許來消耗甚至被內核保留給緊急狀況的最后的內存頁;
      __GFP_REPEAT:分配器的動作;當分配器有困難滿足一個分配請求時,通過重復嘗試的方式來"盡力嘗試",但是分配操作仍然有可能失敗;
      __GFP_NOFAIL:分配器的動作;當分配器有困難滿足一個分配請求時,這個標志告訴分配器不要失敗,盡最大努力來滿足分配請求;
      __GFP_NORETRY:分配器的動作;當分配器有困難滿足一個分配請求時,這個標志告訴分配器立即放棄,不再做任何嘗試;
      通常,一個或多個帶雙下劃線前綴的標記相或,即可得到對應的不帶雙下劃線前綴的標記;
      最常用的標記就是GFP_KERNEL,它的意思就是當前的這個分配代表運行在內核空間的進程而進行的;換句話說,這意味著調用函數是代表一個進程在執行一個系統調用;使用GFP_KERNEL標記,就意味著kmalloc能夠使當前進程在少內存的情況下通過睡眠來等待一個內存頁;因此,一個使用GFP_KERNEL的函數必須是可重入的,且不能在原子上下文中運行;當當前進程睡眠,內核采取正確的動作來定位一些空閑的內存頁,或者通過刷新緩存到磁盤或者交換出去一個用戶進程的內存頁;
      如果一個內存分配動作發生在中斷處理或內核定時器的上下文中時,當前進程就不能被設置為睡眠,也就不能再使用GFP_KERNEL標志了,此時應該使用GFP_ATOMIC標志來代替;正常地,內核試圖保持一些空閑頁以便來滿足原子的分配;當使用GFP_ATOMIC標志時,kmalloc標志能夠使用甚至最后一個空閑頁;如果這最后一個空閑頁不存在,那分配就會失敗;

      posted @ 2019-09-17 09:17  菜鳥升級  閱讀(1554)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲精品国产综合久久一线| 成人免费视频一区二区三区| 熟妇人妻无码中文字幕老熟妇 | 激情国产一区二区三区四区| 成人av午夜在线观看| 国产精品视频亚洲二区| 人妻少妇精品无码专区二区| 国产区精品福利在线观看精品| 国产精品视频不卡一区二区| 国产欧美日韩精品第二区| 成人区人妻精品一区二区| 综合色一色综合久久网| 国产伦码精品一区二区| 日韩一区二区三区在线观院| 国产精品一码二码三码四码| 免费无码毛片一区二三区| 视频一区二区三区刚刚碰| 国产内射XXXXX在线| 免费观看欧美猛交视频黑人| 午夜福利电影| 国产精品特级毛片一区二区三区| 久久zyz资源站无码中文动漫| 欧美日韩另类国产| 亚洲精品tv久久久久久久久久 | 久久青青草原亚洲AV无码麻豆| 亚洲全网成人资源在线观看| 亚洲精品第一区二区在线| 极品少妇的粉嫩小泬视频| 蜜臀视频在线观看一区二区| 亚洲欧美人成电影在线观看| 国产三级精品三级在线观看| 国产国产午夜福利视频| 一本一本久久a久久精品综合| 精品无码久久久久久久久久| 免费无遮挡毛片中文字幕| 亚洲欧美日韩愉拍自拍美利坚| 精品久久久久久国产| 日韩av一区二区三区在线| 成人影片麻豆国产影片免费观看| 中文熟妇人妻av在线| 成人一区二区人妻不卡视频|