共享子樹

1) 概述

考慮以下情況

一個程序想要克隆它自己的名稱空間,但是仍然想要訪問最近掛載的 CD。共享子樹語義提供了完成上述操作的必要機制。

它為諸如每個使用者名稱空間和版本化檔案系統等功能提供了必要的構建塊。

2) 功能

共享子樹提供了四種不同型別的掛載;更準確地說,是 struct vfsmount

  1. 共享掛載

  2. 從屬掛載

  3. 私有掛載

  4. 不可繫結掛載

2a) 一個共享掛載可以被複制到多個掛載點,並且所有的副本都保持完全相同。

這是一個例子

假設 /mnt 有一個共享掛載

mount --make-shared /mnt

注意:mount(8) 命令現在支援 --make-shared 標誌,因此不再需要示例 'smount' 程式,並且已被刪除。

# mount --bind /mnt /tmp

上面的命令將 /mnt 上的掛載複製到 /tmp 掛載點,並且兩個掛載的內容保持一致。

#ls /mnt
a b c

#ls /tmp
a b c

現在假設我們在 /tmp/a 上掛載一個裝置

# mount /dev/sd0  /tmp/a

#ls /tmp/a
t1 t2 t3

#ls /mnt/a
t1 t2 t3

請注意,掛載也已傳播到 /mnt 上的掛載。

即使在 /mnt/a 上掛載 /dev/sd0 也是如此。內容在 /tmp/a 下也會可見。

2b) 從屬掛載類似於共享掛載,除了掛載和解除安裝事件

只向其傳播。

所有從屬掛載都有一個主掛載,該主掛載是共享的。

這是一個例子

假設 /mnt 有一個共享掛載。 # mount --make-shared /mnt

讓我們將 /mnt 繫結掛載到 /tmp # mount --bind /mnt /tmp

在 /tmp 上的新掛載變成了一個共享掛載,並且是 /mnt 上掛載的副本。

現在讓我們使 /tmp 上的掛載成為 /mnt 的從屬 # mount --make-slave /tmp

讓我們在 /mnt/a 上掛載 /dev/sd0 # mount /dev/sd0 /mnt/a

#ls /mnt/a t1 t2 t3

#ls /tmp/a t1 t2 t3

請注意,掛載事件已傳播到 /tmp 上的掛載。

但是讓我們看看如果在 /tmp 上的掛載上掛載某些東西會發生什麼

# mount /dev/sd1 /tmp/b

#ls /tmp/b s1 s2 s3

#ls /mnt/b

請注意,掛載事件如何沒有傳播到 /mnt 上的掛載

2c) 私有掛載不轉發或接收傳播。

這是我們熟悉的掛載。它是預設型別。

2d) 不可繫結掛載是不可繫結的私有掛載

假設我們在 /mnt 上有一個掛載,並且我們使其不可繫結

   # mount --make-unbindable /mnt

Let's try to bind mount this mount somewhere else::

   # mount --bind /mnt /tmp
   mount: wrong fs type, bad option, bad superblock on /mnt,
           or too many mounted file systems

繫結不可繫結的掛載是無效的操作。

3) 設定掛載狀態

mount 命令 (util-linux 包) 可用於設定掛載狀態

mount --make-shared mountpoint
mount --make-slave mountpoint
mount --make-private mountpoint
mount --make-unbindable mountpoint

4) 用例

  1. 一個程序想要克隆它自己的名稱空間,但是仍然想要訪問最近掛載的 CD。

    解決方案

    系統管理員可以使 /cdrom 上的掛載共享

    mount --bind /cdrom /cdrom
    mount --make-shared /cdrom
    

    現在,任何克隆出新名稱空間的程序都將在 /cdrom 上有一個掛載,它是父名稱空間中相同掛載的副本。

    因此,當插入 CD 並掛載到 /cdrom 時,該掛載會傳播到所有其他克隆名稱空間中 /cdrom 上的其他掛載。

B) 一個程序想要使其掛載對任何其他程序不可見,但仍然能夠看到其他系統掛載。

解決方案

首先,管理員可以將整個掛載樹標記為可共享

