Kernel version:2.6.14
CPU architecture:ARM920T
作為文件的使用者,進程理所當然的要將所使用的文件記錄於自己的控制塊中,也就是task_struct。另外,由於進程所對應的程序也是一個文件,因此進程控制塊還必須記錄這個文件的相關信息。由於OS要對所有進程提供服務,因此OS還要維護一個記錄所有進程打開的文件的總表。
1、文件對象
當進程通過open系統調用打開一個文件時,該系統調用找到這個文件後,會把文件封裝到一個file結構的實例中提供給進程,這個實例稱為file對象。file結構的定義如下:
struct file {
struct list_head f_list; //所有打開文件的鏈表
struct dentry *f_dentry; //文件的dentry
struct vfsmount *f_vfsmnt; //文件目錄的VFS安裝點指針
struct file_operations *f_op; //指向文件操作函數集的指針
atomic_t f_count; //記錄訪問本文件的進程數目的計數器
unsigned int f_flags; //訪問類型
mode_t f_mode; //訪問模式
loff_t f_pos; //文件當前的讀寫位置
struct fown_struct f_owner;
unsigned int f_uid, f_gid; //文件所有者ID和用戶組ID
struct file_ra_state f_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 rcu_head f_rcuhead;
};
結構中的域f_uid為文件所有者的ID,f_gid為文件所有者所在組的ID。這樣就使得一個文件可能面臨三種用戶的訪問:
● 文件所有者;
● 同組用戶;
● 其他用戶。
內核在處理一個進程或用戶訪問一個文件的請求時,要根據進程的f_uid和f_gid以及訪問模式來確定該進程是否具有訪問這個文件的權限。對於一個用戶來說,可以有讀、寫和執行三種文件權限,這三種權限和三種用戶就共有9中組合,即文件的訪問權限可以用9個bit來表示,並將其保存在文件的dentry中。
結構中的域f_pos記錄了進程對文件讀寫位置的當前值,可以通過調用函數llseek進程移動。
結構中的f_op執向結構file_operations,該結構封裝了對文件進行操作的函數,定義如下:
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 *);
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 *);
};
從上面的代碼可以看到,結構中是一系列函數的指針,這裡有我們比較熟悉的read、open、write和close等函數的指針。進程就是通過這些函數訪問一個文件的,file_operations是linux虛擬文件系統VFS和進程之間的接口。
2、文件描述符
下面進一步介紹進程對自己所訪問的file對象的管理方法。linux中使用一個數組來管理進程打開的文件的file對象,數組中的每個元素都存放一個紙箱進程所打開的文件的file對象。既然用一個數組來存放file對象,那麼用數組的下標來訪問文件就是一件順理成章的方法,於是,linux就把數組元素的下標叫做該數組元素所對應的文件的文件描述符,該描述符就是系統對文件的標識,這個數組也叫文件描述符數組,如下圖所示:
內核通過系統調用dup、dup2和fctl可以使數組中的多個元素指向同一個文件的file對象,也就是說,在linux中,同一個文件可以有多個文件描述符。
3、進程打開文件表
進程描述符數組中存放了一個進程所訪問的所有文件,把這個文件描述符數組和這個數組在系統中的一些動態信息組合到一起,就形成了一個新的數據結構——進程打開文件表,即file_struct,其定義如下:
/*
* Open file table structure
*/
struct files_struct {
atomic_t count; //引用計數
spinlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */
struct fdtable *fdt; //管理文件描述符
struct fdtable fdtab; //管理文件描述符
fd_set close_on_exec_init; //位圖
fd_set open_fds_init; //位圖
struct file * fd_array[NR_OPEN_DEFAULT]; //文件描述符數組
};
顯然,這個結構應該屬於進程的私有數據,所以進程控制塊task_struct用指針files指向它。
struct task_struct {
...
/* open file information */
struct files_struct *files;
...
};
進程與其打開文件之間的關系如下圖所示。
4、文件描述符的管理
file_struct中的fdt和fdtab用於管理文件文件描述符,一個是fdtable類型,另一個是其指針類型。fdtable的定義如下:
struct fdtable {
unsigned int max_fds; //可以代開的最大文件數
int max_fdset; //位圖的最大長度
int next_fd; //下一個可用的fd
struct file ** fd; /* current fd array 指向files_struct的fd_array */
fd_set *close_on_exec;
fd_set *open_fds; //打開的文件標記,比如第2位為0,則打開了2號文件
struct rcu_head rcu;
struct files_struct *free_files;
struct fdtable *next;
};
下圖可以很直觀的說明文件描述符fd的管理。