對於Linux系統中,通常字符設備和驅動之間的函數調用關係以下圖所示html
上圖描述了用戶空間應用程序經過系統調用來調用程序的過程。通常而言在驅動程序的設計中,會關係 struct file 和 struct inode 這兩個結構體。node
用戶空間使用open()系統調用函數打開一個字符設備時( int fd = open("dev/demo", O_RDWR) )大體有如下過程:linux
VFS inode 包含文件訪問權限、屬主、組、大小、生成時間、訪問時間、最後修改時間等信息。它是Linux 管理文件系統的最基本單位,也是文件系統鏈接任何子目錄、文件的橋樑。數組
內核使用inode結構體在內核內部表示一個文件。所以,它與表示一個已經打開的文件描述符的結構體(即file 文件結構)是不一樣的,咱們可使用多個file 文件結構表示同一個文件的多個文件描述符,但此時,全部的這些file文件結構所有都必須只能指向一個inode結構體。數據結構
inode結構體包含了一大堆文件相關的信息,可是就針對驅動代碼來講,咱們只要關心其中的兩個域便可:app
下面是源代碼:ide
struct inode { struct hlist_node i_hash; struct list_head i_list; struct list_head i_sb_list; struct list_head i_dentry; unsigned long i_ino; atomic_t i_count; unsigned int i_nlink; uid_t i_uid;//inode擁有者id gid_t i_gid;//inode所屬羣組id dev_t i_rdev;//如果設備文件,表示記錄設備的設備號 u64 i_version; loff_t i_size;//inode所表明大少 #ifdef __NEED_I_SIZE_ORDERED seqcount_t i_size_seqcount; #endif struct timespec i_atime;//inode最近一次的存取時間 struct timespec i_mtime;//inode最近一次修改時間 struct timespec i_ctime;//inode的生成時間 unsigned int i_blkbits; blkcnt_t i_blocks; unsigned short i_bytes; umode_t i_mode; spinlock_t i_lock; struct mutex i_mutex; struct rw_semaphore i_alloc_sem; const struct inode_operations *i_op; const struct file_operations *i_fop; struct super_block *i_sb; struct file_lock *i_flock; struct address_space *i_mapping; struct address_space i_data; #ifdef CONFIG_QUOTA struct dquot *i_dquot[MAXQUOTAS]; #endif struct list_head i_devices; union { struct pipe_inode_info *i_pipe; struct block_device *i_bdev; struct cdev *i_cdev;//如果字符設備,對應的爲cdev結構 }; };
inode的相關操做函數函數
/* 內核函數從inode中提取設備號 */ /* 提取主設備號 */ static inline unsigned imajor(const struct inode *inode)
{
return MAJOR(inode->i_rdev); } /* 提取次設備號 */ static inline unsigned iminor(const struct inode *inode) { return MINOR(inode->i_rdev); }
在設備驅動中,這也是個很是重要的數據結構,必需要注意一點,這裏的file與用戶空間程序中的FILE指針是不一樣的,用戶空間FILE是定義在C庫中,歷來不會出如今內核中。而struct file,倒是內核當中的數據結構,所以,它也不會出如今用戶層程序中。post
file結構體指示一個已經打開的文件(設備對應於設備文件),其實系統中的每一個打開的文件在內核空間都有一個相應的struct file結構體,它由內核在打開文件時建立,並傳遞給在文件上進行操做的任何函數,直至文件被關閉。若是文件被關閉,內核就會釋放相應的數據結構。ui
在內核源碼中,struct file要麼表示爲file,或者爲filp(意指「file pointer」), 注意區分一點,file指的是struct file自己,而filp是指向這個結構體的指針。
下面是幾個重要成員:
一、fmode_t f_mode;
此文件模式經過 FMODE_READ , FMODE_WRITE 識別了文件爲可讀的,可寫的,或者是兩者。在open或ioctl函數中可能須要檢查此域以確認文件的讀/寫權限,你沒必要直接去檢測讀或寫權限,由於在進行octl等操做時內核自己就須要對其權限進行檢測。
二、 loff_t f_pos;
當前讀寫文件的位置。爲64位。若是想知道當前文件當前位置在哪,驅動能夠讀取這個值而不會改變其位置。對read,write來講,當其接收到一個loff_t型指針做爲其最後一個參數時,他們的讀寫操做便做更新文件的位置,而不須要直接執行filp ->f_pos操做。而
三、unsigned int f_flags;
文件標誌,如 O_RDONLY , O_NONBLOCK 以及 O_SYNC 。在驅動中還能夠檢查O_NONBLOCK標誌查看是否有非阻塞請求。其它的標誌較少使用。特別地注意的是,讀寫權限的檢查是使用f_mode而不是f_flog。全部的標量定義在頭文件中
四、struct file_operations *f_op;
與文件相關的各類操做。當文件須要迅速進行各類操做時,內核分配這個指針做爲它實現文件打開,讀,寫等功能的一部分。filp->f_op 其值從未被內核保存做爲下次的引用,即你能夠改變與文件相關的各類操做,這種方式效率很是高。
file_operation 結構體解析以下:Linux字符設備驅動file_operations
五、 void *private_data;
在驅動調用open方法以前,open系統調用設置此指針爲NULL值。你能夠很自由的將其作爲你本身須要的一些數據域或者無論它,如,你能夠將其指向一個分配好的數據,可是你必須記得在file struct被內核銷燬以前在release方法中釋放這些數據的內存空間。private_data用於在系統調用期間保存各類狀態信息是很是有用的。
前面對用戶層open()的分析提到,經過數據結構 struct inode{...} 中的 i_cdev 成員能夠找到cdev,而全部的字符設備都在 chrdevs 數組中,chrdevs具體是什麼樣的呢
下面先看一下 chrdevs 的定義:
#define CHRDEV_MAJOR_HASH_SIZE 255 static DEFINE_MUTEX(chrdevs_lock);
static struct char_device_struct { struct char_device_struct *next; // 結構體指針 unsigned int major; // 主設備號 unsigned int baseminor; // 次設備起始號 int minorct; // 次備號個數 char name[64]; struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];// 只能掛255個字符主設備<span style="font-family: Arial, Helvetica, sans-serif; rgb(255, 255, 255);"> </span>
能夠看到全局數組 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)個 struct char_device_struct的元素,每個對應一個相應的主設備號。
若是分配了一個設備號,就會建立一個 struct char_device_struct 的對象,並將其添加到 chrdevs 中;這樣,經過chrdevs數組,咱們就能夠知道分配了哪些設備號。
相關函數,(這些函數在上篇已經介紹過,如今回顧一下:
register_chrdev_region( ) 分配指定的設備號範圍
alloc_chrdev_region( ) 動態分配設備範圍
他們都主要是經過調用函數 __register_chrdev_region() 來實現的;要注意,這兩個函數僅僅是註冊設備號!若是要和cdev關聯起來,還要調用cdev_add()。
register_chrdev( )申請指定的設備號,而且將其註冊到字符設備驅動模型中.
它所作的事情爲:
4、cdev 結構體
Linux內核中,使用 struct cdev 來描述一個字符設備
5、文件系統中對字符設備文件的訪問
下面看一下上層應用open() 調用系統調用函數的過程
對於一個字符設備文件, 其inode->i_cdev 指向字符驅動對象cdev, 若是i_cdev爲 NULL ,則說明該設備文件沒有被打開.
因爲多個設備能夠共用同一個驅動程序.因此,經過字符設備的inode 中的i_devices 和 cdev中的list組成一個鏈表
首先,系統調用open打開一個字符設備的時候, 經過一系列調用,最終會執行到 chrdev_open
(最終是經過調用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 這一系列的調用過程,本文暫不討論)
int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所作的事情能夠歸納以下:
1. 根據設備號(inode->i_rdev), 在字符設備驅動模型中查找對應的驅動程序, 這經過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程序cdev的kobject.
2. 設置inode->i_cdev , 指向找到的cdev.
3. 將inode添加到cdev->list 的鏈表中.
4. 使用cdev的ops 設置file對象的f_op
5. 若是ops中定義了open方法,則調用該open方法
6. 返回
執行完 chrdev_open()以後,file對象的f_op指向cdev的ops,於是以後對設備進行的read, write等操做,就會執行cdev的相應操做。
獲得進程號:
asm/current.h
linux/sched.h
#define current get_current()
獲得進程號:current->pid獲得進程名:current->comm