file_operation(文件操作)file(文件)inode(節點)三者比較

file_operation就是把系統調用和驅動程序關聯起來的關鍵數據結構。這個結構的每一個成員都對應着一個系統調用。讀取file_operation中相應的函數指針,接着把控制權轉交給函數,從而完成了Linux設備驅動程序的工作。

   在系統內部,I/O設備的存取操作通過特定的入口點來進行,而這組特定的入口點恰恰是由設備驅動程序提供的。通常這組設備驅動程序接口是由結構file_operations結構體向系統說明的,它定義在include/linux/fs.h中。

傳統上,一個file_operation結構或者其一個指針稱爲fops(或者它的一些變體).結構中的每個成員必須指向驅動中的函數,這些函數實現一個特別的操作,或者對於不支持的操作留置爲NULL.當指定爲NULL指針時內核的確切的行爲是每個函數不同的。

在你通讀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 {

      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,一個在<linux/module.h>中定義的宏.

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的指針通常被命名爲filefilp。一下是對結構中的每個數據成員的解釋:
一、
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/
二、
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
是與文件相關的目錄項對象。
三、
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函數對文件內容進行讀取。
四、
atomic_t                f_count;
atomic_t
被定義爲:
typedef struct { volatile int counter; } atomic_t;
volatile
修飾字段告訴gcc不要對該類型的數據做優化處理,對它的訪問都是對內存的訪問,而不是對寄存器的訪問。
本質是int類型,之所以這樣寫是讓編譯器對基於該類型變量的操作進行嚴格的類型檢查。此處f_count的作用是記錄對文件對象的引用計數,也即當前有多少個進程在使用該文件。

五、
unsigned int            f_flags;
當打開文件時指定的標誌,對應系統調用openint flags參數。驅動程序爲了支持非阻塞型操作需要檢查這個標誌。
六、
mode_t                  f_mode;
對文件的讀寫模式,對應系統調用openmod_t mode參數。如果驅動程序需要這個值,可以直接讀取這個字段。
mod_t
被定義爲:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t         mode_t;
七、
loff_t                  f_pos;
當前的文件指針位置,即文件的讀寫位置。
loff_t
被定義爲:
typedef long long       __kernel_loff_t;
typedef __kernel_loff_t         loff_t;
八、
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時間通知的數據。

九、
unsigned int            f_uid, f_gid;
標識文件的所有者id,所有者所在組的id.
十、

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
十一、
unsigned long           f_version;
記錄文件的版本號,每次使用後都自動遞增。
十二、
#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;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章