一.mnt_namespace介绍    
    namespace的一种将不同进程“资源”分隔开来的一种方式,不同进程使用不同namespace可使不同进程之间对对方namespace内的资源不可见,而mnt_namespace是namespace当中的一种,是指文件系统树对应的命名namespace,也就说如果不同进程使用不同的mnt_namespace,那么他们将拥有不同的文件系统树(已经挂载了的)。
    在创建进程时,如果子进程需要拥有和父进程不同的mnt_namespace,那么需要将已安装文件系统树全部拷贝一份,这是通过copy_tree函数完成的(为思路清晰起见,省略非重点代码)。

二.mnt_namespace拷贝代码解读

点击(此处)折叠或打开

  1. // 'x'表示已安装文件系统 
  2. // 解读copy_tree函数如何遍历'x'树
  3. //
  4. // copy_tree函数通过了非递归方式遍历整个'x'树,从上到下依次处理,先处理到最低层'x'
  5. // 然后从左到右,处理完最低层后回溯到上一层的下一个节点,重复这个过程
  6. // 外层for循环遍历第一层节点,内层for循环遍历以第一层节点为根的树
  7. // next_mnt函数完成从上到下,从下到上,从左到右的查找
  8. static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
  9. {
  10.         struct vfsmount *res, *p, *q, *r, *s;
  11.         struct list_head *h;
  12.         struct nameidata nd;

  13.         res = q = clone_mnt(mnt, dentry); // tree's gen
  14.         if (!q)
  15.                 goto Enomem;
  16.         ....
  17.         p = mnt;

  18.         // mnt_mounts节点属于父安装文件系统,其作用是连接所有的子安装文件系统        
  19.         // 连接方式是: parent.mnt_mounts children.child children.child
  20.         // 所以这里最外层循环做的事情就是遍历所有的子安装文件系统
  21.         for (h = mnt->mnt_mounts.next; h != &mnt->mnt_mounts; h = h->next) {
  22.                 r = list_entry(h, struct vfsmount, mnt_child);
  23.     
  24.                 // 外层循环是对树形结构当中第一层子文件系统进行遍历,
  25.                 // 也就是说在外层循环当中不会遍历孙子以及更低层次的安装文件系统
  26.                 // 而这里内层循环完成的就是对孙子以及更低层次的'x'的遍历('x'代表已安装文件系统)
  27.                 // 这里的难点就在于理解这个内层循环如何遍历了孙子以及更低层次'x'

  28.                 //1.从这个for语句看出,第一个要处理的是外层循环传入的第一层'x',而结束条件是空
  29.                 //(接2,位于next_mnt函数当中)
  30.                 for (s = r; s; s = next_mnt(s, r)) {
  31.                         //找到此次要处理的'x'的父节点,以便通过挂到父节点所在的树上去
  1.                         while (p != s->mnt_parent) {
  2.                                 p = p->mnt_parent;
  3.                                 q = q->mnt_parent;
  4.                         }
  5.                         p = s;
  6.                         nd.mnt = q;
  7.                         nd.dentry = p->mnt_mountpoint;
  8.                         //此次要处理的'x'
  1.                         q = clone_mnt(p, p->mnt_root);
  2.                         if (!q)
  3.                                 goto Enomem;

  4.                         //spin_lock(&vfsmount_lock);
  5.                         //list_add_tail(&q->mnt_list, &res->mnt_list);
  6.                         //attach_mnt(q, &nd);
  7.                         //spin_unlock(&vfsmount_lock);
  8.                 }
  9.         }
  10.         return res;
  11.  Enomem:
  12.         if (res) {
  13.                 spin_lock(&vfsmount_lock);
  14.                 umount_tree(res);
  15.                 spin_unlock(&vfsmount_lock);
  16.         }
  17.         return NULL;
  18. }
  19. // 从上到下,从左到右的遍历一颗树
  20. static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
  21. {
  22.         struct list_head *next = p->mnt_mounts.next;
  23.         // 如果当前处理的'x'没有子节点
  24.         if (next == &p->mnt_mounts) {
  25.                 while (1) {
  26.                         // 2:如果当前函数传入的的'x',没有子节点(next和自身相等),且已经已经
  27.                         //     回溯到了root,表示已root为根的树已经全部遍历完成,返回空
  28.                         if (p == root)
  29.                                 return NULL;

  30.                         // 没有子节点且没有回溯到root,那么继续遍历下一个兄弟节点
  31.                         next = p->mnt_child.next;

  32.                         // 由于最后一个children.child是指向的parent.mnt_mounts,那么如果
  33.                         // child和mnt_mounts不相等,那么代表存在兄弟节点,那么就跳出循环
  34.                         // 处理这个兄弟节点
  35.                         if (next != &p->mnt_parent->mnt_mounts)
  36.                                 break;

  37.                         // 已经没有兄弟节点就继续向上层回溯
  38.                         p = p->mnt_parent;
  39.                 }
  40.         }
  41.         // 如果有子节点,则直接处理子节点
  42.         return list_entry(next, struct vfsmount, mnt_child);
  43. }
三.实例分析(以下面的树形图为例说明其遍历过程)mnt_namespace的拷贝过程解读(copy_tree函数)-LMLPHP

1.在外层循环中传入child1,在内层循环直接处理。
2.处理完child1,在内层for语句调用next_mnt,next_mnt当中发现child1的mnt_mounts的next和自
   身相等,且等于第二个参数child1,则返回NULL,退出内层for语句。
3.处理child2。
4.处理完child2,在内层for语句调用next_mnt,next_mnt当中发现child2的mnt_mounts的next和自
  身不相等,则处理其next grandson1。
5.处理完grandson1,在内层for语句调用next_mnt,next_mnt当中发现grandson1的mnt_mounts
  的next和自
身相等,但不等于child2,则查找grandson1的next grandson2。
6.处理grandson2。
7.处理完grandson2,在内层for语句调用next_mnt,next_mnt当中发现grandson2的mnt_mounts
  的next和自
身不相等,则处理regrandson。
8.处理完regrandson,在内层for语句调用next_mnt,next_mnt当中发现regrandson的mnt_mounts
  的next和自
身相等,但不等于child2,则查找regrandson的next,发现和其parent grandson2相
  等,则查找grandson2的next,发现和其parent相等,且等于传入的第二个参数child2,返回
  NULL。
9.处理child3。
10.处理完child3,在内层for语句调用next_mnt,next_mnt当中发现child3的mnt_mounts的next和
  自
身相等,且等于child3,则返回NULL,退出内层for语句

四.总结
    简而言之,copy_tree目的是遍历整个mnt_namespace,而遍历方式是先依次查找到最深的节点,然后在回溯。

12-16 23:20