linux定时器王贤才,Linux虚拟文件系统--open()

open()系统调用用来打开一个文件,本文就VFS层,对open系统调用的过程进行一个简单的分析。

SYSCALL_DEFINE3(open,constchar__user *, filename,int, flags,int, mode)

{

longret;

if(force_o_largefile())

flags |= O_LARGEFILE;

ret = do_sys_open(AT_FDCWD, filename, flags, mode);

/* avoid REGPARM breakage on x86: */

asmlinkage_protect(3, ret, filename, flags, mode);

returnret;

}

force_o_largefile()用来判断系统是否为32位的,如果不是32位,也就是说为64位,则将O_LARGEFILE置位,主体工作由do_sys_open()来做

longdo_sys_open(intdfd,constchar__user *filename,intflags,intmode)

{

char*tmp = getname(filename);//拷贝文件名字符串到内核空间

intfd = PTR_ERR(tmp);

if(!IS_ERR(tmp)) {

fd = get_unused_fd_flags(flags);//为文件分配一个文件描述符

if(fd >= 0) {

//实际的OPEN操作处理

structfile *f = do_filp_open(dfd, tmp, flags, mode, 0);

if(IS_ERR(f)) {

put_unused_fd(fd);

fd = PTR_ERR(f);

} else{

fsnotify_open(f->f_path.dentry);

fd_install(fd, f);

}

}

putname(tmp);

}

returnfd;

}

open操作是特定于某个进程进行的,因此涉及到了VFS中特定于进程的结构,这里简单的介绍下

"font-size:12px;">structfiles_struct {

/*

* read mostly part

*/

atomic_t count;

structfdtable *fdt;

structfdtable fdtab;

/*

* written part on a separate cache line in SMP

*/

spinlock_t file_lock ____cacheline_aligned_in_smp;

intnext_fd;

structembedded_fd_set close_on_exec_init;

structembedded_fd_set open_fds_init;

structfile * fd_array[NR_OPEN_DEFAULT];

};

count表示共享该结构的进程数

fdtable是该进程的文件描述符数组

fdt指向fdtable

next_fd表示最大文件描述符号+1

embedded_fd_set是一个位图结构,用来标记文件描述符,close_on_exec_init用来标记那些执行exec时要关闭的文件的文件描述符,open_fds_init用来标记已经分配出去了的文件描述符

fd_array用来存储进程打开的文件的struct file指针

do_sys_open()的一个重要任务就是调用get_unused_fd_flags()为即将打开的文件分配一个文件描述符

"font-size:12px;">#define get_unused_fd_flags(flags) alloc_fd(0, (flags))

"font-size:12px;">intalloc_fd(unsigned start, unsigned flags)

{

structfiles_struct *files = current->files;//获取当前进程的files_struct

unsigned intfd;

interror;

structfdtable *fdt;

spin_lock(&files->file_lock);

repeat:

fdt = files_fdtable(files);//获取进程的fdtable

fd = start;

if(fd next_fd)

fd = files->next_fd;

if(fd max_fds)

fd = find_next_zero_bit(fdt->open_fds->fds_bits,

fdt->max_fds, fd);//从位图中获取一个空闲位

error = expand_files(files, fd);//这里根据需要扩充文件描述符数组

if(error

gotoout;

/*

* If we needed to expand the fs array we

* might have blocked - try again.

*/

if(error)//之前进行了扩充操作,重新进行一次空闲bit的搜索

gotorepeat;

if(start <= files->next_fd)

files->next_fd = fd + 1;

FD_SET(fd, fdt->open_fds);//在open_fds的位图上置位

if(flags & O_CLOEXEC)//如果设定了O_CLOEXEC,则在close_on_exec位图上将相应位置位

FD_SET(fd, fdt->close_on_exec);

else

FD_CLR(fd, fdt->close_on_exec);

error = fd;

#if 1

/* Sanity check */

if(rcu_dereference(fdt->fd[fd]) != NULL) {

printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);

rcu_assign_pointer(fdt->fd[fd], NULL);

}

#endif

out:

spin_unlock(&files->file_lock);

returnerror;

}