mount --make-rshared /

新程序可以克隆出一個新的名稱空間。並將它的名稱空間的一部分標記為從屬

mount --make-rslave /myprivatetree

因此,程序在 /myprivatetree 中進行的任何掛載都不會出現在任何其他名稱空間中。但是,在父名稱空間中 /myprivatetree 下進行的掛載仍然會出現在程序的名稱空間中。

除了上述語義之外,此功能還提供瞭解決以下問題的構建塊

  1. 每個使用者的名稱空間

    上述語義提供了一種跨名稱空間共享掛載的方法。但是名稱空間與程序相關聯。如果名稱空間成為頭等物件,並具有使用者 API 來將名稱空間與 userid 關聯/分離,那麼每個使用者都可以擁有他/她自己的名稱空間,並根據他/她的要求進行定製。這需要在 PAM 中支援。

  2. 版本化檔案

    如果整個掛載樹在多個位置可見,那麼底層版本控制檔案系統可以根據用於訪問該檔案的路徑返回該檔案的不同版本。

    一個例子是

    mount --make-shared /
    mount --rbind / /view/v1
    mount --rbind / /view/v2
    mount --rbind / /view/v3
    mount --rbind / /view/v4
    

    如果 /usr 掛載了版本控制檔案系統,那麼該掛載也會出現在 /view/v1/usr、/view/v2/usr、/view/v3/usr 和 /view/v4/usr 中

    使用者可以透過訪問 /view/v3/usr/fs/namespace.c 來請求檔案 /usr/fs/namespace.c 的 v3 版本。然後,底層版本控制檔案系統可以解密正在請求檔案系統的 v3 版本,並返回相應的 inode。

5) 詳細語義

以下部分解釋了繫結、rbind、move、mount、umount 和 clone-namespace 操作的詳細語義。

注意:在整個文件中,單詞“vfsmount”和名詞“mount”已被用來表示相同的事物。

5a) 掛載狀態

給定的掛載可以處於以下狀態之一

  1. 共享

  2. 從屬

  3. 共享和從屬

  4. 私有

  5. 不可繫結

“傳播事件”定義為在 vfsmount 上生成的事件,該事件導致其他 vfsmount 中掛載或解除安裝操作。

“對等組”定義為彼此傳播事件的 vfsmount 組。

  1. 共享掛載

    “共享掛載”定義為屬於“對等組”的 vfsmount。

    例如

    mount --make-shared /mnt
    mount --bind /mnt /tmp
    

    在 /mnt 上的掛載和在 /tmp 上的掛載都是共享的,並且屬於同一個對等組。在 /mnt 或 /tmp 下掛載或解除安裝的任何內容都會反映在其對等組的所有其他掛載中。

  2. 從屬掛載

    “從屬掛載”定義為接收傳播事件但不轉發傳播事件的 vfsmount。

    顧名思義,從屬掛載具有一個主掛載,從中接收掛載/解除安裝事件。事件不會從從屬掛載傳播到主掛載。只有共享掛載可以透過執行以下命令來製作成從屬掛載

    mount --make-slave mount
    

    被製作成從屬的共享掛載不再共享,除非修改為共享。

  3. 共享和從屬

    vfsmount 可以既是共享的又是從屬的。此狀態表示掛載是從某個 vfsmount 的從屬,並且也具有其自己的對等組。此 vfsmount 從其主 vfsmount 接收傳播事件,並且還將其傳播事件轉發到其“對等組”和其從屬 vfsmount。

    嚴格來說,vfsmount 是共享的,具有其自己的對等組,並且該對等組是其他某個對等組的從屬。

    只有從屬 vfsmount 可以透過執行以下命令之一來製作成“共享和從屬”

    mount --make-shared mount
    

    或者透過將從屬 vfsmount 移動到共享 vfsmount 下。

  4. 私有掛載

    “私有掛載”定義為不接收或轉發任何傳播事件的 vfsmount。

  5. 不可繫結掛載

    “不可繫結掛載”定義為不接收或轉發任何傳播事件並且無法繫結掛載的 vfsmount。

狀態圖

下面的狀態圖解釋了掛載響應各種命令的狀態轉換

