OS / Linux / 文件描述符以及 file 结构体
零、前言
程序可以理解為硬盤上的普通二進(jìn)制文件;進(jìn)程是加載到內(nèi)存中的二進(jìn)制文件,除了加載到內(nèi)存中的二進(jìn)制文件外,還附有所有對(duì)于該二進(jìn)制文件描述信息的結(jié)構(gòu)體,描述該進(jìn)程的結(jié)構(gòu)體叫PCB(進(jìn)程控制塊),在這就不在討論。對(duì)于程序與進(jìn)程,也就可以簡(jiǎn)單地理解為是否有PCB(進(jìn)程控制塊)。下面我們?cè)賮碛懻?PCB 與 file_struct 的關(guān)系。
在每一個(gè)PCB中,都有一個(gè)文件描述符表,通過文件描述符索引指向 file 結(jié)構(gòu)體 (系統(tǒng)打開文件表)。
文件描述符在形式上是一個(gè)非負(fù)整數(shù),實(shí)際上,它是一個(gè)索引值,指向內(nèi)核為每一個(gè)進(jìn)程所維護(hù)的該進(jìn)程打開文件的記錄表,當(dāng)程序打開一個(gè)現(xiàn)有文件或創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符。也就是說,一個(gè)程序能夠訪問文件是因?yàn)榻o這個(gè)程序分配了文件描述符。
下面我們來討論 file 結(jié)構(gòu)體里面具體有哪些內(nèi)容,file 結(jié)構(gòu)體定義在 linux 系統(tǒng)中的(/kernels/include/linus/fs.h)文件中。
一、file 結(jié)構(gòu)體
struct file {union{struct list_head fu_list; //文件對(duì)象鏈表指針 linux/include/linux/list.h 。struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是 Linux 2.6 內(nèi)核中新的鎖機(jī)制。} f_u;struct path f_path; //包含 dentry 和 mnt 兩個(gè)成員,用于確定文件路徑。 #define f_dentry f_path.dentry //f_path 的成員之一,當(dāng)統(tǒng)的掛載根目錄。const struct file_operations; //*f_op; 與該文件相關(guān)聯(lián)的操作函數(shù)。atomic_t f_count; //文件的引用計(jì)數(shù)(有多少進(jìn)程打開該文件)。unsigned int f_flags; //對(duì)應(yīng)于 open 時(shí)指定的 flag 。mode_t f_mode; //讀寫模式:open 的 mod_t mode 參數(shù)。off_t f_pos; //該文件在當(dāng)前進(jìn)程中的文件偏移量。struct fown_struct f_owner; //該結(jié)構(gòu)的作用是通過信號(hào)進(jìn)行 I/O 時(shí)間通知的數(shù)據(jù)。unsigned int f_uid, f_gid; //文件所有者 id,所有者組 id。struct file_ra_state f_ra; //在 linux/include/linux/fs.h 中定義,文件預(yù)讀相關(guān)。unsigned long f_version; #ifdef CONFIG_SECURITYvoid *f_security; #endif void *private_data; #ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock; #endif struct address_space *f_mapping; };其中重要參數(shù)參數(shù)介紹如下:
f_flags:表示打開文件的權(quán)限。
f_pos:表示當(dāng)前讀寫文件的位置。
f_count:這個(gè)是一個(gè)相對(duì)來說比較重要的參數(shù),表示打開文件的引用計(jì)數(shù),如果有多個(gè)文件指針指向它,就會(huì)增加 f_count 的值。
f_mode:設(shè)置對(duì)文件的訪問模式,例如:只讀,只寫等。
當(dāng)然其中還定義了許多結(jié)構(gòu)體等內(nèi)容,這里就不在深究,下面我們來討論一個(gè) fd 與 files_struct 的關(guān)系。files_struct 不同于 file 結(jié)構(gòu)體。在這里要區(qū)分清楚。
二、file_operations
當(dāng)我們打開一個(gè)文件時(shí),操作系統(tǒng)為了管理所打開的文件,都會(huì)為這個(gè)文件創(chuàng)建一個(gè) file 結(jié)構(gòu)體,而 file 結(jié)構(gòu)體中的 f_op 指針又指向 file_operations 結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中的成員除了 struct module* owner 其余都是函數(shù)指針,file_operation 就是把系統(tǒng)調(diào)用和驅(qū)動(dòng)程序關(guān)聯(lián)起來的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)的每一個(gè)成員都對(duì)應(yīng)著一個(gè)系統(tǒng)調(diào)用。讀取 file_operation 中相應(yīng)的函數(shù)指針,接著把控制權(quán)轉(zhuǎn)交給函數(shù),從而完成了 Linux 設(shè)備驅(qū)動(dòng)程序的工作。
我們先來看看 file_operations 結(jié)構(gòu)體的實(shí)現(xiàn)和相關(guān)成員的介紹。
struct file_operations {struct module *owner;//指向擁有該模塊的指針;loff_t (*llseek)(struct file *, loff_t, int);//llseek 方法用作改變文件中的當(dāng)前讀/寫位置, 并且新位置作為(正的)返回值.ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);//用來從設(shè)備中獲取數(shù)據(jù). 在這個(gè)位置的一個(gè)空指針導(dǎo)致 read 系統(tǒng)調(diào)用以 -EINVAL("Invalid argument") 失敗. 一個(gè)非負(fù)返回值代表了成功讀取的字節(jié)數(shù)( 返回值是一個(gè) "signed size" 類型, 常常是目標(biāo)平臺(tái)本地的整數(shù)類型).ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);//發(fā)送數(shù)據(jù)給設(shè)備. 如果 NULL, -EINVAL 返回給調(diào)用 write 系統(tǒng)調(diào)用的程序. 如果非負(fù), 返回值代表成功寫的字節(jié)數(shù).ssize_t (*aio_read)(struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一個(gè)異步讀 -- 可能在函數(shù)返回前不結(jié)束的讀操作.ssize_t (*aio_write)(struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化設(shè)備上的一個(gè)異步寫.int (*readdir)(struct file *, void *, filldir_t);//對(duì)于設(shè)備文件這個(gè)成員應(yīng)當(dāng)為 NULL; 它用來讀取目錄, 并且僅對(duì)**文件系統(tǒng)**有用.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 *);//mmap 用來請(qǐng)求將設(shè)備內(nèi)存映射到進(jìn)程的地址空間. 如果這個(gè)方法是 NULL, mmap 系統(tǒng)調(diào)用返回 -ENODEV.int (*open)(struct inode *, struct file *);//打開一個(gè)文件int (*flush)(struct file *, fl_owner_t id);//flush 操作在進(jìn)程關(guān)閉它的設(shè)備文件描述符的拷貝時(shí)調(diào)用;int (*release)(struct inode *, struct file *);//在文件結(jié)構(gòu)被釋放時(shí)引用這個(gè)操作. 如同 open, release 可以為 NULL.int (*fsync)(struct file *, struct dentry *, int datasync);//用戶調(diào)用來刷新任何掛著的數(shù)據(jù).int (*aio_fsync)(struct kiocb *, int datasync);int (*fasync)(int, struct file *, int);int (*lock)(struct file *, int, struct file_lock *);//lock 方法用來實(shí)現(xiàn)文件加鎖; 加鎖對(duì)常規(guī)文件是必不可少的特性, 但是設(shè)備驅(qū)動(dòng)幾乎從不實(shí)現(xiàn)它.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 (*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);int (*setlease)(struct file *, long, struct file_lock **); };?
文件描述符(file)是操作系統(tǒng)用來管理文件的數(shù)據(jù)結(jié)構(gòu),fd 就是 fd_array 的索引,FILE * 指針值就是該 fd_array 數(shù)組中元素的值,也就是 file 結(jié)構(gòu)體的地址。
三、files_struct
每個(gè)進(jìn)程用一個(gè) files_struct 結(jié)構(gòu)來記錄文件描述符的使用情況,這個(gè) files_struct 結(jié)構(gòu)稱為用戶打開文件表,它是進(jìn)程的私有數(shù)據(jù)。
files_struct 結(jié)構(gòu)在 include/linux/fdtable.h 中定義如下:
struct files_struct {atomic_t count; /* 共享該表的進(jìn)程數(shù) */rwlock_t file_lock; /* 保護(hù)以下的所有域,以免在tsk->alloc_lock中的嵌套*/int max_fds; /*當(dāng)前文件對(duì)象的最大數(shù)*/int max_fdset; /*當(dāng)前文件描述符的最大數(shù)*/int next_fd;/*已分配的文件描述符加1 */struct file **fd; /* 指向文件對(duì)象指針數(shù)組的指針 */fd_set *close_on_exec; /*指向執(zhí)行exec( )時(shí)需要關(guān)閉的文件描述符*/fd_set *open_fds; /*指向打開文件描述符的指針*/fd_set close_on_exec_init; /* 執(zhí)行exec( )時(shí)需要關(guān)閉的文件描述符的初 值集合*/fd_set open_fds_init; /*文件描述符的初值集合*/struct file *fd_array[32]; /* 文件對(duì)象指針的初始化數(shù)組*/ };?四、概括
?
參考:https://www.cnblogs.com/xiangtingshen/p/11961434.html
?
(SAW:Game Over!)
?
總結(jié)
以上是生活随笔為你收集整理的OS / Linux / 文件描述符以及 file 结构体的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OS / CPU是如何访问内存的?
- 下一篇: Qt / 对象树