intexpand_files(structfiles_struct *files,intnr)

{

structfdtable *fdt;

fdt = files_fdtable(files);

/*

* N.B. For clone tasks sharing a files structure, this test

* will limit the total number of files that can be opened.

*/

/*如果nr大于进程允许的最大打开文件数,则返回错误*/

if(nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)

return-EMFILE;

/*nr小于最大文件描述符,则不用进行fdtable的扩展,直接返回*/

if(nr max_fds)

return0;

/*扩展的话不能超过sysctl_nr_opend的上限*/

if(nr >= sysctl_nr_open)

return-EMFILE;

/* 到这里表示确实需要进行扩充,进行实际的扩展操作 */

returnexpand_fdtable(files, nr);

}

实际的扩充操作:

"font-size:12px;">staticintexpand_fdtable(structfiles_struct *files,intnr)

__releases(files->file_lock)

__acquires(files->file_lock)

{

structfdtable *new_fdt, *cur_fdt;

spin_unlock(&files->file_lock);

new_fdt = alloc_fdtable(nr);//根据nr重新创建一个新的fdtable

spin_lock(&files->file_lock);

if(!new_fdt)

return-ENOMEM;

/*

* extremely unlikely race - sysctl_nr_open decreased between the check in

* caller and alloc_fdtable().  Cheaper to catch it here...

*/

/*这里为了防止因为竞争,在alloc_fdtable调用之前systl_nr_open减小了新创建的fdtable小于nr*/

if(unlikely(new_fdt->max_fds <= nr)) {

free_fdarr(new_fdt);

free_fdset(new_fdt);

kfree(new_fdt);

return-EMFILE;

}

/*

* Check again since another task may have expanded the fd table while

* we dropped the lock

*/

cur_fdt = files_fdtable(files);//获取旧的fdtable

if(nr >= cur_fdt->max_fds) {//新的nr必须大于旧的fdtable的大小

/* Continue as planned */

copy_fdtable(new_fdt, cur_fdt);//将旧的fdtable中的内容拷贝至新的fdtable

rcu_assign_pointer(files->fdt, new_fdt);//用新的fdtable替换旧的fdtable

if(cur_fdt->max_fds > NR_OPEN_DEFAULT)

free_fdtable(cur_fdt);//释放旧的fdtable

} else{

/* Somebody else expanded, so undo our attempt */

free_fdarr(new_fdt);

free_fdset(new_fdt);

kfree(new_fdt);

}

return1;

}

到此为止,分配新的fd的工作完成,如果分配fd成功,接下来do_sys_open()就要通过do_filp_open()函数查找文件并执行相应的打开操作

do_filp_open的工作针对两种情况进行:

1.flag中未标识O_CREAT,也就是只进行单纯的搜索打开,如果没有搜索到目标文件的话,不会进行创建,这种情况处理起来比较简单,主要工作就是通过路径解析来查找文件,查找到了的话再根据文件系统定义的open方式进行打开

2.flag中标识了O_CREAT,也就是说如果没找到目标文件要进行创建。这种情况要先查找目标文件的父目录(通过将LOOKUP_PARENT标识置位然后进行路径解析来实现),因为假如没查找到目标文件的话,创建工作需要在父目录下完成;然后再查找最后一个文件分量,也就是目标文件,并进行打开操作,其中涉及到的许多部分在前面几篇文章中也都已经分析过了

"font-size:12px;">structfile *do_filp_open(intdfd,constchar*pathname,

intopen_flag,intmode,intacc_mode)

