Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 設備驅動核心:內存管理與 I/O 訪問完全指南

編輯:Linux 教學

Linux 設備驅動開發中,內存分配與 I/O 訪問是兩大基石。本文系統梳理內核虛擬地址佈局、三大分配 API(kmalloc/get_free_page/vmalloc)差異、物理/虛擬地址轉換、I/O 端口與 I/O 內存訪問模型、ioremap 映射機制、以及 mmap 實現用戶態映射設備內存。

1. 內核虛擬地址空間佈局(32 位 x86)

3GB (PAGE_OFFSET = 0xC0000000)
├── 物理內存線性映射區 (3GB ~ vmalloc_start)
│   ├── 內核鏡像
│   ├── mem_map 頁框陣列
│   └── kmalloc/get_free_page 分配區(物理連續,邏輯地址 = 物理 + 3GB)
├── 8MB Gap(防越界)
├── vmalloc 區 (vmalloc_start ~ vmalloc_end)
│   └── vmalloc/vmap 分配區(虛擬連續,物理不連續)
├── 4MB 持久內核映射區
├── 固定映射區
└── 128KB 頂端保留區
4GB (0xFFFFFFFF)

2. 三大分配 API 對比

API物理連續虛擬連續地址轉換適用場景
kmalloc是是virt_to_phys() 簡單減法小塊內存(<128KB)、DMA 緩衝區
get_free_page是是同 kmalloc頁級分配、DMA 大緩衝區
vmalloc否是需遍歷頁表大塊內存(>128KB)、非 DMA

地址轉換宏(線性映射區)

#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET))

extern inline unsigned long virt_to_phys(volatile void *address) { return __pa(address); }
extern inline void *phys_to_virt(unsigned long address) { return __va(address); }

3. I/O 訪問模型

兩種硬件編址方式

  • I/O 映射:x86 獨立 I/O 地址空間,專用指令 in/out
  • 內存映射:ARM/PowerPC/RISC-V 統一地址空間,普通訪存指令

Linux 統一抽象為 I/O 內存資源,驅動開發者無需關心底層差異。

ioremap:物理地址 → 內核虛擬地址

void *ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
void iounmap(void *addr);
  • 將設備寄存器物理地址映射到 vmalloc 區
  • 映射後不可直接解引用指針,必須用訪問函數

跨平台訪問函數(以 x86 為例)

#define readb(addr)  (*(volatile u8  *)__io_virt(addr))
#define readw(addr)  (*(volatile u16 *)__io_virt(addr))
#define readl(addr)  (*(volatile u32 *)__io_virt(addr))

#define writeb(v,addr) (*(volatile u8  *)__io_virt(addr) = (v))
#define writew(v,addr) (*(volatile u16 *)__io_virt(addr) = (v))
#define writel(v,addr) (*(volatile u32 *)__io_virt(addr) = (v))

ARM 等平台實現不同,但 API 統一,保證驅動可移植性。

4. 實戰:S3C2410 RTC 驅動中的地址映射

#define ALMDAY  bRTC(0x60)
#define ALMMON  bRTC(0x64)
#define ALMYEAR bRTC(0x68)

#define bRTC(Nb)  __REG(0x57000000 + (Nb))
#define __REG(x)  io_p2v(x)
#define io_p2v(x) ((x) | 0xA0000000)   // 物理→虛擬:置位 29-31bit
#define io_v2p(x) ((x) & ~0xA0000000)  // 虛擬→物理:清位 29-31bit

這是 ARM 平台常見的「靜態映射」技巧:利用固定偏移(0xA0000000)直接計算虛擬地址,避免運行時 ioremap 開銷。但現代驅動推薦使用 ioremap + readl/writel。

5. mmap:用戶態映射設備內存

驅動實現 file_operations.mmap,調用 remap_pfn_range 或 io_remap_pfn_range 將設備物理頁映射到用戶虛擬地址空間:

static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long pfn = phys_addr >> PAGE_SHIFT;
    unsigned long size = vma->vm_end - vma->vm_start;

    if (size > dev_mem_size) return -EINVAL;

    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);  // 設備內存不可緩存
    return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
}

總結

  • 優先 kmalloc(小塊)、DMA 用 dma_alloc_coherent、大塊非 DMA 用 vmalloc
  • 線性映射區用 virt_to_phys/phys_to_virt,vmalloc 區不可用
  • I/O 訪問統一用 ioremap + readb/writeb 族,保證跨架構可移植
  • mmap 實現零拷貝用戶態訪問設備內存,需設置 pgprot_noncached
Copyright © Windows教程網 All Rights Reserved