Linux虚拟文件系统之文件打开(sys

字体大小: 中小 标准 ->行高大小: 标准
在文件读写之前,我们必须先打开文件。从应用程序的角度来看,这是通过标准库的open函数完成的,该函数返回一个文件描述符。内核中是由系统调用sys_open()函数完成。 [cpp]
  1. /*sys_open*/   SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)  
  2. {       long ret;  
  3.     /*检查是否应该不考虑用户层传递的标志、总是强行设置      O_LARGEFILE标志。如果底层处理器的字长不是32位,就是这种 
  4.     情况*/       if (force_o_largefile())  
  5.         flags |= O_LARGEFILE;       /*实际工作*/  
  6.     ret = do_sys_open(AT_FDCWD, filename, flags, mode);       /* avoid REGPARM breakage on x86: */  
  7.     asmlinkage_protect(3, ret, filename, flags, mode);       return ret;  
  8. }  

实际实现工作

[cpp]
  1. <pre class="cpp" name="code">long do_sys_open(int dfd, const char __user *filename, int flags, int mode)   {  
  2.     /*从进程地址空间读取该文件的路径名*/       char *tmp = getname(filename);  
  3.     int fd = PTR_ERR(tmp);     
  4.     if (!IS_ERR(tmp)) {           /*在内核中,每个打开的文件由一个文件描述符表示 
  5.         该描述符在特定于进程的数组中充当位置索引(数组是          task_struct->files->fd_arry),该数组的元素包含了file结构,其中 
  6.         包括每个打开文件的所有必要信息。因此,调用下面          函数查找一个未使用的文件描述符,返回的是上面 
  7.         说的数组的下标*/           fd = get_unused_fd_flags(flags);  
  8.         if (fd >= 0) {               /*fd获取成功则开始打开文件,此函数是主要完成打开功能的函数*/  
  9.             struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);               if (IS_ERR(f)) {  
  10.                 put_unused_fd(fd);                   fd = PTR_ERR(f);  
  11.             } else {                   fsnotify_open(f->f_path.dentry);  
  12.                 fd_install(fd, f);               }  
  13.         }           putname(tmp);  
  14.     }       return fd;  
  15. }  

打开文件主体实现