-----------------------------------------------------------------------
|             |make-shared |  make-slave  | make-private |make-unbindab|
--------------|------------|--------------|--------------|-------------|
|shared       |shared      |*slave/private|   private    | unbindable  |
|             |            |              |              |             |
|-------------|------------|--------------|--------------|-------------|
|slave        |shared      | **slave      |    private   | unbindable  |
|             |and slave   |              |              |             |
|-------------|------------|--------------|--------------|-------------|
|shared       |shared      | slave        |    private   | unbindable  |
|and slave    |and slave   |              |              |             |
|-------------|------------|--------------|--------------|-------------|
|private      |shared      |  **private   |    private   | unbindable  |
|-------------|------------|--------------|--------------|-------------|
|unbindable   |shared      |**unbindable  |    private   | unbindable  |
------------------------------------------------------------------------

* if the shared mount is the only mount in its peer group, making it
slave, makes it private automatically. Note that there is no master to
which it can be slaved to.

** slaving a non-shared mount has no effect on the mount.

除了下面列出的命令之外,“move”操作還會根據目標掛載的型別更改掛載的狀態。它在第 5d 節中進行了解釋。

5b) 繫結語義

考慮以下命令

mount --bind A/a  B/b

其中“A”是源掛載,“a”是掛載“A”中的目錄項,“B”是目標掛載,“b”是目標掛載中的目錄項。

結果取決於“A”和“B”的掛載型別。下表包含快速參考

--------------------------------------------------------------------------
|         BIND MOUNT OPERATION                                           |
|************************************************************************|
|source(A)->| shared      |       private  |       slave    | unbindable |
| dest(B)  |              |                |                |            |
|   |      |              |                |                |            |
|   v      |              |                |                |            |
|************************************************************************|
|  shared  | shared       |     shared     | shared & slave |  invalid   |
|          |              |                |                |            |
|non-shared| shared       |      private   |      slave     |  invalid   |
**************************************************************************

詳細資訊

  1. “A”是共享掛載,“B”是共享掛載。一個新的掛載“C”

    是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“C1”、“C2”、“C3” ... 並且被掛載在目錄項“b”上,在“B”傳播到的所有掛載上。建立了一個包含“C1”,..,“Cn”的新傳播樹。此傳播樹與“B”的傳播樹相同。最後,“C”的對等組與“A”的對等組合並。

  2. “A”是私有掛載,“B”是共享掛載。一個新的掛載“C”

    是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“C1”、“C2”、“C3” ... 並且被掛載在目錄項“b”上,在“B”傳播到的所有掛載上。設定了一個新的傳播樹,其中包含所有新的掛載“C”、“C1”、..、“Cn”,其配置與“B”的傳播樹完全相同。

  3. “A”是掛載“Z”的從屬掛載,“B”是共享掛載。一個新的

    是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“C1”、“C2”、“C3” ... 並且被掛載在目錄項“b”上,在“B”傳播到的所有掛載上。建立了一個包含新掛載“C”,'C1',.. 'Cn' 的新傳播樹。此傳播樹與“B”的傳播樹相同。最後,掛載“C”及其對等組被設定為掛載“Z”的從屬。換句話說,掛載“C”處於“從屬和共享”狀態。

  4. “A”是不可繫結掛載,“B”是共享掛載。這是一個

    無效的操作。

  5. “A”是私有掛載,“B”是非共享(私有、從屬或

    不可繫結)掛載。一個新的掛載“C”是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。

  6. “A”是共享掛載,“B”是非共享掛載。一個新的掛載“C”

    是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。“C”被設定為“A”的對等組的成員。

  7. “A”是掛載“Z”的從屬掛載,“B”是非共享掛載。一個

    是“A”的克隆,被建立。它的根目錄項是“a”。“C”被掛載在目錄項“b”上的掛載“B”上。此外,“C”被設定為“Z”的從屬掛載。換句話說,“A”和“C”都是“Z”的從屬掛載。“Z”上的所有掛載/解除安裝事件都會傳播到“A”和“C”。但是“A”上的掛載/解除安裝不會傳播到任何地方。同樣,“C”上的掛載/解除安裝也不會傳播到任何地方。

  8. “A”是不可繫結掛載,“B”是非共享掛載。這是一個

    無效的操作。不可繫結掛載無法繫結掛載。

