linux 使用的是乙個虛擬記憶體系統,意味著使用者程式所使用的位址和硬體使用的實體地址是不等同的。本篇部落格首先會簡單描述相應的概念,然後介紹下記憶體訪問的相應介面,最後討論下和硬體io相關的知識。
系統對記憶體的使用是基於頁的,頁是實體地址分成的離散單元。頁的大小一般位 4096 個位元組。記憶體的位址都被分為了頁號和乙個偏移量。
處理器一般會提供乙個記憶體管理單元(mmu),mmu 提供了虛擬位址和實體地址的對映、記憶體訪問許可權保護、cache 快取控制等硬體支援。詳細的 mmu 的工作模式不在這裡詳細描述。
對於乙個linux 程序來講,4gb 的記憶體空間被分為了兩個部分:使用者空間(0~3gb/page_offset)和核心空間(3~4gb)。核心空間又被分為了物理位址對映區(896mb)、專用頁面對映區(fixaddr_start~fixaddr_top)、系統保留對映區(fixaddr_top~4gb)。
一般來講,系統記憶體對映區最大位896mb,低於這個值的記憶體擁有邏輯位址,當大於這個值的時候,核心必須對映到高階頁面對映區。一般來講,核心資料都被放置在低端記憶體中,高階記憶體更趨向於為使用者空間對映保留。
在使用者空間使用malloc()
和free()
函式進行記憶體的申請和釋放,而核心空間往往使用下面的幾個函式來進行記憶體的動態申請:
kmalloc
c
void *kmalloc(size_t size, init flags);
功能:從物理記憶體對映區域申請一塊記憶體。
引數:size
申請的塊的大小;flags
分配標誌,常用的標誌有:
gfp_kernel
在核心空間的程序中申請記憶體。
gfp_atomic
如果不存在空閒頁,直接返回。
使用kmalloc
申請的記憶體應搭配使用kfree
來釋放。
__get_free_pages()
c
//申請
get_zeroed_page(unsigned int flags);
__get_free_page(unsigned int flags);
__get_free_pages(unsigned int flags, unsigned int order);
//釋放
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned int order);
使用 gfp 等函式,申請的空間是以頁為單位的,第乙個函式返回乙個指向新頁的指標並將該頁清零,第二個函式返回乙個指向新頁的指標但不清零,第三個函式返回乙個指向多個頁的首位址,分配的頁數為2的order
次冪。
vmalloc()
c
void *vmalloc(unsigned long size);
void vfree(void *addr);
vmalloc
在虛擬記憶體空間給出一塊連續的記憶體區,這個函式不能用於原子上下文中。
和vmalloc
類似,還有一對ioremap
iounmap
函式,用於對映i/o記憶體到虛擬空間,下面會使用到這個函式。
slab
針對返回使用的同一大小的記憶體塊,核心提供了後備快取記憶體。slab 演算法就是針對上述特點設計的。
c
//建立 slab 快取
struct kmem_cache *kmen_cache_create(const char *name, size_t size,
size_t align, unsigned long flags,
void (*ctor)(void *, struct kmem_cache *, unsigned long),
void (*dtor)(void *, struct kmem_cache *, unsigned long));
//分配 slab 快取
void kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);
//釋放 slab 快取
void kmem_cache_free(struct kmem_cache *cachep, void *objp);
//收回 slab 快取
int kmem_cache_destroy(struct kmem_cache *cachep);
裝置通常會有相應的暫存器來控制相應的功能,這些暫存器可能存在在io空間,也可能存在在記憶體空間。需要注意的是不管是io埠還是io記憶體都存在邊界效應。
i/o埠的分配
c
struct resource *request_region(unsigned long first, unsigned long n,
const char *name);//申請操作的埠
void release_region(unsigned long start, unsigned long n);//釋放使用的埠
int check_region(unsigned long first, unsigned long n);//檢查埠是否可用
申請埠成功返回非null值,失敗返回null。
i/o埠的操作
c
//讀寫8位埠
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
//讀寫16位埠
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
//讀寫32位埠
unsigned inl(unsigned port);
void outl(unsigned long byte, unsigned port);
//讀寫一串位元組
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
//讀寫一串字
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
//讀寫一串長字
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
上述函式的port
型別依賴於硬體平台。
i/o記憶體的分配對映
c
struct resource *request_mem_region(unsigned long start, unsigned long len,
char *name);//分配i/o記憶體區域
void release_mem_region(unsigned long start, unsigned long len);//釋放i/o記憶體
int check_mem_region(unsigned long start, unsigned long len);//檢查是否可用
進行io記憶體的申請和釋放後,並不是意味著得到的指標可以使用,應使用上面提到的ioremap
函式來將相應的io記憶體對映到虛擬空間內。
i/o記憶體的訪問
i/o記憶體訪問使用下述函式,不再詳細描述。
c
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
有一對函式可以把i/o埠對映為i/o記憶體。
c
void *ioport_map(unsigned long port, unsigned int count);
void ioport_unmap(void *addr);
linux字元裝置驅動開發之記憶體對映
linux驅動最終都死通過配置暫存器完成,linux驅動開發需要滿足linux的驅動框架.所以存在乙個記憶體管理單元 mmu 實現虛擬位址與實體地址的對映,記憶體保護和虛擬位址快取功能.通過記憶體對映,只需要對虛擬位址進行操作,就可以實現相應的驅動開發.記憶體對映 對映函式 記憶體對映 void i...
linux驅動開發
這兩天在公司由於一直沒有太過於繁重的任務,於是便給分配了驅動開發的任務,之前一直不明白驅動開發和普通的嵌入式開發到底有什麼區別,然後有沒有經過系統的學習,於是就一直愣在那裡。不過慢慢的還是經過查資料,雖然 沒有編寫出來,但是對於系統的一些東西學習的終於有了一定的了解,感覺這個十分的重要,也為了給自己...
Linux驅動開發之使用dev dbg除錯裝置驅動
gqb666 1 最近在寫i2c下eeprom的驅動程式,但發現使用i2c new probed device函式無法列舉到裝置,於是想除錯該函式 位於driver i2c i2c core.c內 看到其中有些除錯資訊如下 cpp view plain copy i2c new probed dev...