{

structfile *filp;

structnameidata nd;

interror;

structpath path;

structdentry *dir;

intcount = 0;

intwill_write;

intflag = open_to_namei_flags(open_flag);

if(!acc_mode)

acc_mode = MAY_OPEN | ACC_MODE(flag);

/* O_TRUNC implies we need access checks for write permissions */

if(flag & O_TRUNC)

acc_mode |= MAY_WRITE;

/* Allow the LSM permission hook to distinguish append

access from general write access. */

if(flag & O_APPEND)

acc_mode |= MAY_APPEND;

/*

* The simplest case - just a plain lookup.

*/

/*如果没有设置O_CREAT,则在未找到文件的情况下不用创建文件,直接通过查找来打开文件*/

if(!(flag & O_CREAT)) {

error = path_lookup_open(dfd, pathname, lookup_flags(flag),

&nd, flag);

if(error)

returnERR_PTR(error);

gotook;//成功查找到了目标文件的话,就跳转到ok去执行后续操作

}

/*

* Create - we need to know the parent.

*/

/*如果需要creat,那么就要知道目标文件的父目录,因此需要设置LOOKUP_PARENT标识*/

error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);

if(error)

returnERR_PTR(error);

/*进行路径名的解析,父目录将保存到nd中*/

error = path_walk(pathname, &nd);

if(error) {

if(nd.root.mnt)

path_put(&nd.root);

returnERR_PTR(error);

}

if(unlikely(!audit_dummy_context()))

audit_inode(pathname, nd.path.dentry);

/*

* We have the parent and last component. First of all, check

* that we are not asked to creat(2) an obvious directory - that

* will not do.

*/

error = -EISDIR;

/*这里要先保证路径名的最后一个分量是普通文件名(不为.和..),并且长度不为0*/

if(nd.last_type != LAST_NORM || nd.last.name[nd.last.len])

gotoexit_parent;

error = -ENFILE;

filp = get_empty_filp();//分配一个struct file

if(filp == NULL)

gotoexit_parent;

/*将打开文件的信息保存在nd.intent中*/

nd.intent.open.file = filp;

nd.intent.open.flags = flag;

nd.intent.open.create_mode = mode;

dir = nd.path.dentry;//获取父目录

nd.flags &= ~LOOKUP_PARENT;//取消LOOKUP_PARENT标识

nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;//设置CREATE和OPEN标识

if(flag & O_EXCL)

nd.flags |= LOOKUP_EXCL;

mutex_lock(&dir->d_inode->i_mutex);

//lookup_hash进行最终分量的查找,先查找dentry缓存,没找到的话再通过特定于文件系统的lookup方式从磁盘查找

path.dentry = lookup_hash(&nd);

path.mnt = nd.path.mnt;

do_last:

error = PTR_ERR(path.dentry);//检查目标dentry是否有效

if(IS_ERR(path.dentry)) {

mutex_unlock(&dir->d_inode->i_mutex);

gotoexit;

}

if(IS_ERR(nd.intent.open.file)) {//检查file是否有效

error = PTR_ERR(nd.intent.open.file);

gotoexit_mutex_unlock;

}

/* Negative dentry, just create the file */

if(!path.dentry->d_inode) {//dentry没有对应上inode,创建之,可能的情况就是该文件被删除了

/*

* This write is needed to ensure that a

* ro->rw transition does not occur between

* the time when the file is created and when

* a permanent write count is taken through

* the 'struct file' in nameidata_to_filp().

*/

error = mnt_want_write(nd.path.mnt);

if(error)

gotoexit_mutex_unlock;

/*__open_namei_create将会调用到父目录所属文件系统中定义的create方式创建文件*/

error = __open_namei_create(&nd, &path, flag, mode);

if(error) {

mnt_drop_write(nd.path.mnt);

gotoexit;

}

/*nameidata_to_filp将会调用目标文件的inode对应的open函数进行打开操作*/

filp = nameidata_to_filp(&nd, open_flag);

if(IS_ERR(filp))

ima_counts_put(&nd.path,

acc_mode & (MAY_READ | MAY_WRITE |

MAY_EXEC));

mnt_drop_write(nd.path.mnt);

if(nd.root.mnt)

path_put(&nd.root);

returnfilp;

}

