1. 程式人生 > >《Linux Device Drivers》第八章 分配內存——note

《Linux Device Drivers》第八章 分配內存——note

用戶 代碼 troy 避免 又一 span 使用 star 下使用

本章主要介紹Linux內核的內存管理。
  • kmalloc函數的內幕
    • 不正確所獲取的內存空間清零
    • 分配的區域在物理內存中也是連續的
    • flags參數
      • <linux/slab.h>
      • <linux/gfp.h>
        • GFP_KERNEL
          • 在空暇內存較少時把當前進程轉入休眠以等待一個頁面
          • 分配內存的函數必須是可重入的
        • GFP_ATOMIC
          • 用於在中斷處理例程或其它執行於進程上下文之外的代碼中分配內存,不會休眠
        • GFP_USER
          • 用於為用戶空間頁分配內存。可能會休眠
        • GFP_HIGHUSER
          • 類似於GFP_USER,只是假設有高端內存的話就從那裏分配
        • GFP_NOIO, GFP_NOFS
          • 這兩個標誌的功能類似於GFP_KERNEL,可是為內核分配內存的工作方式加入了一些限制。具有GFP_NOFS標誌的分配不同意運行不論什麽文件系統調用。而GFP_NOIO禁止不論什麽I/O的初始化。

            這兩個標誌主要在文件系統和虛擬內存代碼中使用,這些代碼中的內存分配可休眠。但不應該發生遞歸的文件系統調用

        • __GFP_DMA
          • 該標誌請求分配發生在可進行DMA的內存區段中
        • __GFP_HIGHHEM
          • 這個標誌表明要分配的內存可位於高端內存
        • __GFP_COLD
          • 這個標誌請求尚未使用的“冷”頁面,對於DMA讀取的頁面分配。可使用這個標誌
        • __GFP_NOWARN
          • 非常少使用。能夠避免內核在無法滿足分配請求時產生警告
        • __GFP_HIGH
          • 標記了一個高優先級的請求,它同意為緊急善而消耗由內核保留的最後一些頁面
        • __GFP_REPEAT, __GFP_NOFAIL, __GFP_NORETRY
          • 告訴分配器在滿足分配請求而遇到困難時應該採取何種行為
          • __GFP_REPEAT表示“努力再嘗試一次”,它會又一次嘗試分配。但仍有可能失效
          • __GFP_NOFAIL標誌告訴分配器始終不返回失敗。它會努力滿足分配請求,不鼓舞使用這個標誌
          • __GFP_NORETRY告訴分配器,假設所請求的內存不可獲得,就馬上返回
    • 內存區段
      • __GFP_DMA和__GFP_HIGHHEM的使用與平臺相關
      • Linux內核把內存分為三個區段:可用於DMA的內存、常規內存以及高端內存
      • 可用於DMA的內存指存在於特別地址範圍內的內存,外設能夠利用這些內存運行DMA訪問
      • 高端內存是32位平臺為了訪問大量的內存而存在的一種機制
      • 假設沒有指定特定的標誌。則kmalloc會在常規區段和DMA區段搜索
      • 假設設置了__GFP_HIGHHEM標誌。則全部三個區段都會被搜索
      • 內存區段的背後機制在mm/page_alloc.c中實現
    • size參數
      • Linux處理內存分配的方法是。創建一系列的內存對象池,每一個池中的內存塊大小是固定一致的。

        處理分配請求時,就直接在包括有足夠大的內存塊的池中傳遞一個整塊給請求者

      • kmalloc能處理的最小的內存塊是32或者64
      • 假設希望代碼具有完整的可移植性。則不應該分配大於128KB的內存

  • 後備快速緩存
    • Linux內核的調整緩存管理有時稱為“slab分配器”
    • slag分配器實現的快速緩存具有kmem_cache_t類型
    • kmem_cache_t *kem_cache_create(const char *name, size_t size, size_t offset, unsigned long flags, void (*constructor) (void *, keme_cache_t *, unsigned long flags), void (*destructor) (void *, kmem_cache_t *, unsigned long flags));
    • 參數flags控制怎樣完畢分配
      • SLAB_NO_REAP
        • 能夠保護快速緩存在系統尋找內存的時候不會被降低
      • SLAB_HWCACHE_ALIGN
        • 要求全部數據對象跟調整緩存行(cache line)對齊。實際的操作則依賴於主要平臺的硬件調整緩存布局
      • SLAB_CACHE_DMA
        • 要求每一個數據對象都從可用於DMA的內存區段中分配
    • mm/slab.c
    • 能夠使用同一個函數同一時候作為constructor和destructor使用,當調用的是一個constructor函數的時候,slab分配器總是傳遞SLAB_CTOR_CONSTRUCTOR標誌
    • void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
    • void kmem_cache_free(kmem_cache_t *cache, const void *obj);
    • int kmem_cache_destroy(kmem_cache_t *cache);
    • 快速緩存的使用統計情況能夠從/proc/slabinfo獲得
    • 內存池
      • 內存池事實上就是某種形式的後備快速緩存,它試圖始終保存空暇的內存,以便把在緊急狀態下使用
      • 內存池對象的類型為mempool_t
      • <linux/mempool.h>
        • mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data);
        • typedef void *(mempool_alloc_t) (int gfp_mask, void *pool_data);
        • typedef void (mempool_free_t) (void *element, void *pool_data);
        • void *mempool_alloc(mempool_t *pool, int gfp_mask);
        • void mempool_free(void *element, mempool_t *pool);
        • int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
        • void mempool_destroy(mempool_t *pool);
      • example
        • cache = kmem_cache_create(…);
        • pool =- mempool_create(MY_POOL_MINIMUM, mempool_alloc_slab, mempool_free_slab, cache);
      • mempool會分配一些內存塊,空暇且不會真正得到使用
      • 應盡量避免在驅動程序代碼中使用mempool
  • get_free_page和相關函數
    • get_zeroed_page(unsigned int flags);
      • 返回指向新頁面的指針並將頁面清零
    • __get_free_page(unsigned int flags);
      • 類似於get_zeroed_page,但不清零頁面
    • __get_free_pages(unsigned int flags, unsigned int order);
      • 分配若幹(物理連續的)頁面,並返回指向該內在區域第一個字節的指針。但不清零頁面
      • 參數order是要申請或釋放的頁面數的以2為底的對數
    • void free_page(unsigned long addr);
    • void free_pages(unsigned long addr, unsigned long order);
    • alloc_pages接口
      • struct page *alloc_pages_node(int nid, unsigned int flags, unsigned int order);
        • nid是NUMA節點的ID號
      • struct page *alloc_page(unsigned int flags, unsigned int order);
      • struct page *alloc_page(unsigned int flags);
      • void __free_page(struct page *page);
      • void __free_pages(struct page *page, unsigned int order);
      • void free_hot_page(struct page *page);
      • void free_code_page(struct page *page);
    • Subtopic 7
  • vmalloc及其輔助函數
    • 分配虛擬地址空間的連續區域,這段區域右物理上可能是不連續的,內核卻覺得它們在地址上是連續的
    • vmalloc獲得的內存使用起來效率不高
    • <linux/vmalloc.h>
      • void *vmalloc(unsigned long size);
      • void vfree(void *addr);
      • void *ioremap(unsigned long offset, unsigned long size);
      • void iounmap(void *addr);
    • vmalloc能夠獲得的地址在VMALLOC_START到VMALLOC_END的範圍中。這兩個符號都在<asm/pgtable.h>中定義
    • 使用vmalloc函數的正確場合是在分配一大塊連續的、僅僅在軟件中存在的、用於緩沖的內存區域的時候
    • ioremap很多其它用於映射(物理的)PCI緩沖區地址到(虛擬的)內核空間
  • per-CPU變量
    • 當建立一個per-CPU變量時。系統中的每一個處理器都會擁有該變量的特有副本
    • 不須要鎖定
    • 能夠保存在相應處理器的快速緩存中
    • <linux/percpu.h>
      • DEFINE_PER_CPU(type, name);
      • get_cpu_var(variable);
      • put_cpu_var(variable);
      • per_cpu(variable, int cpu_id);
      • void *alloc_percpu(type);
      • void *__alloc_percpu(size_t size, size_t align);
      • per_cpu_ptr(void *per_cpu_var, int cpu_id);
      • EXPORT_PER_CPU_SYMBOL(per_cpu_var);
      • EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);
      • DECLARE_PER_CPU(type, name);
  • 獲取大的緩沖區
    • 在引導時獲得專用緩沖區
      • <linux/bootmem.h>
        • void *alloc_bootmem(unsigned long size);
        • void *alloc_bootmem_low(unsigned long size);
        • void *alloc_bootmem_pages(unsigned long size);
        • void *alloc_bootmem_low_pages(unsigned long size);
        • void free_bootmem(unsigned long addr, unsigned long size);
      • 這些函數要麽分配整個頁,要麽分配不在頁面邊界上對齊的內存區
      • 除非使用具有_low後綴的版本號,否則分配的內存可能會是高端內存

《Linux Device Drivers》第八章 分配內存——note