5c) Rbind 語義

rbind 與 bind 相同。Bind 複製指定的掛載。Rbind 複製屬於指定掛載的樹中的所有掛載。Rbind 掛載是應用於樹中所有掛載的繫結掛載。

如果 rbind 的源樹具有一些不可繫結掛載,那麼不可繫結掛載下的子樹將在新位置被修剪。

例如

假設我們有以下掛載樹

   A
 /   \
 B   C
/ \ / \
D E F G

假設樹中的所有掛載(除了掛載 C)都是不可繫結型別之外的型別。

如果此樹被 rbind 到說 Z

我們將在新位置獲得以下樹

    Z
    |
    A'
   /
  B'                Note how the tree under C is pruned
 / \                in the new location.
D' E'

5d) 移動語義

考慮以下命令

mount --move A B/b

其中“A”是源掛載,“B”是目標掛載,“b”是目標掛載中的目錄項。

結果取決於“A”和“B”的掛載型別。下表是一個快速參考

---------------------------------------------------------------------------
|                   MOVE MOUNT OPERATION                                 |
|**************************************************************************
| source(A)->| shared      |       private  |       slave    | unbindable |
| dest(B)  |               |                |                |            |
|   |      |               |                |                |            |
|   v      |               |                |                |            |
|**************************************************************************
|  shared  | shared        |     shared     |shared and slave|  invalid   |
|          |               |                |                |            |
|non-shared| shared        |      private   |    slave       | unbindable |
***************************************************************************

注意

移動位於共享掛載下的掛載是無效的。

詳細資訊如下

  1. “A”是共享掛載,“B”是共享掛載。掛載“A”是

    掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“A1”、“A2”...“An”,並掛載在目錄項“b”上,在從掛載“B”接收傳播的所有掛載上。建立一個新的傳播樹,其配置與“B”的配置完全相同。這個新的傳播樹包含所有新的掛載“A1”、“A2”...“An”。並且這個新的傳播樹被附加到“A”的已經存在的傳播樹上。

  2. “A”是私有掛載,“B”是共享掛載。掛載“A”是

    掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“A1”、“A2”...“An”,並掛載在目錄項“b”上,在從掛載“B”接收傳播的所有掛載上。掛載“A”變成共享掛載,並建立一個與“B”相同的傳播樹。這個新的傳播樹包含所有新的掛載“A1”、“A2”...“An”。

  3. “A”是掛載“Z”的從屬掛載,“B”是共享掛載。該

    掛載“A”掛載在目錄項“b”上的掛載“B”上。還建立了新的掛載“A1”、“A2”...“An”,並掛載在目錄項“b”上,在從掛載“B”接收傳播的所有掛載上。建立一個新的傳播樹,其配置與“B”的配置完全相同。這個新的傳播樹包含所有新的掛載“A1”、“A2”...“An”。並且這個新的傳播樹被附加到“A”的已經存在的傳播樹上。掛載“A”繼續是“Z”的從屬掛載,但它也變為“共享”。

  4. “A”是不可繫結掛載,“B”是共享掛載。操作

    是無效的。因為在共享掛載“B”上掛載任何東西都可以建立新的掛載,這些掛載會被掛載在從“B”接收傳播的掛載上。並且由於掛載“A”是不可繫結的,因此無法克隆它以掛載在其他掛載點。

  5. “A”是私有掛載,“B”是非共享(私有、從屬或

    不可繫結)掛載。掛載“A”掛載在目錄項“b”上的掛載“B”上。

  6. “A”是共享掛載,“B”是非共享掛載。掛載“A”

    掛載在目錄項“b”上的掛載“B”上。掛載“A”繼續是共享掛載。

  7. “A”是掛載“Z”的從屬掛載,“B”是非共享掛載。

    掛載“A”掛載在目錄項“b”上的掛載“B”上。掛載“A”繼續是掛載“Z”的從屬掛載。

  8. “A”是不可繫結掛載,“B”是非共享掛載。掛載

    “A”掛載在目錄項“b”上的掛載“B”上。掛載“A”繼續是不可繫結掛載。