/*

* 下面的情况对应目标文件存在

*/

mutex_unlock(&dir->d_inode->i_mutex);

audit_inode(pathname, path.dentry);

error = -EEXIST;

if(flag & O_EXCL)

gotoexit_dput;

/*下面要做一些必要的检查*/

if(__follow_mount(&path)) {//检测目标对象上是否挂载了文件系统

error = -ELOOP;

if(flag & O_NOFOLLOW)

gotoexit_dput;

}

error = -ENOENT;

if(!path.dentry->d_inode)//检测目标对象的inode是否存在

gotoexit_dput;

if(path.dentry->d_inode->i_op->follow_link)//检测目标对象是否为链接文件

gotodo_link;

/*检查OK,将path保存至nd*/

path_to_nameidata(&path, &nd);

error = -EISDIR;

if(path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))

gotoexit;

ok:

/*

* Consider:

* 1. may_open() truncates a file

* 2. a rw->ro mount transition occurs

* 3. nameidata_to_filp() fails due to

*    the ro mount.

* That would be inconsistent, and should

* be avoided. Taking this mnt write here

* ensures that (2) can not occur.

*/

will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);

if(will_write) {

error = mnt_want_write(nd.path.mnt);

if(error)

gotoexit;

}

/*may_open()会做一些检测*/

error = may_open(&nd.path, acc_mode, flag);

if(error) {

if(will_write)

mnt_drop_write(nd.path.mnt);

gotoexit;

}

//执行文件系统定义的打开操作,并保存信息至filp

filp = nameidata_to_filp(&nd, open_flag);

if(IS_ERR(filp))

ima_counts_put(&nd.path,

acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));

/*

* It is now safe to drop the mnt write

* because the filp has had a write taken

* on its behalf.

*/

if(will_write)

mnt_drop_write(nd.path.mnt);

if(nd.root.mnt)

path_put(&nd.root);

returnfilp;

exit_mutex_unlock:

mutex_unlock(&dir->d_inode->i_mutex);

exit_dput:

path_put_conditional(&path, &nd);

exit:

if(!IS_ERR(nd.intent.open.file))

release_open_intent(&nd);

exit_parent:

if(nd.root.mnt)

path_put(&nd.root);

path_put(&nd.path);

returnERR_PTR(error);

do_link://目标文件为符号链接的处理,前文已经分析过

error = -ELOOP;

if(flag & O_NOFOLLOW)

gotoexit_dput;

/*

* This is subtle. Instead of calling do_follow_link() we do the

* thing by hands. The reason is that this way we have zero link_count

* and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.

* After that we have the parent and last component, i.e.

* we are in the same situation as after the first path_walk().

* Well, almost - if the last component is normal we get its copy

* stored in nd->last.name and we will have to putname() it when we

* are done. Procfs-like symlinks just set LAST_BIND.

*/

nd.flags |= LOOKUP_PARENT;

error = security_inode_follow_link(path.dentry, &nd);

if(error)

gotoexit_dput;

error = __do_follow_link(&path, &nd);

if(error) {

/* Does someone understand code flow here? Or it is only

* me so stupid? Anathema to whoever designed this non-sense

* with "intent.open".

*/

release_open_intent(&nd);

if(nd.root.mnt)

path_put(&nd.root);

returnERR_PTR(error);

}

nd.flags &= ~LOOKUP_PARENT;

if(nd.last_type == LAST_BIND)

gotook;

error = -EISDIR;

if(nd.last_type != LAST_NORM)

gotoexit;

if(nd.last.name[nd.last.len]) {

__putname(nd.last.name);

gotoexit;

}

error = -ELOOP;

if(count++==32) {

__putname(nd.last.name);

gotoexit;

}

dir = nd.path.dentry;

mutex_lock(&dir->d_inode->i_mutex);

path.dentry = lookup_hash(&nd);

path.mnt = nd.path.mnt;

__putname(nd.last.name);

gotodo_last;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值