Linux中文件描述符fd和文件指針flip的理解


轉自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html

簡單歸納:fd只是一個整數,在open時產生。起到一個索引的作用,進程通過PCB中的文件描述符表找到該fd所指向的文件指針filp。

open:文件描述符的操作(如: open)返回的是一個文件描述符(int fd),內核會在每個進程空間中維護一個文件描述符表, 所有打開的文件都將通過此表中的文件描述符來引用(fd1,fd2,fd3...); 
fopen:而流(如: fopen)返回的是一個FILE結構指針, FILE結構是包含有文件描述符的,FILE結構函數可以看作是對fd直接操作的系統調用的封裝, 它的優點是帶有I/O緩存.

Linux支持各種各樣的文件系統格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不同的磁盤分區、光盤或其它存儲設備都有不同的文件系統格式,然而這些文件系統都可以mount到某個目錄下,使我們看到一個統一的目錄樹,各種文件系統上的目錄和文件我們用ls命令看起來是一樣的,讀寫操作用起來也都是一樣的,這是怎么做到的呢?Linux內核在各種不同的文件系統格式之上做了一個抽象層,使得文件、目錄、讀寫訪問等概念成為抽象層的概念,因此各種文件系統看起來用起來都一樣(VFS作用),這個抽象層稱為虛擬文件系統(VFS,Virtual Filesystem),這一節我們介紹運行時文件系統在內核中的表示。

Linux內核的VFS子系統可以圖示如下:

每個進程在PCB(Process Control Block)即進程控制塊中都保存着一份文件描述符表,文件描述符就是這個表的索引,文件描述表中每個表項都有一個指向已打開文件的指針,現在我們明確一下:已打開的文件在內核中用file結構體表示,文件描述符表中的指針指向file結構體(理解:fd為打開文件的文件描述符,而每個進程都有一張文件描述表,fd文件描述符就是這張表的索引,同樣這張表中有一表項,該表項又是指向前面提到打開文件的file結構體,file結構體才是內核中用於描述文件屬性的結構體)。

struct file-----define in inlcude/linux/fs.h

struct file_operations------define in include/linux/fs.h

 1 struct file {
 2     union {
 3         struct llist_node    fu_llist;
 4         struct rcu_head     fu_rcuhead;
 5     } f_u;
 6     struct path        f_path;
 7 #define f_dentry    f_path.dentry
 8     struct inode        *f_inode;    /* cached value */
 9     const struct file_operations    *f_op;
10 
11     /*
12      * Protects f_ep_links, f_flags.
13      * Must not be taken from IRQ context.
14      */
15     spinlock_t        f_lock;
16     atomic_long_t        f_count;
17     unsigned int         f_flags;
18     fmode_t            f_mode;
19     struct mutex        f_pos_lock;
20     loff_t            f_pos;
21     struct fown_struct    f_owner;
22     const struct cred    *f_cred;
23     struct file_ra_state    f_ra;
24 
25     u64            f_version;
26 #ifdef CONFIG_SECURITY
27     void            *f_security;
28 #endif
29     /* needed for tty driver, and maybe others */
30     void            *private_data;
31 
32 #ifdef CONFIG_EPOLL
33     /* Used by fs/eventpoll.c to link all the hooks to this file */
34     struct list_head    f_ep_links;
35     struct list_head    f_tfile_llink;
36 #endif /* #ifdef CONFIG_EPOLL */
37     struct address_space    *f_mapping;
38 #ifdef CONFIG_DEBUG_WRITECOUNT
39     unsigned long f_mnt_write_state;
40 #endif
41 } __attribute__((aligned(4)));    /* lest something weird decides that 2 is OK */
struct file
 1 struct file_operations {
 2     struct module *owner;
 3     loff_t (*llseek) (struct file *, loff_t, int);
 4     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 5     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 6     ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 7     ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 8     int (*iterate) (struct file *, struct dir_context *);
 9     unsigned int (*poll) (struct file *, struct poll_table_struct *);
10     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
11     long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
12     int (*mmap) (struct file *, struct vm_area_struct *);
13     int (*open) (struct inode *, struct file *);
14     int (*flush) (struct file *, fl_owner_t id);
15     int (*release) (struct inode *, struct file *);
16     int (*fsync) (struct file *, loff_t, loff_t, int datasync);
17     int (*aio_fsync) (struct kiocb *, int datasync);
18     int (*fasync) (int, struct file *, int);
19     int (*lock) (struct file *, int, struct file_lock *);
20     ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
21     unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
22     int (*check_flags)(int);
23     int (*flock) (struct file *, int, struct file_lock *);
24     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
25     ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
26     int (*setlease)(struct file *, long, struct file_lock **);
27     long (*fallocate)(struct file *file, int mode, loff_t offset,
28               loff_t len);
29     int (*show_fdinfo)(struct seq_file *m, struct file *f);
30 };
struct file_operations

 