5e) 掛載語義

考慮以下命令

mount device  B/b

“B”是目標掛載,“b”是目標掛載中的目錄項。

上述操作與繫結操作相同,但源掛載始終是私有掛載。

5f) 解除安裝語義

考慮以下命令

umount A

其中“A”是掛載在目錄項“b”上的掛載“B”上的掛載。

如果掛載“B”是共享的,那麼從掛載“B”接收傳播並且其中沒有子掛載的掛載上的目錄項“b”上所有最近掛載的掛載都將被解除安裝。

示例:假設“B1”、“B2”、“B3”是相互傳播的共享掛載。

假設“A1”、“A2”、“A3”分別首先掛載在掛載“B1”、“B2”和“B3”上的目錄項“b”上。

假設接下來“C1”、“C2”、“C3”分別掛載在掛載“B1”、“B2”和“B3”上的相同目錄項“b”上。

如果解除安裝“C1”,則解除安裝掛載“B1”上和“B1”傳播到的掛載上最近掛載的所有掛載。

“B1”傳播到“B2”和“B3”。目錄項“b”上“B2”上最近掛載的掛載是“C2”,掛載“B3”是“C3”。

因此,應解除安裝所有“C1”、“C2”和“C3”。

如果“C2”或“C3”中的任何一個具有一些子掛載,那麼該掛載不會被解除安裝,但所有其他掛載都會被解除安裝。但是,如果告知解除安裝“C1”,並且“C1”有一些子掛載,則 umount 操作將完全失敗。

5g) 克隆名稱空間

克隆的名稱空間包含與父名稱空間相同的所有掛載。

假設“A”和“B”是父名稱空間和子名稱空間中的相應掛載。

如果“A”是共享的,那麼“B”也是共享的,“A”和“B”相互傳播。

如果“A”是“Z”的從屬掛載,那麼“B”也是“Z”的從屬掛載。

如果“A”是私有掛載,那麼“B”也是私有掛載。

如果“A”是不可繫結掛載,那麼“B”也是不可繫結掛載。

6) 測驗

  1. 以下命令序列的結果是什麼?

    mount --bind /mnt /mnt
    mount --make-shared /mnt
    mount --bind /mnt /tmp
    mount --move /tmp /mnt/1
    

    /mnt /mnt/1 /mnt/1/1 的內容應該是什麼?它們應該都是相同的嗎?還是隻有 /mnt 和 /mnt/1 相同?

  2. 以下命令序列的結果是什麼?

    mount --make-rshared /
    mkdir -p /v/1
    mount --rbind / /v/1
    

    /v/1/v/1 的內容應該是什麼?

  3. 以下命令序列的結果是什麼?

    mount --bind /mnt /mnt
    mount --make-shared /mnt
    mkdir -p /mnt/1/2/3 /mnt/1/test
    mount --bind /mnt/1 /tmp
    mount --make-slave /mnt
    mount --make-shared /mnt
    mount --bind /mnt/1/2 /tmp1
    mount --make-slave /mnt
    

    此時,我們在 /tmp 中有第一個掛載,其根目錄項是 1。我們稱此掛載為“A”。然後我們在 /tmp1 中有第二個掛載,其根目錄項為 2。我們稱此掛載為“B”。接下來,我們在 /mnt 中有第三個掛載,其根目錄項為 mnt。我們稱此掛載為“C”

    “B”是“A”的從屬,“C”是“B”的從屬 A -> B -> C

    此時,如果我們執行以下命令

    mount --bind /bin /tmp/test

    嘗試在“A”上掛載

    掛載會傳播到“B”和“C”嗎?

    /mnt/1/test 的內容會是什麼?

7) 常見問題解答

問題 1. 為什麼需要繫結掛載?它與符號連結有什麼不同?

如果目標掛載被解除安裝或移動,符號連結可能會失效。即使其他掛載被解除安裝或移動,繫結掛載仍然存在。

