Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1177160
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-03 19:27:29

Linux虚拟文件系统--open()

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

  1. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)  
  2. {  
  3.     long ret;  
  4.   
  5.     if (force_o_largefile())  
  6.         flags |= O_LARGEFILE;  
  7.   
  8.     ret = do_sys_open(AT_FDCWD, filename, flags, mode);  
  9.     /* avoid REGPARM breakage on x86: */  
  10.     asmlinkage_protect(3, ret, filename, flags, mode);  
  11.     return ret;  
  12. }  

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

  1. long do_sys_open(int dfd, const char __user *filename, int flags, int mode)  
  2. {  
  3.     char *tmp = getname(filename);//拷贝文件名字符串到内核空间  
  4.     int fd = PTR_ERR(tmp);  
  5.   
  6.     if (!IS_ERR(tmp)) {  
  7.         fd = get_unused_fd_flags(flags);//为文件分配一个文件描述符  
  8.         if (fd >= 0) {  
  9.             //实际的OPEN操作处理  
  10.             struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);  
  11.             if (IS_ERR(f)) {  
  12.                 put_unused_fd(fd);  
  13.                 fd = PTR_ERR(f);  
  14.             } else {  
  15.                 fsnotify_open(f->f_path.dentry);  
  16.                 fd_install(fd, f);  
  17.             }  
  18.         }  
  19.         putname(tmp);  
  20.     }  
  21.     return fd;  
  22. }  


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

  1. "font-size:12px;">struct files_struct {  
  2.   /* 
  3.    * read mostly part 
  4.    */  
  5.     atomic_t count;  
  6.     struct fdtable *fdt;  
  7.     struct fdtable fdtab;  
  8.   /* 
  9.    * written part on a separate cache line in SMP 
  10.    */  
  11.     spinlock_t file_lock ____cacheline_aligned_in_smp;  
  12.     int next_fd;  
  13.     struct embedded_fd_set close_on_exec_init;  
  14.     struct embedded_fd_set open_fds_init;  
  15.     struct file * fd_array[NR_OPEN_DEFAULT];  
  16. };  

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()为即将打开的文件分配一个文件描述符

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


  1. "font-size:12px;">int alloc_fd(unsigned start, unsigned flags)  
  2. {  
  3.     struct files_struct *files = current->files;//获取当前进程的files_struct  
  4.     unsigned int fd;  
  5.     int error;  
  6.     struct fdtable *fdt;  
  7.   
  8.     spin_lock(&files->file_lock);  
  9. repeat:  
  10.     fdt = files_fdtable(files);//获取进程的fdtable  
  11.     fd = start;  
  12.     if (fd < files->next_fd)  
  13.         fd = files->next_fd;  
  14.   
  15.     if (fd < fdt->max_fds)  
  16.         fd = find_next_zero_bit(fdt->open_fds->fds_bits,  
  17.                        fdt->max_fds, fd);//从位图中获取一个空闲位  
  18.   
  19.     error = expand_files(files, fd);//这里根据需要扩充文件描述符数组  
  20.     if (error < 0)  
  21.         goto out;  
  22.   
  23.     /* 
  24.      * If we needed to expand the fs array we 
  25.      * might have blocked - try again. 
  26.      */  
  27.     if (error)//之前进行了扩充操作,重新进行一次空闲bit的搜索  
  28.         goto repeat;  
  29.   
  30.     if (start <= files->next_fd)  
  31.         files->next_fd = fd + 1;  
  32.   
  33.     FD_SET(fd, fdt->open_fds);//在open_fds的位图上置位  
  34.     if (flags & O_CLOEXEC)//如果设定了O_CLOEXEC,则在close_on_exec位图上将相应位置位  
  35.         FD_SET(fd, fdt->close_on_exec);  
  36.     else  
  37.         FD_CLR(fd, fdt->close_on_exec);  
  38.     error = fd;  
  39. #if 1  
  40.     /* Sanity check */  
  41.     if (rcu_dereference(fdt->fd[fd]) != NULL) {  
  42.         printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);  
  43.         rcu_assign_pointer(fdt->fd[fd], NULL);  
  44.     }  
  45. #endif  
  46.   
  47. out:  
  48.     spin_unlock(&files->file_lock);  
  49.     return error;  
  50. }  
  51.   


  1. int expand_files(struct files_struct *files, int nr)  
  2. {  
  3.     struct fdtable *fdt;  
  4.   
  5.     fdt = files_fdtable(files);  
  6.   
  7.     /* 
  8.      * N.B. For clone tasks sharing a files structure, this test 
  9.      * will limit the total number of files that can be opened. 
  10.      */  
  11.      /*如果nr大于进程允许的最大打开文件数,则返回错误*/  
  12.     if (nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)  
  13.         return -EMFILE;  
  14.   
  15.     /*nr小于最大文件描述符,则不用进行fdtable的扩展,直接返回*/  
  16.     if (nr < fdt->max_fds)  
  17.         return 0;  
  18.   
  19.     /*扩展的话不能超过sysctl_nr_opend的上限*/  
  20.     if (nr >= sysctl_nr_open)  
  21.         return -EMFILE;  
  22.   
  23.     /* 到这里表示确实需要进行扩充,进行实际的扩展操作 */  
  24.     return expand_fdtable(files, nr);  
  25. }  


