Linux驅動開發(六) 記憶體使用

2021-07-25 15:28:41 字數 4588 閱讀 1490

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類似,還有一對ioremapiounmap函式,用於對映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...