Linux VFS 之 open系统调用(kernel 3.4)

来源:互联网 发布:2016nba新秀弹跳数据 编辑:程序博客网 时间:2024/04/28 00:43

linux version: 3.4.67

kernel 代码

fs/open.c

fs/namei.c

open命令调用关系

mount- > /fs/open.c/SYSCALL_DEFINE3(open, ....); -> do_sys_open->get_unused_fd_flags->

->do_filp_open->fd_install

根据调用关系对各函数注解

1、do_sys_open

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
int lookup = build_open_flags(flags, mode, &op);
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
fd = get_unused_fd_flags(flags);   //获取一个未使用的文件描述符,参考fs/file.c文件alloc_fd() ,后续仔细研究file.c文件。
if (fd >= 0) {
struct file *f = do_filp_open(dfd, tmp, &op, lookup);  //直接调用path_openat打开文件,返回file文件对象。
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);  //file文件对象与当前进程current关联。fd作为file对象的索引,这样file与fd也一一对应。
}
}
putname(tmp);
}
return fd;  //返回文件描述符
}


2、path_openat
static struct file *path_openat(int dfd, const char *pathname,
struct nameidata *nd, const struct open_flags *op, int flags)
{
struct file *base = NULL;
struct file *filp;
struct path path;
int error;
//申请文件对象file结构体内存批针
filp = get_empty_filp();

if (!filp)
return ERR_PTR(-ENFILE);
filp->f_flags = op->open_flag;     //保存open flag
nd->intent.open.file = filp;       //将文件对象到nd->intent.open.file,后面就直接从这儿拿文件对象指针

nd->intent.open.flags = open_to_namei_flags(op->open_flag);
nd->intent.open.create_mode = op->mode;

error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);   //初始化nd
if (unlikely(error))
goto out_filp;

current->total_link_count = 0;
error = link_path_walk(pathname, nd);//查找到打开文件所在目录的目录项对象dentry和vfsmount,保存到nd。
 后面仔细研研path lookup的方法namei.c


filp = do_last(nd, &path, op, pathname);   //  调用do_last继续做其它工作
........

return filp; 
}

3、do_last

static struct file *do_last(struct nameidata *nd, struct path *path,
   const struct open_flags *op, const char *pathname)
{
struct dentry *dir = nd->path.dentry;  //所在目录的目录项对象dentry
struct dentry *dentry;
int open_flag = op->open_flag;
int will_truncate = open_flag & O_TRUNC;
int want_write = 0;
int acc_mode = op->acc_mode;
struct file *filp;
int error;

nd->flags &= ~LOOKUP_PARENT;
nd->flags |= op->intent;
  ..........
dentry = lookup_hash(nd); //基于文件所在目录的nd,从目录项缓存中查找要打开文件的目录项对象dentry。
//如果缓存查找不到,放心会新创建一个新的目录项对项
path->dentry = dentry;
path->mnt = nd->path.mnt;


/* Negative dentry, just create the file */
if (!dentry->d_inode) {  //如果文件dentry对应的inode为空,则需要为它创建inode
umode_t mode = op->mode;
  。。。。。。。
error = vfs_create(dir->d_inode, dentry, mode, nd);  //利用所在目录的目录项对象的inode operation为相关文件创建新inode 。注:这里dentry与inode就关联起来了。

nd->path.dentry = dentry;  //建文件的目录项对象保存到nd->path->dentry
goto common;  // 跑到common 继续~~

}

common:

filp = nameidata_to_filp(nd);  //将nameidata相关项赋值给struct file对象。

return filp;


4、nameidata_to_filp
struct file *nameidata_to_filp(struct nameidata *nd)
{
const struct cred *cred = current_cred();
struct file *filp;


/* Pick up the filp from the open intent */
filp = nd->intent.open.file;     //获取函数path_openat申请并保存到nameidata的file对象
nd->intent.open.file = NULL;


// 文件对象是在path_openat函数申请及一些简单初始化,这儿先判断filp->f_path.dentry 是否为空
//注:虽然前面do_last函数将file对象相关的dentry和inode已经创建好了,但并未与file对象关联起来。
//file对象的很多其它重要成员也都没有初始化!
//接下来__dentry_open就要做这些事情了!
/* Has the filesystem initialised the file for us? */
if (filp->f_path.dentry == NULL) {
path_get(&nd->path);
filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,  // file对象初始化,包括file与dentry关联工作
    NULL, cred);

}
return filp;
}

6、__dentry_open
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
static const struct file_operations empty_fops = {};
struct inode *inode;
int error;

f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |        //初始化打开mode
FMODE_PREAD | FMODE_PWRITE;

if (unlikely(f->f_flags & O_PATH))
f->f_mode = FMODE_PATH;    //打开的是目录
。。。。。。。

inode = dentry->d_inode;      //从dentry中获取inode。 dentry和inode在前面的do_last函数已经关联过的。

f->f_mapping = inode->i_mapping;   //地址空间map
f->f_path.dentry = dentry;             // 注: 就在这儿file对象与目录象对象关联起来啦!!
f->f_path.mnt = mnt;                   // file对象与挂载点对象关联起来啦。

f->f_pos = 0;                                //初始文件批针位置为0.
file_sb_list_add(f, inode->i_sb);             // add a file to the sb's file list


if (unlikely(f->f_mode & FMODE_PATH)) {
f->f_op = &empty_fops;          //打开目录的话,file operation为空了,直接返回了。
return f;
}

       //注: 在这儿file对象的file_operations初始化好了。
f->f_op = fops_get(inode->i_fop);    //inode节点在初始化的时候已经赋值了i_fop,现在将文件操作赋值给f_op

       // 通过file对象的file_operations->open指针,调用到特定文件系统的open函数,打开特定的设备或文件啦!
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
。。。。。。。。
/* NB: we're sure to have correct a_ops only after f_op->open */
if (f->f_flags & O_DIRECT) {
if (!f->f_mapping->a_ops ||
   ((!f->f_mapping->a_ops->direct_IO) &&
   (!f->f_mapping->a_ops->get_xip_mem))) {
fput(f);
f = ERR_PTR(-EINVAL);
}
}


return f;     //至此file对象已经初始化完成、并且调用特定文件系统的open方法
}


7、fd_install

将进程的current->files对象与file文件对象进行

void fd_install(unsigned int fd, struct file *file)
{
struct files_struct *files = current->files;   //获取当前进程打开的文件表批针
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);        //获取当前进程的文件描述符表指针  
BUG_ON(fdt->fd[fd] != NULL);
rcu_assign_pointer(fdt->fd[fd], file);     //将fdt->fd[fd]指向file对象,这样文件对象与进程关联上了。 fd作为file对象的索引。这样file与fd也一一对应
spin_unlock(&files->file_lock);
}


fs/open.c 提供的内核常用方法

//面向kernel的文件open相关操作接口。注:返回值都是file对象,而非fd文件描述符。
filp_open
file_open_root
filp_close
dentry_open




参考:

http://blog.csdn.net/chenjin_zhong/article/details/8452453

http://blog.csdn.net/chenjin_zhong/article/details/8452487

1 0
原创粉丝点击