1.file.File Status Flag和file.f_count

file結構體中維護File Status Flag(file結構體的成員f_flags)和當前讀寫位置(file結構體的成員f_pos)。在上圖中,進程1和進程2都打開同一文件,但是對應不同的file結構體,因此可以有不同的File Status Flag和讀寫位置。file結構體中比較重要的成員還有f_count,表示引用計數(Reference Count),后面我們會講到,dupfork等系統調用會導致多個文件描述符指向同一個file結構體,例如有fd1fd2都引用同一個file結構體,那么它的引用計數就是2,當close(fd1)時並不會釋放file結構體,而只是把引用計數減到1,如果再close(fd2),引用計數就會減到0同時釋放file結構體,這才真的關閉了文件。

2.file.file_operations

每個file結構體都指向一個file_operations結構體,這個結構體的成員都是函數指針,指向實現各種文件操作的內核函數。比如在用戶程序中read一個文件描述符,read通過系統調用進入內核,然后找到這個文件描述符所指向的file結構體,找到file結構體所指向的file_operations結構體,調用它的read成員所指向的內核函數以完成用戶請求(應用層到內核層的調用流程)。在用戶程序中調用lseekreadwriteioctlopen等函數,最終都由內核調用file_operations的各成員所指向的內核函數完成用戶請求。file_operations結構體中的release成員用於完成用戶程序的close請求,之所以叫release而不叫close是因為它不一定真的關閉文件,而是減少引用計數,只有引用計數減到0才關閉文件。對於同一個文件系統上打開的常規文件來說,readwrite等文件操作的步驟和方法應該是一樣的,調用的函數應該是相同的,所以圖中的三個打開文件的file結構體指向同一個file_operations結構體。如果打開一個字符設備文件,那么它的readwrite操作肯定和常規文件不一樣,不是讀寫磁盤的數據塊而是讀寫硬件設備,所以file結構體應該指向不同的file_operations結構體(也就有了用戶自定義結構體對象或者內核自定義結構體對象),其中的各種文件操作函數由該設備的驅動程序實現。

3.file.dentry

每個file結構體都有一個指向dentry結構體的指針,“dentry”是directory entry(目錄項)的縮寫。我們傳給openstat等函數的參數的是一個路徑,例如/home/akaedu/a,需要根據路徑找到文件的inode為了減少讀盤次數,內核緩存了目錄的樹狀結構,稱為dentry cache(作用),其中每個節點是一個dentry結構體,只要沿着路徑各部分的dentry搜索即可,從根目錄/找到home目錄,然后找到akaedu目錄,然后找到文件a。dentry cache只保存最近訪問過的目錄項,如果要找的目錄項在cache中沒有,就要從磁盤讀到內存中。

4.dentry.inode

每個dentry結構體都有一個指針指向inode結構體。inode結構體保存着從磁盤inode讀上來的信息。在上圖的例子中,有兩個dentry,分別表示/home/akaedu/a/home/akaedu/b,它們都指向同一個inode,說明這兩個文件互為硬鏈接inode結構體中保存着從磁盤分區的inode讀上來信息,例如所有者、文件大小、文件類型和權限位等(inode有哪些參數,正常理解file結構體可能包含這些信息,其實是file.inode成員管理這些信息)。每個inode結構體都有一個指向inode_operations結構體的指針,后者也是一組函數指針指向一些完成文件目錄操作的內核函數。和file_operations不同,inode_operations所指向的不是針對某一個文件進行操作的函數,而是影響文件和目錄布局的函數,例如添加刪除文件和目錄、跟蹤符號鏈接等等,屬於同一文件系統的各inode結構體可以指向同一個inode_operations結構體。

5.inod.super_block

inode結構體有一個指向super_block結構體的指針。super_block結構體保存着從磁盤分區的超級塊讀上來的信息,例如文件系統類型、塊大小等。super_block結構體的s_root成員是一個指向dentry的指針,表示這個文件系統的根目錄被mount到哪里,在上圖的例子中這個分區被mount/home目錄下。

 

filedentryinodesuper_block這幾個結構體組成了VFS的核心概念。對於ext2文件系統來說,在磁盤存儲布局上也有inode和超級塊的概念,所以很容易和VFS中的概念建立對應關系。而另外一些文件系統格式來自非UNIX系統(例如Windows的FAT32、NTFS),可能沒有inode或超級塊這樣的概念,但為了能mount到Linux系統,也只好在驅動程序中硬湊一下,在Linux下看FAT32和NTFS分區會發現權限位是錯的,所有文件都是rwxrwxrwx,因為它們本來就沒有inode和權限位的概念,這是硬湊出來的。

 

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM