只要用過linux的筒子,或者保守點說接觸到一些linux思想的同志肯定聽說過這樣一句話,在linux下,“一切皆是文件”!
不錯,今天walfred將在快速上手linux設備驅動這一塊,談談linux的設備也符合“一切皆是文件”的思想在linux設備驅動模型應用。如果你不理解Linux設備模型,請看下面:
1.[快速上手Linux設備驅動]之我看Linux設備模型(總線篇)
2.[快速上手Linux設備驅動]之我看Linux設備模型(設備篇)
3.[快速上手Linux設備驅動]之我看Linux設備模型(設備驅動篇)
4.[快速上手Linux設備驅動]之我看Linux設備模型(類子系統篇)
“一切皆是文件”是 Unix/Linux 的基本哲學之一。不僅普通的文件,目錄、字符設備、塊設備、 套接字等在 Unix/Linux 中都是以文件被對待;它們雖然類型不同,但是對其提供的卻是同一套操作界面。
當然,在這里我先給大家上一道預備知識的大菜,不過這邊我先暫時小打小鬧下,稍微說下,日后詳述之。
有請VFS上場--
虛擬文件系統(Virtual File System, 簡稱 VFS), 是 Linux 內核中的一個軟件層,用於給用戶空間的程序提供文件系統接口;同時,它也提供了內核中的一個抽象功能,允許不同的文件系統共存。系統中所 有的文件系統不但依賴 VFS 共存,而且也依靠 VFS 協同工作。
為了能夠支持各種實際文件系統,VFS 定義了所有文件系統都支持的基本的、概念上的接口和數據 結構;同時實際文件系統也提供 VFS 所期望的抽象接口和數據結構,將自身的諸如文件、目錄等概念在形式 上與VFS的定義保持一致。換句話說,一個實際的文件系統想要被 Linux 支持,就必須提供一個符合VFS標准 的接口,才能與 VFS 協同工作。實際文件系統在統一的接口和數據結構下隱藏了具體的實現細節,所以在VFS 層和內核的其他部分看來,所有文件系統都 是相同的。下圖顯示了VFS在內核中與實際的文件系統的協同關系。
正文
從上圖,我們可以簡單的看出一個在user space的應用程序通過系統調用,經過VFS層面,在和設備驅動打交道,最后設備驅動驅動對應設備,這是一個比較簡單的描述,如果分析起來也就不難得到:user space的應用程序與VFS的接口就是系統調用,VFS與驅動程序的接口就是file_operations。
關於file_operations
從之前打過交道的字符設備驅動,我們就知道存在了這么一種file_operations結構體,我現在可以說在描述一個設備,我們用戶最接近的方 法是file_operations,Linux每類設備都定義了文件操作方法,例如,字符設備的操作方法為def_chr_fops,塊設備為 def_blk_fops,網絡設備為bad_sock_fops。每種設備類型底層操作方法是不一樣的,但是通過file_operations方法將 設備類型的差異化屏蔽了,這就是Linux能夠將所有設備都理解為文件的緣由。這里因為我這幾天在搞塊設備,所以先看下def_blk_fops。
struct file_operations def_blk_fops = {
open: blkdev_open,
release: blkdev_close,
llseek: block_llseek,
read: generic_file_read,
write: generic_file_write,
mmap: generic_file_mmap,
fsync: block_fsync,
ioctl: blkdev_ioctl,
};
設備差異性
到這里,又提出一個問題:既然這樣,那設備的差異化又該如何體現呢?在文件系統層定義了文件系統訪問設備的方法,該方法就是 address_space_operations,文件系統通過該方法可以訪問具體的設備。對於字符設備而言,沒有實現 address_space_operations方法,也沒有必要,因為字符設備的接口與文件系統的接口是一樣的,在字符設備open操作的過程中,將 inode所指向的file_operations替換成cdev所指向的file_operations就可以了。這樣用戶層讀寫字符設備可以直接調用 cdev中file_operations方法了。
[linux/fs.h]
/*
*Linux頁高速緩存實用化address_space結構體描述頁高速緩存中的頁面
*定義文件在linux/fs.h中
*/
struct address_space {
struct inode *host; /* 擁有節點 */
struct radix_tree_root page_tree; /* 包含全部頁面的radix樹 */
spinlock_t tree_lock; /* 保護page_tree的自旋鎖 */
unsigned int i_mmap_writable; /* VM_SHARED記數 */
struct prio_tree_root i_mmap; /* 似有映射鏈表 */
struct list_head i_mmap_nonlinear; /* VM_NONLINEAR鏈表 */
spinlock_t i_mmap_lock; /* 保護i_mmap的自旋鎖 */
atomic_t truncate_count; /* 截斷記數 */
unsigned long nrpages; /* 頁總數 */
pgoff_t writeback_index; /* 回寫的起始偏移 */
struct address_space_operations *a_ops; /* 操作表 */
unsigned long flags; /* gfp_mask掩碼與錯誤標識 */
struct backing_dev_info *backing_dev_info; /* 預讀信息 */
spinlock_t private_lock; /* 私有address_space鎖 */
struct list_head private_list; /* 私有address_space鏈表 */
struct address_space *assoc_mapping; /* 相關的緩沖 */
};
/*
*/
struct address_space_operations {
int (*writepage)(struct page *, struct writeback_control *);
int (*readpage) (struct file *, struct page *);
int (*sync_page) (struct page *);
int (*writepages) (struct address_space *, struct writeback_control *);
int (*set_page_dirty) (struct page *);
int (*readpages) (struct file *, struct address_space *,
struct list_head *, unsigned);
int (*prepare_write) (struct file *, struct page *, unsigned, unsigned);
int (*commit_write) (struct file *, struct page *, unsigned, unsigned);
sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, int);
int (*direct_IO) (int, struct kiocb *, const struct iovec *,
loff_t, unsigned long);
};
到這里,是不是明白了linux的“設備”也是文件了呢?