歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux教程

Linux虛擬文件系統之文件打開(sys_open())

在文件讀寫之前,我們必須先打開文件。從應用程序的角度來看,這是通過標准庫的open函數完成的,該函數返回一個文件描述符。內核中是由系統調用sys_open()函數完成。 [cpp]
  1. /*sys_open*/  
  2. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)  
  3. {  
  4.     long ret;  
  5.     /*檢查是否應該不考慮用戶層傳遞的標志、總是強行設置 
  6.     O_LARGEFILE標志。如果底層處理器的字長不是32位,就是這種 
  7.     情況*/  
  8.     if (force_o_largefile())  
  9.         flags |= O_LARGEFILE;  
  10.     /*實際工作*/  
  11.     ret = do_sys_open(AT_FDCWD, filename, flags, mode);  
  12.     /* avoid REGPARM breakage on x86: */  
  13.     asmlinkage_protect(3, ret, filename, flags, mode);  
  14.     return ret;  
  15. }  

實際實現工作

[cpp]
  1. <pre class="cpp" name="code">long do_sys_open(int dfd, const char __user *filename, int flags, int mode)  
  2. {  
  3.     /*從進程地址空間讀取該文件的路徑名*/  
  4.     char *tmp = getname(filename);  
  5.     int fd = PTR_ERR(tmp);  
  6.   
  7.     if (!IS_ERR(tmp)) {  
  8.         /*在內核中,每個打開的文件由一個文件描述符表示 
  9.         該描述符在特定於進程的數組中充當位置索引(數組是 
  10.         task_struct->files->fd_arry),該數組的元素包含了file結構,其中 
  11.         包括每個打開文件的所有必要信息。因此,調用下面 
  12.         函數查找一個未使用的文件描述符,返回的是上面 
  13.         說的數組的下標*/  
  14.         fd = get_unused_fd_flags(flags);  
  15.         if (fd >= 0) {  
  16.             /*fd獲取成功則開始打開文件,此函數是主要完成打開功能的函數*/  
  17.             struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);  
  18.             if (IS_ERR(f)) {  
  19.                 put_unused_fd(fd);  
  20.                 fd = PTR_ERR(f);  
  21.             } else {  
  22.                 fsnotify_open(f->f_path.dentry);  
  23.                 fd_install(fd, f);  
  24.             }  
  25.         }  
  26.         putname(tmp);  
  27.     }  
  28.     return fd;  
  29. }  

打開文件主體實現

