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