Linux 檔案系統呼叫open七日遊(三)
阿新 • • 發佈:2018-11-08
接著上回,當對“.”和“..”處理完成後就直接返回進入下一個子路徑迴圈了,但如果當前子路徑不是“.”或“..”呢?
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component
我們這就進入 lookup_fast,看看它到底有多快。
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_fast
如果順利找到了目標 dentry 則還需要進行一系列的檢查(1381、1391)確保在我們做讀取操作的期間沒有人對這些結構進行改動。然後就是更新臨時變數 path,為啥不更新 nd 呢?別忘了 nd 是很有脾氣的,掛載點和符號連結人家都看不上,非真正目錄不嫁。而這個時候還不知道這個目標是不是一個掛載點,如果是掛載點則還需要沿著被掛載的 mount 結構走到真正的目標上;退一步來說,就算這個目標不是掛載點,但它要是具備自動掛載特性呢(1407);再退一步來說,它是不是符號連結我們也不知道,所以現在先不忙著更新 nd。緊接著就通過 __follow_mount_rcu 跨過掛載點這些“偽目標”(1405),這個函式和上一篇裡 follow_dotdot_rcu 的第二部分很相似我們就不深入進去了,有興趣的同學結合程式碼自己研究一下就好了。如果一切順利返回 0,請參考上面 walk_component 的程式碼,如果返回 0 就會跳過 1535 行那個 if,這也就是說在 rcu-walk 模式下是不會啟動 lookup_slow 的。
那麼什麼時候才會啟動 lookup_slow 呢?咱們接著往下看:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_fast
接下來我們就來看看 lookup_slow:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_slow
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_slow > __lookup_hash
lookup_slow 剩下的工作和 fast 差不多,這裡就不重複了。現在回到 walk_component:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component
既然退出 rcu-walk 就可以睡眠了,那我們也休息一下,明天再接著去遊覽有趣的符號連結。
轉自:http://blog.chinaunix.net/uid-20522771-id-4419703.html
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component
點選(此處)摺疊或開啟
...
- err = lookup_fast(nd, path, &inode);
- if
- if (err < 0)
- goto out_err;
- err = lookup_slow(nd, path)
- if (err < 0)
- goto out_err;
- inode = path->dentry->d_inode;
- }
- err = -ENOENT;
- if (!inode || d_is_negative(path->dentry))
- goto out_path_put;
...
在 Kernel 中任何一個常用操作都會有兩套以上的策略,其中一個是高效率的相對而言另一個就是系統開銷比較大的。比如在上面的程式碼中就能直觀的發現 Kernel 會首先嚐試 fast(1534) ,如果失敗了才會啟動 slow(1539)。其實在我們當前的場景中不止這兩種策略,別忘了在這裡還有 rcu-walk 和 ref-walk,現在我們先簡單介紹一下 Kernel 在這裡進行“路徑行走”的策略,讓大家有一個感性認識,然後再進入這幾個函式中進行理性分析。首先 Kernel 會在 rcu-walk 模式下進入 lookup_fast 進行嘗試,如果失敗了那麼就嘗試就地轉入 ref-walk,如果還是不行就回到 do_filp_open 從頭開始。Kernel 在 ref-walk 模式下會首先在記憶體緩衝區查詢相應的目標(lookup_fast),如果找不到就啟動具體檔案系統自己的 lookup 進行查詢(lookup_slow)。注意,在 rcu-walk 模式下是不會進入 lookup_slow 的。如果這樣都還找不到的話就一定是是出錯了,那就報錯返回吧,這時螢幕就會出現喜聞樂見的“No such file or directory”。我們這就進入 lookup_fast,看看它到底有多快。
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_fast
點選(此處)摺疊或開啟
- static int lookup_fast(struct nameidata *nd,
- struct path *path, struct inode **inode)
- {
...
- if (nd->flags & LOOKUP_RCU) {
- unsigned seq;
- dentry = __d_lookup_rcu(parent, &nd->last, &seq);
- if (!dentry)
- goto unlazy;
...
- *inode = dentry->d_inode;
- if (read_seqcount_retry(&dentry->d_seq, seq))
- return -ECHILD;
...
- if (__read_seqcount_retry(&parent->d_seq, nd->seq))
- return -ECHILD;
- nd->seq = seq;
...
- path->mnt = mnt;
- path->dentry = dentry;
- if (unlikely(!__follow_mount_rcu(nd, path, inode)))
- goto unlazy;
- if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
- goto unlazy;
- return 0;
- unlazy:
- if (unlazy_walk(nd, dentry))
- return -ECHILD;
- } else {
...
首先呼叫 __d_lookup_rcu 在記憶體中的某個散列表裡通過字串比較查詢目標 dentry,如果找到了就返回該 dentry;如果沒找到就需要跳轉到 unlazy 標號處(1374),在這裡會使用 unlazy_walk 就地將查詢模式切換到 ref-walk,如果還不行就只好返回到 do_filp_open 從頭來過(1412)。如果順利找到了目標 dentry 則還需要進行一系列的檢查(1381、1391)確保在我們做讀取操作的期間沒有人對這些結構進行改動。然後就是更新臨時變數 path,為啥不更新 nd 呢?別忘了 nd 是很有脾氣的,掛載點和符號連結人家都看不上,非真正目錄不嫁。而這個時候還不知道這個目標是不是一個掛載點,如果是掛載點則還需要沿著被掛載的 mount 結構走到真正的目標上;退一步來說,就算這個目標不是掛載點,但它要是具備自動掛載特性呢(1407);再退一步來說,它是不是符號連結我們也不知道,所以現在先不忙著更新 nd。緊接著就通過 __follow_mount_rcu 跨過掛載點這些“偽目標”(1405),這個函式和上一篇裡 follow_dotdot_rcu 的第二部分很相似我們就不深入進去了,有興趣的同學結合程式碼自己研究一下就好了。如果一切順利返回 0,請參考上面 walk_component 的程式碼,如果返回 0 就會跳過 1535 行那個 if,這也就是說在 rcu-walk 模式下是不會啟動 lookup_slow 的。
那麼什麼時候才會啟動 lookup_slow 呢?咱們接著往下看:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_fast
點選(此處)摺疊或開啟
...
- unlazy:
- if (unlazy_walk(nd, dentry))
- return -ECHILD;
- } else {
- dentry = __d_lookup(parent, &nd->last);
- }
- if (unlikely(!dentry))
- goto need_lookup;
...
- path->mnt = mnt;
- path->dentry = dentry;
- err = follow_managed(path, nd->flags);
- if (unlikely(err < 0)) {
- path_put_conditional(path, nd);
- return err;
- }
- if (err)
- nd->flags |= LOOKUP_JUMPED;
- *inode = path->dentry->d_inode;
- return 0;
- need_lookup:
- return 1;
- }
接下來我們就來看看 lookup_slow:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_slow
點選(此處)摺疊或開啟
- static int lookup_slow(struct nameidata *nd, struct path *path)
- {
...
- mutex_lock(&parent->d_inode->i_mutex);
- dentry = __lookup_hash(&nd->last, parent, nd->flags);
- mutex_unlock(&parent->d_inode->i_mutex);
...
- }
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component > lookup_slow > __lookup_hash
點選(此處)摺疊或開啟
- static struct dentry *__lookup_hash(struct qstr *name,
- struct dentry *base, unsigned int flags)
- {
- bool need_lookup;
- struct dentry *dentry;
- dentry = lookup_dcache(name, base, flags, &need_lookup);
- if (!need_lookup)
- return dentry;
- return lookup_real(base->d_inode, dentry, flags);
- }
lookup_slow 剩下的工作和 fast 差不多,這裡就不重複了。現在回到 walk_component:
【fs/namei.c】 sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component
點選(此處)摺疊或開啟
...
- if (should_follow_link(path->dentry, follow)) {
- if (nd->flags & LOOKUP_RCU) {
- if (unlikely(unlazy_walk(nd, path->dentry))) {
- err = -ECHILD;
- goto out_err;
- }
- }
- BUG_ON(inode != path->dentry->d_inode);
- return 1;
- }
- path_to_nameidata(path, nd);
- nd->inode = inode;
- return 0;
...
- }
既然退出 rcu-walk 就可以睡眠了,那我們也休息一下,明天再接著去遊覽有趣的符號連結。
轉自:http://blog.chinaunix.net/uid-20522771-id-4419703.html