轉自:qian-xu-fenghtml
file_operation就是把系統調用和驅動程序關聯起來的關鍵數據結構。這個結構的每個成員都對應着一個系統調用。讀取file_operation中相應的函數指針,接着把控制權轉交給函數,從而完成了Linux設備驅動程序的工做。node
在系統內部,I/O設備的存取操做經過特定的入口點來進行,而這組特定的入口點偏偏是由設備驅動程序提供的。一般這組設備驅動程序接口是由結構file_operations結構體向系統說明的,它定義在include/linux/fs.h中。linux
傳統上,一個file_operation結構或者其一個指針稱爲fops(或者它的一些變體).結構中的每一個成員必須指向驅動中的函數,這些函數實現一個特別的操做,或者對於不支持的操做留置爲NULL.當指定爲NULL指針時內核的確切的行爲是每一個函數不一樣的。web
在你通讀file_operations方法的列表時,你會注意到很多參數包含字串__user.這種註解是一種文檔形式,注意,一個指針是一個不能被直接解引用的用戶空間地址.對於正常的編譯, __user沒有效果,可是它可被外部檢查軟件使用來找出對用戶空間地址的錯誤使用。算法
---------------------------------------------------------------------後端
註冊設備編號僅僅是驅動代碼必須進行的諸多任務中的第一個。首先須要涉及一個別的,大部分的基礎性的驅動操做包括3個重要的內核數據結構,稱爲file_operations,file,和inode。須要對這些結構的基本瞭解纔可以作大量感興趣的事情。服務器
struct file_operations是一個字符設備把驅動的操做和設備號聯繫在一塊兒的紐帶,是一系列指針的集合,每一個被打開的文件都對應於一系列的操做,這就是file_operations,用來執行一系列的系統調用。網絡
struct file表明一個打開的文件,在執行file_operation中的open操做時被建立,這裏須要注意的是與用戶空間inode指針的區別,一個在內核,而file指針在用戶空間,由c庫來定義。
struct inode被內核用來表明一個文件,注意和struct file的區別,struct inode一個是表明文件,struct file一個是表明打開的文件
struct inode包括很重要的二個成員:
dev_t i_rdev 設備文件的設備號
struct cdev *i_cdev表明字符設備的數據結構
struct inode結構是用來在內核內部表示文件的.同一個文件能夠被打開好屢次,因此能夠對應不少struct file,可是隻對應一個struct inode.數據結構
struct file_operations {app
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};
File_operations的數據結構以下:
struct module *owner
第一個file_operations成員根本不是一個操做;它是一個指向擁有這個結構的模塊的指針.這個成員用來在它的操做還在被使用時阻止模塊被卸載.幾乎全部時間中,它被簡單初始化爲THIS_MODULE,一個在中定義的宏.
loff_t (*llseek) (struct file *, loff_t, int);
llseek方法用做改變文件中的當前讀/寫位置,而且新位置做爲(正的)返回值. loff_t參數是一個"long offset",而且就算在32位平臺上也至少64位寬.錯誤由一個負返回值指示.若是這個函數指針是NULL, seek調用會以潛在地沒法預知的方式修改file結構中的位置計數器(在"file結構"一節中描述).
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
用來從設備中獲取數據.在這個位置的一個空指針致使read系統調用以-EINVAL("Invalid argument")失敗.一個非負返回值表明了成功讀取的字節數(返回值是一個"signed size"類型,經常是目標平臺本地的整數類型).
ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);
初始化一個異步讀--可能在函數返回前不結束的讀操做.若是這個方法是NULL,全部的操做會由read代替進行(同步地).
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
發送數據給設備.若是NULL, -EINVAL返回給調用write系統調用的程序.若是非負,返回值表明成功寫的字節數.
ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);
初始化設備上的一個異步寫.
int (*readdir) (struct file *, void *, filldir_t);
對於設備文件這個成員應當爲NULL;它用來讀取目錄,而且僅對文件系統有用.
unsigned int (*poll) (struct file *, struct poll_table_struct *);
poll方法是3個系統調用的後端: poll, epoll,和select,都用做查詢對一個或多個文件描述符的讀或寫是否會阻塞. poll方法應當返回一個位掩碼指示是否非阻塞的讀或寫是可能的,而且,可能地,提供給內核信息用來使調用進程睡眠直到I/O變爲可能.若是一個驅動的poll方法爲NULL,設備假定爲不阻塞地可讀可寫.
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
ioctl系統調用提供了發出設備特定命令的方法(例如格式化軟盤的一個磁道,這不是讀也不是寫).另外,幾個ioctl命令被內核識別而沒必要引用fops表.若是設備不提供ioctl方法,對於任何未事先定義的請求(-ENOTTY, "設備無這樣的ioctl"),系統調用返回一個錯誤.
int (*mmap) (struct file *, struct vm_area_struct *);
mmap用來請求將設備內存映射到進程的地址空間.若是這個方法是NULL, mmap系統調用返回-ENODEV.
int (*open) (struct inode *, struct file *);
儘管這經常是對設備文件進行的第一個操做,不要求驅動聲明一個對應的方法.若是這個項是NULL,設備打開一直成功,可是你的驅動不會獲得通知.
int (*flush) (struct file *);
flush操做在進程關閉它的設備文件描述符的拷貝時調用;它應當執行(而且等待)設備的任何未完成的操做.這個必須不要和用戶查詢請求的fsync操做混淆了.當前, flush在不多驅動中使用; SCSI磁帶驅動使用它,例如,爲確保全部寫的數據在設備關閉前寫到磁帶上.若是flush爲NULL,內核簡單地忽略用戶應用程序的請求.
int (*release) (struct inode *, struct file *);
在文件結構被釋放時引用這個操做.如同open, release能夠爲NULL.
int (*fsync) (struct file *, struct dentry *, int);
這個方法是fsync系統調用的後端,用戶調用來刷新任何掛着的數據.若是這個指針是NULL,系統調用返回-EINVAL.
int (*aio_fsync)(struct kiocb *, int);
這是fsync方法的異步版本.
int (*fasync) (int, struct file *, int);
這個操做用來通知設備它的FASYNC標誌的改變.異步通知是一個高級的主題,在第6章中描述.這個成員能夠是NULL若是驅動不支持異步通知.
int (*lock) (struct file *, int, struct file_lock *);
lock方法用來實現文件加鎖;加鎖對常規文件是必不可少的特性,可是設備驅動幾乎從不實現它.
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
這些方法實現發散/匯聚讀和寫操做.應用程序偶爾須要作一個包含多個內存區的單個讀或寫操做;這些系統調用容許它們這樣作而沒必要對數據進行額外拷貝.若是這些函數指針爲NULL, read和write方法被調用(可能多於一次).
ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);
這個方法實現sendfile系統調用的讀,使用最少的拷貝從一個文件描述符搬移數據到另外一個.例如,它被一個須要發送文件內容到一個網絡鏈接的web服務器使用.設備驅動經常使sendfile爲NULL.
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
sendpage是sendfile的另外一半;它由內核調用來發送數據,一次一頁,到對應的文件.設備驅動實際上不實現sendpage.
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
這個方法的目的是在進程的地址空間找一個合適的位置來映射在底層設備上的內存段中.這個任務一般由內存管理代碼進行;這個方法存在爲了使驅動能強制特殊設備可能有的任何的對齊請求.大部分驅動能夠置這個方法爲NULL.
int (*check_flags)(int)
這個方法容許模塊檢查傳遞給fnctl(F_SETFL...)調用的標誌.
int (*dir_notify)(struct file *, unsigned long);
這個方法在應用程序使用fcntl來請求目錄改變通知時調用.只對文件系統有用;驅動不須要實現dir_notify.
---------------------------------------------------------------------
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_structf_owner;
unsigned int f_uid, f_gid;
struct file_ra_statef_ra;
unsigned long f_version;
void *f_security;
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
文件結構體表明一個打開的文件,系統中的每一個打開的文件在內核空間都有一個關聯的struct file。它由內核在打開文件時建立,並傳遞給在文件上進行操做的任何函數。在文件的全部實例都關閉後,內核釋放這個數據結構。在內核建立和驅動源碼中,struct file的指針一般被命名爲file或filp。一下是對結構中的每一個數據成員的解釋:
1、
union {
struct list_head fu_list;
struct rcu_head rcuhead;
}f_u;
其中的struct list_head定義在linux/include/linux/list.h中,原型爲:
struct list_head {
struct list_head *next, *prev;
};
用於通用文件對象鏈表的指針。
struct rcu_head定義在linux/include/linux/rcupdate.h中,其原型爲:
/**
* struct rcu_head - callback structure for use with RCU
* @next: next update requests in a list
* @func: actual update function to call after the grace period.
*/
struct rcu_head {
struct rcu_head *next;
void (*func)(struct rcu_head *head);
};
RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制,具體在這裏有介紹:
http://www.ibm.com/developerworks/cn/linux/l-rcu/
2、
struct path f_path;
被定義在linux/include/linux/namei.h中,其原型爲:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
在早些版本的內核中並無此結構,而是直接將path的兩個數據成員做爲struct file的數據成員,
struct vfsmount *mnt的做用是指出該文件的已安裝的文件系統,
struct dentry *dentry是與文件相關的目錄項對象。
3、
const struct file_operations *f_op;
被定義在linux/include/linux/fs.h中,其中包含着與文件關聯的操做,如:
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
等。當打開一個文件時,內核就建立一個與該文件相關聯的struct file結構,其中的*f_op就指向的是
具體對該文件進行操做的函數。例如用戶調用系統調用read來讀取該文件的內容時,那麼系統調用read最終會陷入內核調用sys_read函數,而sys_read最終會調用於該文件關聯的struct file結構中的f_op->read函數對文件內容進行讀取。
4、
atomic_t f_count;
atomic_t被定義爲:
typedef struct { volatile int counter; } atomic_t;
volatile修飾字段告訴gcc不要對該類型的數據作優化處理,對它的訪問都是對內存的訪問,而不是對寄存器的訪問。
本質是int類型,之因此這樣寫是讓編譯器對基於該類型變量的操做進行嚴格的類型檢查。此處f_count的做用是記錄對文件對象的引用計數,也即當前有多少個進程在使用該文件。
5、
unsigned int f_flags;
當打開文件時指定的標誌,對應系統調用open的int flags參數。驅動程序爲了支持非阻塞型操做須要檢查這個標誌。
6、
mode_t f_mode;
對文件的讀寫模式,對應系統調用open的mod_t mode參數。若是驅動程序須要這個值,能夠直接讀取這個字段。
mod_t被定義爲:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t mode_t;
7、
loff_t f_pos;
當前的文件指針位置,即文件的讀寫位置。
loff_t被定義爲:
typedef long long __kernel_loff_t;
typedef __kernel_loff_t loff_t;
8、
struct fown_struct f_owner;
struct fown_struct在linux/include/linux/fs.h被定義,原型爲:
struct fown_struct {
rwlock_t lock; /* protects pid, uid, euid fields */
struct pid *pid; /* pid or -pgrp where SIGIO should be sent */
enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
uid_t uid, euid; /* uid/euid of process setting the owner */
int signum; /* posix.1b rt signal to be delivered on IO */
};
該結構的做用是經過信號進行I/O時間通知的數據。
9、
unsigned int f_uid, f_gid;
標識文件的全部者id,全部者所在組的id.
10、
struct file_ra_state f_ra;
struct file_ra_state結構被定義在/linux/include/linux/fs.h中,原型爲:
struct file_ra_state {
pgoff_t start; /* where readahead started */
unsigned long size; /* # of readahead pages */
unsigned long async_size; /* do asynchronous readahead when
there are only # of pages ahead */
unsigned long ra_pages; /* Maximum readahead window */
unsigned long mmap_hit; /* Cache hit stat for mmap accesses */
unsigned long mmap_miss; /* Cache miss stat for mmap accesses */
unsigned long prev_index; /* Cache last read() position */
unsigned int prev_offset; /* Offset where last read() ended in a page */
};
文件預讀狀態,文件預讀算法使用的主要數據結構,當打開一個文件時,f_ra中出了perv_page(默認爲-1)和ra_apges(對該文件容許的最大預讀量)這兩個字段外,其餘的全部西端都置爲0。
11、
unsigned long f_version;
記錄文件的版本號,每次使用後都自動遞增。
12、
#ifdef CONFIG_SECURITY
void *f_security;
------------------------------------------------------------------------------------------------------
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;
umode_t i_mode;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;