实际的扩充操作:

  1. "font-size:12px;">static int expand_fdtable(struct files_struct *files, int nr)  
  2.     __releases(files->file_lock)  
  3.     __acquires(files->file_lock)  
  4. {  
  5.     struct fdtable *new_fdt, *cur_fdt;  
  6.   
  7.     spin_unlock(&files->file_lock);  
  8.     new_fdt = alloc_fdtable(nr);//根据nr重新创建一个新的fdtable  
  9.     spin_lock(&files->file_lock);  
  10.     if (!new_fdt)  
  11.         return -ENOMEM;  
  12.     /* 
  13.      * extremely unlikely race - sysctl_nr_open decreased between the check in 
  14.      * caller and alloc_fdtable().  Cheaper to catch it here... 
  15.      */  
  16.      /*这里为了防止因为竞争,在alloc_fdtable调用之前systl_nr_open减小了新创建的fdtable小于nr*/  
  17.     if (unlikely(new_fdt->max_fds <= nr)) {  
  18.         free_fdarr(new_fdt);  
  19.         free_fdset(new_fdt);  
  20.         kfree(new_fdt);  
  21.         return -EMFILE;  
  22.     }  
  23.     /* 
  24.      * Check again since another task may have expanded the fd table while 
  25.      * we dropped the lock 
  26.      */  
  27.     cur_fdt = files_fdtable(files);//获取旧的fdtable  
  28.     if (nr >= cur_fdt->max_fds) {//新的nr必须大于旧的fdtable的大小  
  29.         /* Continue as planned */  
  30.         copy_fdtable(new_fdt, cur_fdt);//将旧的fdtable中的内容拷贝至新的fdtable  
  31.         rcu_assign_pointer(files->fdt, new_fdt);//用新的fdtable替换旧的fdtable  
  32.         if (cur_fdt->max_fds > NR_OPEN_DEFAULT)  
  33.             free_fdtable(cur_fdt);//释放旧的fdtable  
  34.     } else {  
  35.         /* Somebody else expanded, so undo our attempt */  
  36.         free_fdarr(new_fdt);  
  37.         free_fdset(new_fdt);  
  38.         kfree(new_fdt);  
  39.     }  
  40.     return 1;  
  41. }  


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

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

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

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

  1. "font-size:12px;">struct file *do_filp_open(int dfd, const char *pathname,  
  2.         int open_flag, int mode, int acc_mode)  
  3. {  
  4.     struct file *filp;  
  5.     struct nameidata nd;  
  6.     int error;  
  7.     struct path path;  
  8.     struct dentry *dir;  
  9.     int count = 0;  
  10.     int will_write;  
  11.     int flag = open_to_namei_flags(open_flag);  
  12.   
  13.     if (!acc_mode)  
  14.         acc_mode = MAY_OPEN | ACC_MODE(flag);  
  15.   
  16.     /* O_TRUNC implies we need access checks for write permissions */  
  17.     if (flag & O_TRUNC)  
  18.         acc_mode |= MAY_WRITE;  
  19.   
  20.     /* Allow the LSM permission hook to distinguish append  
  21.        access from general write access. */  
  22.     if (flag & O_APPEND)  
  23.         acc_mode |= MAY_APPEND;  
  24.   
  25.     /* 
  26.      * The simplest case - just a plain lookup. 
  27.      */  
  28.   
  29.     /*如果没有设置O_CREAT,则在未找到文件的情况下不用创建文件,直接通过查找来打开文件*/  
  30.     if (!(flag & O_CREAT)) {  
  31.         error = path_lookup_open(dfd, pathname, lookup_flags(flag),  
  32.                      &nd, flag);  
  33.         if (error)  
  34.             return ERR_PTR(error);  
  35.         goto ok;  //成功查找到了目标文件的话,就跳转到ok去执行后续操作  
  36.     }  
  37.   
  38.     /* 
  39.      * Create - we need to know the parent. 
  40.      */  
  41.      /*如果需要creat,那么就要知道目标文件的父目录,因此需要设置LOOKUP_PARENT标识*/  
  42.     error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);  
  43.     if (error)  
  44.         return ERR_PTR(error);  
  45.     /*进行路径名的解析,父目录将保存到nd中*/  
  46.     error = path_walk(pathname, &nd);  
  47.     if (error) {  
  48.         if (nd.root.mnt)  
  49.             path_put(&nd.root);  
  50.         return ERR_PTR(error);  
  51.     }  
  52.     if (unlikely(!audit_dummy_context()))  
  53.         audit_inode(pathname, nd.path.dentry);  
  54.   
  55.     /* 
  56.      * We have the parent and last component. First of all, check 
  57.      * that we are not asked to creat(2) an obvious directory - that 
  58.      * will not do. 
  59.      */  
  60.     error = -EISDIR;  
  61.   
  62.     /*这里要先保证路径名的最后一个分量是普通文件名(不为.和..),并且长度不为0*/  
  63.     if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])  
  64.         goto exit_parent;  
  65.   
  66.     error = -ENFILE;  
  67.     filp = get_empty_filp();//分配一个struct file  
  68.     if (filp == NULL)  
  69.         goto exit_parent;  
  70.     /*将打开文件的信息保存在nd.intent中*/  
  71.     nd.intent.open.file = filp;  
  72.     nd.intent.open.flags = flag;  
  73.     nd.intent.open.create_mode = mode;  
  74.     dir = nd.path.dentry;//获取父目录  
  75.     nd.flags &= ~LOOKUP_PARENT;//取消LOOKUP_PARENT标识  
  76.     nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;//设置CREATE和OPEN标识  
  77.     if (flag & O_EXCL)  
  78.         nd.flags |= LOOKUP_EXCL;  
  79.     mutex_lock(&dir->d_inode->i_mutex);  
  80.   
  81.     //lookup_hash进行最终分量的查找,先查找dentry缓存,没找到的话再通过特定于文件系统的lookup方式从磁盘查找  
  82.     path.dentry = lookup_hash(&nd);  
  83.     path.mnt = nd.path.mnt;  
  84.   
  85. do_last:  
  86.     error = PTR_ERR(path.dentry);//检查目标dentry是否有效  
  87.     if (IS_ERR(path.dentry)) {  
  88.         mutex_unlock(&dir->d_inode->i_mutex);  
  89.         goto exit;  
  90.     }  
  91.   
  92.     if (IS_ERR(nd.intent.open.file)) {//检查file是否有效  
  93.         error = PTR_ERR(nd.intent.open.file);  
  94.         goto exit_mutex_unlock;  
  95.     }  
  96.   
  97.     /* Negative dentry, just create the file */  
  98.     if (!path.dentry->d_inode) {//dentry没有对应上inode,创建之,可能的情况就是该文件被删除了  
  99.         /* 
  100.          * This write is needed to ensure that a 
  101.          * ro->rw transition does not occur between 
  102.          * the time when the file is created and when 
  103.          * a permanent write count is taken through 
  104.          * the 'struct file' in nameidata_to_filp(). 
  105.          */  
  106.         error = mnt_want_write(nd.path.mnt);  
  107.         if (error)  
  108.             goto exit_mutex_unlock;  
  109.         /*__open_namei_create将会调用到父目录所属文件系统中定义的create方式创建文件*/  
  110.         error = __open_namei_create(&nd, &path, flag, mode);  
  111.         if (error) {  
  112.             mnt_drop_write(nd.path.mnt);  
  113.             goto exit;  
  114.         }  
  115.         /*nameidata_to_filp将会调用目标文件的inode对应的open函数进行打开操作*/  
  116.         filp = nameidata_to_filp(&nd, open_flag);  
  117.         if (IS_ERR(filp))  
  118.             ima_counts_put(&nd.path,  
  119.                        acc_mode & (MAY_READ | MAY_WRITE |  
  120.                            MAY_EXEC));  
  121.         mnt_drop_write(nd.path.mnt);  
  122.         if (nd.root.mnt)  
  123.             path_put(&nd.root);  
  124.         return filp;  
  125.     }  
  126.   
  127.     /* 
  128.      * 下面的情况对应目标文件存在 
  129.      */  
  130.     mutex_unlock(&dir->d_inode->i_mutex);  
  131.     audit_inode(pathname, path.dentry);  
  132.   
  133.     error = -EEXIST;  
  134.     if (flag & O_EXCL)  
  135.         goto exit_dput;  
  136.   
  137.     /*下面要做一些必要的检查*/  
  138.     if (__follow_mount(&path)) {//检测目标对象上是否挂载了文件系统  
  139.         error = -ELOOP;  
  140.         if (flag & O_NOFOLLOW)  
  141.             goto exit_dput;  
  142.     }  
  143.   
  144.     error = -ENOENT;  
  145.     if (!path.dentry->d_inode)//检测目标对象的inode是否存在  
  146.         goto exit_dput;  
  147.     if (path.dentry->d_inode->i_op->follow_link)//检测目标对象是否为链接文件  
  148.         goto do_link;  
  149.   
  150.     /*检查OK,将path保存至nd*/  
  151.     path_to_nameidata(&path, &nd);  
  152.     error = -EISDIR;  
  153.     if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))  
  154.         goto exit;  
  155. ok:  
  156.     /* 
  157.      * Consider: 
  158.      * 1. may_open() truncates a file 
  159.      * 2. a rw->ro mount transition occurs 
  160.      * 3. nameidata_to_filp() fails due to 
  161.      *    the ro mount. 
  162.      * That would be inconsistent, and should 
  163.      * be avoided. Taking this mnt write here 
  164.      * ensures that (2) can not occur. 
  165.      */  
  166.     will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);  
  167.     if (will_write) {  
  168.         error = mnt_want_write(nd.path.mnt);  
  169.         if (error)  
  170.             goto exit;  
  171.     }  
  172.     /*may_open()会做一些检测*/  
  173.     error = may_open(&nd.path, acc_mode, flag);  
  174.     if (error) {  
  175.         if (will_write)  
  176.             mnt_drop_write(nd.path.mnt);  
  177.         goto exit;  
  178.     }  
  179.     //执行文件系统定义的打开操作,并保存信息至filp  
  180.     filp = nameidata_to_filp(&nd, open_flag);  
  181.     if (IS_ERR(filp))  
  182.         ima_counts_put(&nd.path,  
  183.                    acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));  
  184.     /* 
  185.      * It is now safe to drop the mnt write 
  186.      * because the filp has had a write taken 
  187.      * on its behalf. 
  188.      */  
  189.     if (will_write)  
  190.         mnt_drop_write(nd.path.mnt);  
  191.     if (nd.root.mnt)  
  192.         path_put(&nd.root);  
  193.     return filp;  
  194.   
  195. exit_mutex_unlock:  
  196.     mutex_unlock(&dir->d_inode->i_mutex);  
  197. exit_dput:  
  198.     path_put_conditional(&path, &nd);  
  199. exit:  
  200.     if (!IS_ERR(nd.intent.open.file))  
  201.         release_open_intent(&nd);  
  202. exit_parent:  
  203.     if (nd.root.mnt)  
  204.         path_put(&nd.root);  
  205.     path_put(&nd.path);  
  206.     return ERR_PTR(error);  
  207.   
  208. do_link://目标文件为符号链接的处理,前文已经分析过  
  209.     error = -ELOOP;  
  210.     if (flag & O_NOFOLLOW)  
  211.         goto exit_dput;  
  212.     /* 
  213.      * This is subtle. Instead of calling do_follow_link() we do the 
  214.      * thing by hands. The reason is that this way we have zero link_count 
  215.      * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. 
  216.      * After that we have the parent and last component, i.e. 
  217.      * we are in the same situation as after the first path_walk(). 
  218.      * Well, almost - if the last component is normal we get its copy 
  219.      * stored in nd->last.name and we will have to putname() it when we 
  220.      * are done. Procfs-like symlinks just set LAST_BIND. 
  221.      */  
  222.     nd.flags |= LOOKUP_PARENT;  
  223.     error = security_inode_follow_link(path.dentry, &nd);  
  224.     if (error)  
  225.         goto exit_dput;  
  226.     error = __do_follow_link(&path, &nd);  
  227.     if (error) {  
  228.         /* Does someone understand code flow here? Or it is only 
  229.          * me so stupid? Anathema to whoever designed this non-sense 
  230.          * with "intent.open". 
  231.          */  
  232.         release_open_intent(&nd);  
  233.         if (nd.root.mnt)  
  234.             path_put(&nd.root);  
  235.         return ERR_PTR(error);  
  236.     }  
  237.     nd.flags &= ~LOOKUP_PARENT;  
  238.     if (nd.last_type == LAST_BIND)  
  239.         goto ok;  
  240.     error = -EISDIR;  
  241.     if (nd.last_type != LAST_NORM)  
  242.         goto exit;  
  243.     if (nd.last.name[nd.last.len]) {  
  244.         __putname(nd.last.name);  
  245.         goto exit;  
  246.     }  
  247.     error = -ELOOP;  
  248.     if (count++==32) {  
  249.         __putname(nd.last.name);  
  250.         goto exit;  
  251.     }  
  252.     dir = nd.path.dentry;  
  253.     mutex_lock(&dir->d_inode->i_mutex);  
  254.     path.dentry = lookup_hash(&nd);  
  255.     path.mnt = nd.path.mnt;  
  256.     __putname(nd.last.name);  
  257.     goto do_last;  
  258. }  
阅读(334) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~