linux核心筆記(五)高階I O操作(三)

2021-10-09 06:24:01 字數 4090 閱讀 8530

顯示卡一類的裝置有一片很大的視訊記憶體,驅動程式要將這片視訊記憶體對映到核心的位址空間,方便操作。如果使用者想要在螢幕上進行繪製操作,將要在使用者空間開闢出一片至少一樣大的記憶體,將要繪製的影象資料填充在這片記憶體空間中,然後呼叫write系統呼叫,將資料複製到核心空間的視訊記憶體中,從而進行影象繪製。

但這種複製帶來的效能損耗是不可接受的,要消除這個複製操作就需要應用程式能夠直接訪問視訊記憶體,但視訊記憶體被對映再核心中間,應用程式沒有這個訪問許可權。字元裝置驅動提供了mmap介面,可以把核心空間中的那片記憶體所對應實體地址空間再次對映到使用者空間,這樣乙個物理記憶體就有了兩份對映(兩個虛擬位址),乙個在核心空間,乙個在使用者空間。這樣就可以通過直接操作使用者空間的這片對映之後的記憶體來直接訪問物理空間,從而提公升了效率。

示例:乙個虛擬的幀緩衝裝置的驅動程式,其實現了mmap介面。

#include #include #include #include #include #include #include #define vfb_major	256

#define vfb_minor 1

#define vfb_dev_cnt 1

#define vfb_dev_name "vfbdev"

struct vfb_dev ;

static struct vfb_dev vfbdev;

static int vfb_open(struct inode * inode, struct file * filp)

static int vfb_release(struct inode *inode, struct file *filp)

static int vfb_mmap(struct file *filp, struct vm_area_struct *vma)

ssize_t vfb_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)

static struct file_operations vfb_fops = ;

static int __init vfb_init(void)

static void __exit vfb_exit(void)

module_init(vfb_init);

module_exit(vfb_exit);

module_license("gpl");

__get_free_page動態分配了一頁記憶體,核心空間按頁來管理記憶體,在進行對映時,位址要按照頁大小對齊。

模組解除安裝時使用free_page來釋放之前分配的記憶體。

read介面函式即vfb_read,首先判斷了讀取的位元組數是否超過了分配記憶體的大小(page_size是頁大小的巨集,通常為4096位元組),如果超了,則限定最多只能讀一頁的資料。然後用copy_to_user返回未成功複製的位元組數,全部複製成功返回0。

mmap介面的實現,呼叫了remap_pfn_range函式

int remap_pfn_range(struct vm_area_struct *vma,unsigned long addr,

unsigned long pfn,unsigned long size,pgprot_t prot);

vma:描述一片對映區域的結構指標

乙個程序有很多片對映的區域,每乙個區域都有這樣對應的乙個結構,這些結構通過鍊錶和紅黑樹組織在一起,該結構描述了這片對映區域虛擬的起始位址,結束位址和訪問的許可權等資訊

addr:使用者指定的對映之後的虛擬起始位址,如果沒有指定,那麼由核心來指定

pfn:物理記憶體所對應的頁框號,就是實體地址除以頁大小得到的值

size:想要對映的空間的大小

prot:該記憶體區域的訪問許可權

通過該函式一片物理記憶體區域將被對映到使用者空間,而這邊物理記憶體本身在之前又被對映到核心空間,所以這片物理記憶體區域被映**兩次,在使用者空間和核心空間都可以訪問。

virt_to_phys(vfbdev.buf) >> page_shift是首先把核心空間的虛擬位址vfbdev.buf通過virt_to_phys轉換成對應的實體地址,然後將該位址右移page_shift位元位(除以頁的大小)得到物理頁框號。其他的實參都是由vma中的成員來指定。起始位址、大小和許可權都可以由使用者在系統呼叫函式中指定,struct vm_area_struct結構由核心來構造。

#include #include #include #include #include #include int main(int argc, char * ar**)