問題 2. 為什麼不能使用 exportfs 實現共享子樹?

exportfs 是一種實現共享子樹可以完成的部分功能的一種重量級方式。我無法想象如何使用 exportfs 實現從屬掛載的語義?

問題 3 為什麼需要不可繫結掛載?

假設我們想要在同一子樹中的多個位置複製掛載樹。

如果一個 rbind 在同一子樹中掛載一個樹“n”次,則建立的掛載數量是“n”的指數函式。擁有不可繫結掛載可以幫助修剪不需要的繫結掛載。這是一個例子。

步驟 1

假設根樹只有兩個目錄和一個 vfsmount

                root
               /    \
              tmp    usr

And we want to replicate the tree at multiple
mountpoints under /root/tmp
步驟 2
mount --make-shared /root

mkdir -p /tmp/m1

mount --rbind /root /tmp/m1

現在新樹看起來像這樣

          root
         /    \
       tmp    usr
      /
     m1
    /  \
   tmp  usr
   /
  m1

it has two vfsmounts
步驟 3
     mkdir -p /tmp/m2
     mount --rbind /root /tmp/m2

 the new tree now looks like this::

               root
              /    \
            tmp     usr
           /    \
         m1       m2
        / \       /  \
      tmp  usr   tmp  usr
      / \          /
     m1  m2      m1
         / \     /  \
       tmp usr  tmp   usr
       /        / \
      m1       m1  m2
     /  \
   tmp   usr
   /  \
  m1   m2

it has 6 vfsmounts
步驟 4
::

mkdir -p /tmp/m3 mount --rbind /root /tmp/m3

我不會繪製樹..但它有 24 個 vfsmounts

在步驟 i,vfsmount 的數量是 V[i] = i*V[i-1]。這是一個指數函式。並且這棵樹比我們一開始真正需要的掛載要多得多。

可以使用一系列 umount 在每個步驟修剪掉不需要的掛載。但有一個更好的解決方案。不可克隆掛載在這裡派上用場。

步驟 1

假設根樹只有兩個目錄和一個 vfsmount

                root
               /    \
              tmp    usr

How do we set up the same tree at multiple locations under
/root/tmp
步驟 2
mount --bind /root/tmp /root/tmp

mount --make-rshared /root
mount --make-unbindable /root/tmp

mkdir -p /tmp/m1

mount --rbind /root /tmp/m1

現在新樹看起來像這樣

       root
      /    \
    tmp    usr
   /
  m1
 /  \
tmp  usr
步驟 3
mkdir -p /tmp/m2
mount --rbind /root /tmp/m2

現在新樹看起來像這樣

       root
      /    \
    tmp    usr
   /   \
  m1     m2
 /  \     / \
tmp  usr tmp usr
步驟 4
mkdir -p /tmp/m3
mount --rbind /root /tmp/m3

現在新樹看起來像這樣

             root
         /           \
        tmp           usr
    /    \    \
  m1     m2     m3
 /  \     / \    /  \
tmp  usr tmp usr tmp usr

8) 實現

8A) 資料結構

struct vfsmount 中引入了 4 個新欄位

  • ->mnt_share

  • ->mnt_slave_list

  • ->mnt_slave

  • ->mnt_master

->mnt_share

將所有掛載連結在一起,此 vfsmount 從/向這些掛載傳送/接收傳播事件。

->mnt_slave_list

連結此 vfsmount 傳播到的所有掛載。

->mnt_slave

連結其主 vfsmount 傳播到的所有從屬。

->mnt_master

指向此 vfsmount 從中接收傳播的主 vfsmount。

->mnt_flags

採用兩個更多標誌來指示 vfsmount 的傳播狀態。MNT_SHARE 指示 vfsmount 是共享 vfsmount。MNT_UNCLONABLE 指示無法複製 vfsmount。

對等組中的所有共享 vfsmount 透過 ->mnt_share 形成一個迴圈列表。

具有相同 ->mnt_master 的所有 vfsmount 在錨定在 ->mnt_master->mnt_slave_list 中並透過 ->mnt_slave 的迴圈列表上形成。

