一、进程与vfs对象之间的关系很重要
1、、文件对象是在文件被打开的时候,由进程创建的,由一个file结构来描述,文件结构也仅仅存在于内存中。
2、文件对象中存了一个重要信息:文件指针(文件当前位置),几个进程可能同时访问同一文件,因此文件指针必须存放在文件对象,而非索引节点。 这也是为什么一个文件被打开一次,就要创建一次文件对象。
3、使用dup(), dup2(), fcntl() 两个文件描述符可以指向同一个打开的文件对象
二、进程与文件系统通过如下结构关联
struct task_struct {
。。。。。。。。
/* filesystem information */
struct fs_struct *fs; //文件系统信息
/* open file information */
struct files_struct *files; //打开的文件对象信息
/* namespaces */
struct nsproxy *nsproxy; //合名空间
。。。。。。。
}
进程利用fs_struct结构体的当前的工作目录,和它自已的根目录与文件系统关联起来。如下:
struct fs_struct {
int users;
spinlock_t lock;
seqcount_t seq;
int umask;
int in_exec;
struct path root, pwd; //进程当前的工作目录,和它自已的根目录。
/*
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
}; */
};
进程利用files_struct结构体与打开的文件对象进行关联,如下:
/*
* Open file table structure
*/
struct files_struct {
atomic_t count;
struct fdtable __rcu *fdt; //文件描述符表指针,指向fdtab
struct fdtable fdtab; //文件描述符表
spinlock_t file_lock ____cacheline_aligned_in_smp; //文件锁
int next_fd; // 下一个分配的文件描述符:所分配的文件描述符加1,或者是释放的文件描述符。
//注:next_fd 并不一定是指下一个可分配的文件描述符啊!!下面再会再继续说明它的作用!
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT]; //文件对象指针数组,
//这里存放的就是文件对象指针,由文件描述符fd作为索引
//index查找文件对象,数组大小默认32
};
struct fdtable {
unsigned int max_fds; //文件对象最大数,默认32。如果超过32,
//重新申请分配一个更大的文件对象指针数组fd_array, 并更新max_fds
struct file __rcu **fd; //指向文件对象指针数组的指针,即指向 fd_array
unsigned long *close_on_exec;
unsigned long *open_fds; //指向打开开文件上描述符的指针
struct rcu_head rcu;
struct fdtable *next;
};
下面举为init task进程定义的全局init_files,说明该结构体中几个指针的初始化:
struct files_struct init_files = {
.count = ATOMIC_INIT(1),
.fdt = &init_files.fdtab, //指向fdtab
.fdtab = {
.max_fds = NR_OPEN_DEFAULT,
.fd = &init_files.fd_array[0], //指向fd_array
.close_on_exec= init_files.close_on_exec_init,
.open_fds = init_files.open_fds_init, //指向open_fds_init
},
.file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock),
};
注:其他进程的files_struct应该是fork时copyinit_files而来。看如下调用关系:
do_fork=》copy_process=》copy_files=》dup_fd
三、重点参考代码文件及函数注释
/kernel/fs/file.c
/*分配一个文件描述符fd : start从哪开始,一般是0 */
int alloc_fd(unsigned start, unsigned flags)
{
struct files_struct *files = current->files; //当前进程中获取结构体files_struct数据
unsigned int fd;
int error;
struct fdtable *fdt;
spin_lock(&files->file_lock); //锁住files_struct
repeat:
fdt = files_fdtable(files); //取出files_struct中的fdt指针
fd = start; //一般这时fd=start=0;
if (fd < files->next_fd)
fd = files->next_fd; //新的fd先设置为下一个分配的文件描述符:next_fd,
if (fd < fdt->max_fds) //如果next_fd仍然小于的最大分配数目max_fds。
fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, fd);// 从next_fd到max_fds开始找open_fds为0的bit
//注:如果next_fd仍然小于的最大分配数目max_fds, 则肯定可以找到一个未分配的fd,否则说明上次fd已经分配到max_fds,下面就不要再查了,要进行扩展。
//
error = expand_files(files, fd); //判断是否要进行扩展fdtable,或进行fdtable扩展
/* 注:expand_files函数里面通过下面这句话对前面已经设置、查找到的fd与max_fds进行比较,
确定是否需要进行扩展!
/* Do we need to expand? */
if (nr < fdt->max_fds)
return 0;
*/
/*
* If we needed to expand the fs array we
* might have blocked - try again.
*/
if (error) //如果expand_files返非0,说明进行了fdtable扩展,需要再进行确认一遍。
goto repeat;
if (start <= files->next_fd)
files->next_fd = fd + 1; //fd已经分配成功,更新下一个分配描述符next_fd 。
// 如果next_fd 小于max_fds,则next_fd指下一个可分配的fd。
//如果next_fd已经等于max_fds,则next_fd并不是可分配的fd。
// close 系统调用关闭文件时会回收文件描述符fd,因此next_fd位置可能是0--max_fds之间任意位置。
//close回收文件对象占用的fd的代码如下:(fs/open.c)
static void __put_unused_fd(struct files_struct *files, unsigned int fd)
{
struct fdtable *fdt = files_fdtable(files);
__clear_open_fd(fd, fdt);
if (fd < files->next_fd) //如果fd小于next_fd,则更新next_fd。
files->next_fd = fd; //next_fd 的位置可能0--max_fds随机啦!
}
*/
__set_open_fd(fd, fdt); //更新fd在open_fds所对应的bit。
error = fd; //new fd
out:
spin_unlock(&files->file_lock); //释放锁
return error; //最后返回新分配的fd
}
好了,有函数alloc_fd的注释,看一下面几个用到的函数就很容易理解了。
int expand_files(struct files_struct *files, int nr)
static int expand_fdtable(struct files_struct *files, int nr)
static struct fdtable * alloc_fdtable(unsigned int nr)
struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
注:使用dup(), dup2(), fcntl() 两个文件描述符可以指向同一个打开的文件对象。即:
数组fd_array的两个元素可能指向同一个文件对象。下面看看dup是如何实现的:
fs/fcntl.c
SYSCALL_DEFINE1(dup, unsigned int, fildes)
{
int ret = -EBADF;
struct file *file = fget_raw(fildes); //通过fd查到从fdtable中查找已有的文件对象file
if (file) {
ret = get_unused_fd(); //重新申请一个新的文件描述符fd
if (ret >= 0)
fd_install(ret, file); // 当前进程的文件对象file与新的fd关联起来。
else
fput(file);
}
return ret;
}
/kernel/fs/file_table.c
void __init files_init(unsigned long mempages)
struct file *fget(unsigned int fd)
void fput(struct file *file)
struct file *alloc_file(struct path *path, fmode_t mode,const struct file_operations *fop)
struct file *get_empty_filp(void)
好啦,相信这些对理解VFS是非常有意义的!