支援隨機訪問的裝置檔案,訪問的檔案位置可以由使用者來指定,並且對於讀寫這類操作,下一次訪問的檔案位置將會緊接在上一次訪問結束的位置之後,上面的模擬的虛擬顯示卡裝置並不支援這一操作。首先每次讀取的位置都是從檔案最開頭的位置開始的,形參pos沒有用上,沒有llseek系統呼叫所對應的介面函式。

要讓驅動支援定位操作,首先看pos形參的作用。檔案對使用者的抽象是一段線性儲存的資料,可以把檔案看出乙個陣列,每個陣列元素占用乙個位元組,那麼pos引數就是訪問這個陣列的下標的位址。

例如,虛擬顯示卡分配了一頁記憶體,即檔案的內容,如果一頁是4096位元組,那麼*pos的值可以為0~4095,*pos就指定了要訪問的資料的位址相對於起始位址偏移的位元組數。不同於普通檔案的是,這個裝置的大小是固定的。而且,虛擬顯示卡裝置在每次讀取後,驅動應該負責更新*pos值。

和llseek對應的驅動介面是file_operations結構體中llseek函式指標指向的函式。

loff_t (*llseek) (struct fo;e *, loft_t, int);
第乙個引數代表開啟檔案的file結構

第二個引數代表偏移量

第三個引數代表 位置,分別是seek_set、seek_cur和seek_end

llseek介面要做的事情是根據傳入的引數來調整儲存在file結構中的檔案位置值。

示例:

#include #include #include #include #include #include #include #define vfb_major	256

#define vfb_minor 1

#define vfb_dev_cnt 1

#define vfb_dev_name "vfbdev"

struct vfb_dev ;

static struct vfb_dev vfbdev;

static int vfb_open(struct inode * inode, struct file * filp)

static int vfb_release(struct inode *inode, struct file *filp)

static int vfb_mmap(struct file *filp, struct vm_area_struct *vma)

ssize_t vfb_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)

//檔案定位操作

static loff_t vfb_llseek(struct file * filp, loff_t off, int whence)

//判斷新的位置值是否合法

if (newpos < 0 || newpos > page_size)

return -einval;

//將新的檔案位置值更新到file結構的f_pos成員中

filp->f_pos = newpos;

return newpos;

}static struct file_operations vfb_fops = ;

static int __init vfb_init(void)

static void __exit vfb_exit(void)

module_init(vfb_init);

module_exit(vfb_exit);

module_license("gpl");

#include #include #include #include #include #include int main(int argc, char * ar**)

linux 五種高階IO模型

阻塞io模型 在核心將資料準備好,系統呼叫會一直等待,所有的套接字預設都是阻塞io方式 阻塞io是最常見的io模型 非阻塞io模型 非阻塞io往往需要程式設計師迴圈的方式反覆嘗試讀取檔案描述符,這個過程稱為輪詢,這對於cpu來說的話是較大的浪費,一般只有特定的場景下才能使用 訊號驅動io模型 核心將...

Linux核心開發之阻塞非阻塞IO 輪詢操作

小王,來聊聊,今天面試的情況怎麼樣,應該挺順利的吧.看著小王平淡的眉頭,我問道。唉,別提了,你說,我的運氣咋這差呢,面試前你不是給我講了有關阻塞的問題嗎,我見了面試官是吧,還跟他好好的用今天排隊的例子說了有關阻塞的問題,但是.小王哀聲嘆氣地說到。別但是了,怎麼啦.可問題是面試官壓根就沒打算問我有關阻...

linux中五種高階IO模型總結

高階io模型 高階io其實是相對於基礎io的乙個優化,一般基礎io都是像read,write這樣的函式來供我們呼叫io介面,當我們呼叫它們時,它們一般情況下就是阻塞接收 也就是我們的程式在執行read函式時停下來,如果檔案中有資料了,再進行讀取,等io操作完畢後,才能繼續執行下面的程式,當然我們也可...