[cpp]
  1. /*   * Note that the low bits of the passed in "open_flag" 
  2.  * are not the same as in the local variable "flag". See   * open_to_namei_flags() for more details. 
  3.  */   struct file *do_filp_open(int dfd, const char *pathname,  
  4.         int open_flag, int mode, int acc_mode)   {  
  5.     struct file *filp;       struct nameidata nd;  
  6.     int error;       struct path path;  
  7.     struct dentry *dir;       int count = 0;  
  8.     int will_write;         /*改变参数flag的值,具体做法是flag+1*/  
  9.     int flag = open_to_namei_flags(open_flag);       /*设置访问权限*/  
  10.     if (!acc_mode)           acc_mode = MAY_OPEN | ACC_MODE(flag);  
  11.        /* O_TRUNC implies we need access checks for write permissions */  
  12.            /*根据 O_TRUNC标志设置写权限 */  
  13.     if (flag & O_TRUNC)           acc_mode |= MAY_WRITE;  
  14.        /* Allow the LSM permission hook to distinguish append  
  15.        access from general write access. */          /* 设置O_APPEND 标志*/  
  16.     if (flag & O_APPEND)           acc_mode |= MAY_APPEND;  
  17.        /* 
  18.      * The simplest case - just a plain lookup.       */  
  19.       /*如果不是创建文件*/       if (!(flag & O_CREAT)) {  
  20.         /*当内核要访问一个文件的时候,第一步要做的是找到这个文件,          而查找文件的过程在vfs里面是由path_lookup或者path_lookup_open函数来完成的。 
  21.         这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,          并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后 
  22.         通过文件描述符,来访问这些数据结构*/           error = path_lookup_open(dfd, pathname, lookup_flags(flag),  
  23.                      &nd, flag);           if (error)  
  24.             return ERR_PTR(error);           goto ok;/*跳过下面的创建部分*/  
  25.     }     
  26.     /*       * Create - we need to know the parent. 
  27.      */        /*到此则是要创建文件*/  
  28.     /* path-init为查找作准备工作,path_walk真正上路查找,      这两个函数联合起来根据一段路径名找到对应的dentry */  
  29.     error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);       if (error)  
  30.         return ERR_PTR(error);       error = path_walk(pathname, &nd);  
  31.     if (error) {           if (nd.root.mnt)  
  32.             path_put(&nd.root);           return ERR_PTR(error);  
  33.     }       if (unlikely(!audit_dummy_context()))  
  34.         /*保存inode节点信息*/           audit_inode(pathname, nd.path.dentry);  
  35.        /* 
  36.      * We have the parent and last component. First of all, check       * that we are not asked to creat(2) an obvious directory - that 
  37.      * will not do.       */  
  38.     error = -EISDIR;       /*父节点信息*/  
  39.     if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])           goto exit_parent;  
  40.        error = -ENFILE;  
  41.      /*获取文件指针*/       filp = get_empty_filp();  
  42.     if (filp == NULL)           goto exit_parent;  
  43.     /*填充nameidata 结构*/       nd.intent.open.file = filp;  
  44.     nd.intent.open.flags = flag;       nd.intent.open.create_mode = mode;  
  45.     dir = nd.path.dentry;       nd.flags &= ~LOOKUP_PARENT;  
  46.     nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;       if (flag & O_EXCL)  
  47.         nd.flags |= LOOKUP_EXCL;       mutex_lock(&dir->d_inode->i_mutex);  
  48.     /*从哈希表中查找目的文件对应的dentry,上面路径搜索的是父节点      也就是目的文件的上一层目录,为了得到目的文件的 
  49.     path结构,我们用nd中的last结构和上一层目录的dentry结构      可以找到*/  
  50.     path.dentry = lookup_hash(&nd);       path.mnt = nd.path.mnt;  
  51.     /*到此目标节点的path结构已经找到*/   do_last:  
  52.     error = PTR_ERR(path.dentry);       if (IS_ERR(path.dentry)) {  
  53.         mutex_unlock(&dir->d_inode->i_mutex);           goto exit;  
  54.     }     
  55.     if (IS_ERR(nd.intent.open.file)) {           error = PTR_ERR(nd.intent.open.file);  
  56.         goto exit_mutex_unlock;       }  
  57.        /* Negative dentry, just create the file */  
  58.     /*如果此dentry结构没有对应的inode节点,说明是无效的,应该创建文件节点 */       if (!path.dentry->d_inode) {  
  59.         /*           * This write is needed to ensure that a 
  60.          * ro->rw transition does not occur between           * the time when the file is created and when 
  61.          * a permanent write count is taken through           * the 'struct file' in nameidata_to_filp(). 
  62.          */            /*write权限是必需的*/  
  63.         error = mnt_want_write(nd.path.mnt);           if (error)  
  64.             goto exit_mutex_unlock;           /*按照namei格式的flag open*,主要是创建inode*/  
  65.         error = __open_namei_create(&nd, &path, flag, mode);           if (error) {  
  66.             mnt_drop_write(nd.path.mnt);               goto exit;  
  67.         }           /*根据nameidata 得到相应的file结构*/  
  68.         filp = nameidata_to_filp(&nd, open_flag);           if (IS_ERR(filp))  
  69.             ima_counts_put(&nd.path,                          acc_mode & (MAY_READ | MAY_WRITE |  
  70.                            MAY_EXEC));           /*放弃写权限*/  
  71.         mnt_drop_write(nd.path.mnt);           if (nd.root.mnt)  
  72.             path_put(&nd.root);           return filp;  
  73.     }     
  74.     /*       * It already exists. 
  75.      */         /*要打开的文件已经存在*/  
  76.     mutex_unlock(&dir->d_inode->i_mutex);       /*保存inode节点*/  
  77.     audit_inode(pathname, path.dentry);     
  78.     error = -EEXIST;       if (flag & O_EXCL)  
  79.         goto exit_dput;       /*如果path上安装了文件系统,则依次往下找,直到找到 
  80.     的文件系统没有安装别的文件系统,更新path结构为      此文件系统的根目录信息*/  
  81.     if (__follow_mount(&path)) {           error = -ELOOP;  
  82.         if (flag & O_NOFOLLOW)               goto exit_dput;  
  83.     }     
  84.     error = -ENOENT;       if (!path.dentry->d_inode)  
  85.         goto exit_dput;       if (path.dentry->d_inode->i_op->follow_link)  
  86.         goto do_link;/*顺次遍历符号链接*/       /*路径转化为相应的nameidata 结构*/  
  87.     path_to_nameidata(&path, &nd);       error = -EISDIR;  
  88.     /*如果是文件夹*/       if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))  
  89.         goto exit;       /*到这里,nd结构中存放的信息已经是最后的目的文件信息*/  
  90. ok:       /* 
  91.      * Consider:       * 1. may_open() truncates a file 
  92.      * 2. a rw->ro mount transition occurs       * 3. nameidata_to_filp() fails due to 
  93.      *    the ro mount.       * That would be inconsistent, and should 
  94.      * be avoided. Taking this mnt write here       * ensures that (2) can not occur. 
  95.      */       will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);  
  96.     if (will_write) {           error = mnt_want_write(nd.path.mnt);  
  97.         if (error)               goto exit;  
  98.     }       /*may_open执行权限检测、文件打开和truncate的操作*/  
  99.     error = may_open(&nd.path, acc_mode, flag);       if (error) {  
  100.         if (will_write)               mnt_drop_write(nd.path.mnt);  
  101.         goto exit;       }  
  102.     /*将nameidata转化为file*/       filp = nameidata_to_filp(&nd, open_flag);  
  103.     if (IS_ERR(filp))           ima_counts_put(&nd.path,  
  104.                    acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));       /* 
  105.      * It is now safe to drop the mnt write       * because the filp has had a write taken 
  106.      * on its behalf.       */  
  107.     if (will_write)           /*释放写权限*/  
  108.         mnt_drop_write(nd.path.mnt);       if (nd.root.mnt)  
  109.         /*释放引用计数*/           path_put(&nd.root);  
  110.     return filp;     
  111. exit_mutex_unlock:       mutex_unlock(&dir->d_inode->i_mutex);  
  112. exit_dput:       path_put_conditional(&path, &nd);  
  113. exit:       if (!IS_ERR(nd.intent.open.file))  
  114.         release_open_intent(&nd);   exit_parent:  
  115.     if (nd.root.mnt)           path_put(&nd.root);  
  116.     path_put(&nd.path);       return ERR_PTR(error);  
  117. /*允许遍历连接文件,则手工找到连接文件对应的文件*/   do_link:  
  118.     error = -ELOOP;       if (flag & O_NOFOLLOW)  
  119.         goto exit_dput;/*不允许遍历连接文件,返回错误*/       /* 
  120.      * 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 
  121.      * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.       * After that we have the parent and last component, i.e. 
  122.      * we are in the same situation as after the first path_walk().       * Well, almost - if the last component is normal we get its copy 
  123.      * stored in nd->last.name and we will have to putname() it when we       * are done. Procfs-like symlinks just set LAST_BIND. 
  124.      */        /*以下是手工找到链接文件对应的文件dentry结构代码 
  125.           */             /*设置查找LOOKUP_PARENT标志*/  
  126.     nd.flags |= LOOKUP_PARENT;       /*判断操作是否安全*/  
  127.     error = security_inode_follow_link(path.dentry, &nd);       if (error)  
  128.         goto exit_dput;       /*处理符号链接,即路径搜索,结果放入nd中*/  
  129.     error = __do_follow_link(&path, &nd);       if (error) {  
  130.         /* Does someone understand code flow here? Or it is only           * me so stupid? Anathema to whoever designed this non-sense 
  131.          * with "intent.open".           */  
  132.         release_open_intent(&nd);           if (nd.root.mnt)  
  133.             path_put(&nd.root);           return ERR_PTR(error);  
  134.     }       nd.flags &= ~LOOKUP_PARENT;  
  135.     /*检查最后一段文件或目录名的属性情况*/       if (nd.last_type == LAST_BIND)  
  136.         goto ok;       error = -EISDIR;  
  137.     if (nd.last_type != LAST_NORM)           goto exit;  
  138.     if (nd.last.name[nd.last.len]) {           __putname(nd.last.name);  
  139.         goto exit;       }  
  140.     error = -ELOOP;       /*出现回环标志: 循环超过32次*/  
  141.     if (count++==32) {           __putname(nd.last.name);  
  142.         goto exit;       }  
  143.     dir = nd.path.dentry;       mutex_lock(&dir->d_inode->i_mutex);  
  144.     /*更新路径的挂接点和dentry*/       path.dentry = lookup_hash(&nd);  
  145.     path.mnt = nd.path.mnt;       __putname(nd.last.name);  
  146.     goto do_last;   }  

在内核中要打开一个文件,首先应该找到这个文件,而查找文件的过程在vfs里面是由do_path_lookup或者path_lookup_open函数来完成的,关于文件路径查找在前面已经分析过相关的代码了。这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构,将指向file的描述符返回用户。用户随后通过文件描述符,来访问这些数据结构。

此文章由 http://www.ositren.com 收集整理 ,地址为: http://www.ositren.com/htmls/57778.html