[cpp]
  1. /* 
  2.  * Note that the low bits of the passed in "open_flag" 
  3.  * are not the same as in the local variable "flag". See 
  4.  * open_to_namei_flags() for more details. 
  5.  */  
  6. struct file *do_filp_open(int dfd, const char *pathname,  
  7.         int open_flag, int mode, int acc_mode)  
  8. {  
  9.     struct file *filp;  
  10.     struct nameidata nd;  
  11.     int error;  
  12.     struct path path;  
  13.     struct dentry *dir;  
  14.     int count = 0;  
  15.     int will_write;  
  16.       /*改變參數flag的值,具體做法是flag+1*/  
  17.     int flag = open_to_namei_flags(open_flag);  
  18.     /*設置訪問權限*/  
  19.     if (!acc_mode)  
  20.         acc_mode = MAY_OPEN | ACC_MODE(flag);  
  21.   
  22.     /* O_TRUNC implies we need access checks for write permissions */  
  23.       
  24.     /*根據 O_TRUNC標志設置寫權限 */  
  25.     if (flag & O_TRUNC)  
  26.         acc_mode |= MAY_WRITE;  
  27.   
  28.     /* Allow the LSM permission hook to distinguish append  
  29.        access from general write access. */  
  30.        /* 設置O_APPEND 標志*/  
  31.     if (flag & O_APPEND)  
  32.         acc_mode |= MAY_APPEND;  
  33.   
  34.     /* 
  35.      * The simplest case - just a plain lookup. 
  36.      */  
  37.       /*如果不是創建文件*/  
  38.     if (!(flag & O_CREAT)) {  
  39.         /*當內核要訪問一個文件的時候,第一步要做的是找到這個文件, 
  40.         而查找文件的過程在vfs裡面是由path_lookup或者path_lookup_open函數來完成的。 
  41.         這兩個函數將用戶傳進來的字符串表示的文件路徑轉換成一個dentry結構, 
  42.         並建立好相應的inode和file結構,將指向file的描述符返回用戶。用戶隨後 
  43.         通過文件描述符,來訪問這些數據結構*/  
  44.         error = path_lookup_open(dfd, pathname, lookup_flags(flag),  
  45.                      &nd, flag);  
  46.         if (error)  
  47.             return ERR_PTR(error);  
  48.         goto ok;/*跳過下面的創建部分*/  
  49.     }  
  50.   
  51.     /* 
  52.      * Create - we need to know the parent. 
  53.      */  
  54.      /*到此則是要創建文件*/  
  55.     /* path-init為查找作准備工作,path_walk真正上路查找, 
  56.     這兩個函數聯合起來根據一段路徑名找到對應的dentry */  
  57.     error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);  
  58.     if (error)  
  59.         return ERR_PTR(error);  
  60.     error = path_walk(pathname, &nd);  
  61.     if (error) {  
  62.         if (nd.root.mnt)  
  63.             path_put(&nd.root);  
  64.         return ERR_PTR(error);  
  65.     }  
  66.     if (unlikely(!audit_dummy_context()))  
  67.         /*保存inode節點信息*/  
  68.         audit_inode(pathname, nd.path.dentry);  
  69.   
  70.     /* 
  71.      * We have the parent and last component. First of all, check 
  72.      * that we are not asked to creat(2) an obvious directory - that 
  73.      * will not do. 
  74.      */  
  75.     error = -EISDIR;  
  76.     /*父節點信息*/  
  77.     if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])  
  78.         goto exit_parent;  
  79.   
  80.     error = -ENFILE;  
  81.      /*獲取文件指針*/  
  82.     filp = get_empty_filp();  
  83.     if (filp == NULL)  
  84.         goto exit_parent;  
  85.     /*填充nameidata 結構*/  
  86.     nd.intent.open.file = filp;  
  87.     nd.intent.open.flags = flag;  
  88.     nd.intent.open.create_mode = mode;  
  89.     dir = nd.path.dentry;  
  90.     nd.flags &= ~LOOKUP_PARENT;  
  91.     nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;  
  92.     if (flag & O_EXCL)  
  93.         nd.flags |= LOOKUP_EXCL;  
  94.     mutex_lock(&dir->d_inode->i_mutex);  
  95.     /*從哈希表中查找目的文件對應的dentry,上面路徑搜索的是父節點 
  96.     也就是目的文件的上一層目錄,為了得到目的文件的 
  97.     path結構,我們用nd中的last結構和上一層目錄的dentry結構 
  98.     可以找到*/  
  99.     path.dentry = lookup_hash(&nd);  
  100.     path.mnt = nd.path.mnt;  
  101.     /*到此目標節點的path結構已經找到*/  
  102. do_last:  
  103.     error = PTR_ERR(path.dentry);  
  104.     if (IS_ERR(path.dentry)) {  
  105.         mutex_unlock(&dir->d_inode->i_mutex);  
  106.         goto exit;  
  107.     }  
  108.   
  109.     if (IS_ERR(nd.intent.open.file)) {  
  110.         error = PTR_ERR(nd.intent.open.file);  
  111.         goto exit_mutex_unlock;  
  112.     }  
  113.   
  114.     /* Negative dentry, just create the file */  
  115.     /*如果此dentry結構沒有對應的inode節點,說明是無效的,應該創建文件節點 */  
  116.     if (!path.dentry->d_inode) {  
  117.         /* 
  118.          * This write is needed to ensure that a 
  119.          * ro->rw transition does not occur between 
  120.          * the time when the file is created and when 
  121.          * a permanent write count is taken through 
  122.          * the 'struct file' in nameidata_to_filp(). 
  123.          */  
  124.          /*write權限是必需的*/  
  125.         error = mnt_want_write(nd.path.mnt);  
  126.         if (error)  
  127.             goto exit_mutex_unlock;  
  128.         /*按照namei格式的flag open*,主要是創建inode*/  
  129.         error = __open_namei_create(&nd, &path, flag, mode);  
  130.         if (error) {  
  131.             mnt_drop_write(nd.path.mnt);  
  132.             goto exit;  
  133.         }  
  134.         /*根據nameidata 得到相應的file結構*/  
  135.         filp = nameidata_to_filp(&nd, open_flag);  
  136.         if (IS_ERR(filp))  
  137.             ima_counts_put(&nd.path,  
  138.                        acc_mode & (MAY_READ | MAY_WRITE |  
  139.                            MAY_EXEC));  
  140.         /*放棄寫權限*/  
  141.         mnt_drop_write(nd.path.mnt);  
  142.         if (nd.root.mnt)  
  143.             path_put(&nd.root);  
  144.         return filp;  
  145.     }  
  146.   
  147.     /* 
  148.      * It already exists. 
  149.      */  
  150.       /*要打開的文件已經存在*/  
  151.     mutex_unlock(&dir->d_inode->i_mutex);  
  152.     /*保存inode節點*/  
  153.     audit_inode(pathname, path.dentry);  
  154.   
  155.     error = -EEXIST;  
  156.     if (flag & O_EXCL)  
  157.         goto exit_dput;  
  158.     /*如果path上安裝了文件系統,則依次往下找,直到找到 
  159.     的文件系統沒有安裝別的文件系統,更新path結構為 
  160.     此文件系統的根目錄信息*/  
  161.     if (__follow_mount(&path)) {  
  162.         error = -ELOOP;  
  163.         if (flag & O_NOFOLLOW)  
  164.             goto exit_dput;  
  165.     }  
  166.   
  167.     error = -ENOENT;  
  168.     if (!path.dentry->d_inode)  
  169.         goto exit_dput;  
  170.     if (path.dentry->d_inode->i_op->follow_link)  
  171.         goto do_link;/*順次遍歷符號鏈接*/  
  172.     /*路徑轉化為相應的nameidata 結構*/  
  173.     path_to_nameidata(&path, &nd);  
  174.     error = -EISDIR;  
  175.     /*如果是文件夾*/  
  176.     if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))  
  177.         goto exit;  
  178.     /*到這裡,nd結構中存放的信息已經是最後的目的文件信息*/  
  179. ok:  
  180.     /* 
  181.      * Consider: 
  182.      * 1. may_open() truncates a file 
  183.      * 2. a rw->ro mount transition occurs 
  184.      * 3. nameidata_to_filp() fails due to 
  185.      *    the ro mount. 
  186.      * That would be inconsistent, and should 
  187.      * be avoided. Taking this mnt write here 
  188.      * ensures that (2) can not occur. 
  189.      */  
  190.     will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);  
  191.     if (will_write) {  
  192.         error = mnt_want_write(nd.path.mnt);  
  193.         if (error)  
  194.             goto exit;  
  195.     }  
  196.     /*may_open執行權限檢測、文件打開和truncate的操作*/  
  197.     error = may_open(&nd.path, acc_mode, flag);  
  198.     if (error) {  
  199.         if (will_write)  
  200.             mnt_drop_write(nd.path.mnt);  
  201.         goto exit;  
  202.     }  
  203.     /*將nameidata轉化為file*/  
  204.     filp = nameidata_to_filp(&nd, open_flag);  
  205.     if (IS_ERR(filp))  
  206.         ima_counts_put(&nd.path,  
  207.                    acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));  
  208.     /* 
  209.      * It is now safe to drop the mnt write 
  210.      * because the filp has had a write taken 
  211.      * on its behalf. 
  212.      */  
  213.     if (will_write)  
  214.         /*釋放寫權限*/  
  215.         mnt_drop_write(nd.path.mnt);  
  216.     if (nd.root.mnt)  
  217.         /*釋放引用計數*/  
  218.         path_put(&nd.root);  
  219.     return filp;  
  220.   
  221. exit_mutex_unlock:  
  222.     mutex_unlock(&dir->d_inode->i_mutex);  
  223. exit_dput:  
  224.     path_put_conditional(&path, &nd);  
  225. exit:  
  226.     if (!IS_ERR(nd.intent.open.file))  
  227.         release_open_intent(&nd);  
  228. exit_parent:  
  229.     if (nd.root.mnt)  
  230.         path_put(&nd.root);  
  231.     path_put(&nd.path);  
  232.     return ERR_PTR(error);  
  233. /*允許遍歷連接文件,則手工找到連接文件對應的文件*/  
  234. do_link:  
  235.     error = -ELOOP;  
  236.     if (flag & O_NOFOLLOW)  
  237.         goto exit_dput;/*不允許遍歷連接文件,返回錯誤*/  
  238.     /* 
  239.      * This is subtle. Instead of calling do_follow_link() we do the 
  240.      * thing by hands. The reason is that this way we have zero link_count 
  241.      * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. 
  242.      * After that we have the parent and last component, i.e. 
  243.      * we are in the same situation as after the first path_walk(). 
  244.      * Well, almost - if the last component is normal we get its copy 
  245.      * stored in nd->last.name and we will have to putname() it when we 
  246.      * are done. Procfs-like symlinks just set LAST_BIND. 
  247.      */  
  248.      /*以下是手工找到鏈接文件對應的文件dentry結構代碼 
  249.           */  
  250.           /*設置查找LOOKUP_PARENT標志*/  
  251.     nd.flags |= LOOKUP_PARENT;  
  252.     /*判斷操作是否安全*/  
  253.     error = security_inode_follow_link(path.dentry, &nd);  
  254.     if (error)  
  255.         goto exit_dput;  
  256.     /*處理符號鏈接,即路徑搜索,結果放入nd中*/  
  257.     error = __do_follow_link(&path, &nd);  
  258.     if (error) {  
  259.         /* Does someone understand code flow here? Or it is only 
  260.          * me so stupid? Anathema to whoever designed this non-sense 
  261.          * with "intent.open". 
  262.          */  
  263.         release_open_intent(&nd);  
  264.         if (nd.root.mnt)  
  265.             path_put(&nd.root);  
  266.         return ERR_PTR(error);  
  267.     }  
  268.     nd.flags &= ~LOOKUP_PARENT;  
  269.     /*檢查最後一段文件或目錄名的屬性情況*/  
  270.     if (nd.last_type == LAST_BIND)  
  271.         goto ok;  
  272.     error = -EISDIR;  
  273.     if (nd.last_type != LAST_NORM)  
  274.         goto exit;  
  275.     if (nd.last.name[nd.last.len]) {  
  276.         __putname(nd.last.name);  
  277.         goto exit;  
  278.     }  
  279.     error = -ELOOP;  
  280.     /*出現回環標志: 循環超過32次*/  
  281.     if (count++==32) {  
  282.         __putname(nd.last.name);  
  283.         goto exit;  
  284.     }  
  285.     dir = nd.path.dentry;  
  286.     mutex_lock(&dir->d_inode->i_mutex);  
  287.     /*更新路徑的掛接點和dentry*/  
  288.     path.dentry = lookup_hash(&nd);  
  289.     path.mnt = nd.path.mnt;  
  290.     __putname(nd.last.name);  
  291.     goto do_last;  
  292. }  

在內核中要打開一個文件,首先應該找到這個文件,而查找文件的過程在vfs裡面是由do_path_lookup或者path_lookup_open函數來完成的,關於文件路徑查找在前面已經分析過相關的代碼了。這兩個函數將用戶傳進來的字符串表示的文件路徑轉換成一個dentry結構,並建立好相應的inode和file結構,將指向file的描述符返回用戶。用戶隨後通過文件描述符,來訪問這些數據結構。

Copyright © Linux教程網 All Rights Reserved