->mnt_master 可以指向主對等組的任意(並且可能不同)成員。要查詢對等組的所有直接從屬,您需要遍歷其成員的 _all_ ->mnt_slave_list。從概念上講,它只是一個集合 - 在單個列表中的分佈不會影響傳播或操作修改傳播樹的方式。

對等組中的所有 vfsmount 具有相同的 ->mnt_master。如果它不是 NULL,它們形成從屬列表的連續(有序)段。

示例傳播樹如下圖所示。[注意:雖然它看起來像一個森林,但如果我們考慮所有共享掛載作為一個稱為“pnode”的概念實體,它就變成了一棵樹]

    A <--> B <--> C <---> D
   /|\            /|      |\
  / F G          J K      H I
 /
E<-->K
    /|\
   M L N

在上圖中,A、B、C 和 D 都是共享的並且相互傳播。“A”有 3 個從屬掛載“E”、“F”和“G”。“C”有 2 個從屬掛載“J”和“K”,“D”有 2 個從屬掛載“H”和“I”。“E”也與“K”共享,並且它們相互傳播。“K”有 3 個從屬“M”、“L”和“N”

A 的 ->mnt_share 與“B”、“C”和“D”的 ->mnt_share 連結

A 的 ->mnt_slave_list 與“E”、“K”、“F”和“G”的 ->mnt_slave 連結

E 的 ->mnt_share 與 K 的 ->mnt_share 連結

‘E’, ‘K’, ‘F’, ‘G’ 的 ->mnt_master 指向 ‘A’ 的 struct vfsmount

‘M’, ‘L’, ‘N’ 的 ->mnt_master 指向 ‘K’ 的 struct vfsmount

K 的 ->mnt_slave_list 連結到 ‘M’, ‘L’ 和 ‘N’ 的 ->mnt_slave

C 的 ->mnt_slave_list 連結到 ‘J’ 和 ‘K’ 的 ->mnt_slave

J 和 K 的 ->mnt_master 指向 C 的 struct vfsmount

最終 D 的 ->mnt_slave_list 連結到 ‘H’ 和 ‘I’ 的 ->mnt_slave

‘H’ 和 ‘I’ 的 ->mnt_master 指向 ‘D’ 的 struct vfsmount。

注意:傳播樹與掛載樹是正交的。

8B 鎖

->mnt_share, ->mnt_slave, ->mnt_slave_list, ->mnt_master 受 namespace_sem 保護(修改時獨佔,讀取時共享)。

通常,我們透過 vfsmount_lock 序列化 ->mnt_flags 的修改。有兩個例外:do_add_mount() 和 clone_mnt()。前者修改了一個尚未在任何共享資料結構中可見的 vfsmount。後者持有 namespace_sem,並且對 vfsmount 的唯一引用位於沒有 namespace_sem 就無法遍歷的列表中。

8C 演算法

實現的關鍵在於 rbind/move 操作。

總體的演算法將操作分解為 3 個階段:(檢視 attach_recursive_mnt() 和 propagate_mnt())

  1. 準備階段。

  2. 提交階段。

  3. 中止階段。

準備階段

對於源樹中的每個掛載

  1. 建立所需數量的掛載樹,以便

    附加到從目標掛載接收傳播的每個掛載。

  2. 不要將任何樹附加到其目標。但記下其 ->mnt_parent 和 ->mnt_mountpoint

  3. 連結所有新的掛載,以形成一個與目標掛載的傳播樹相同的傳播樹。

如果此階段成功,則應有 ‘n’ 個新的傳播樹;其中 ‘n’ 是源樹中掛載的數量。轉到提交階段

還應該有 ‘m’ 個新的掛載樹,其中 ‘m’ 是目標掛載傳播到的掛載數量。

如果任何記憶體分配失敗,則轉到中止階段。

提交階段

將每個掛載樹附加到其對應的目標掛載。

中止階段

刪除所有新建立的樹。

注意

所有與傳播相關的功能都位於檔案 pnode.c 中


版本 0.1 (建立初始文件, Ram Pai linuxram@us.ibm.com)

版本 0.2 (合併來自 Al Viro 的評論)