mount namespaceを作成するカーネル側の関数としてはcreate_mnt_ns()とcopy_mnt_ns()の2つがあるんだけどcreate_mnt_ns()のほうはbtrfsとnfs4しか使っていない模様。
これら2個のコールフローは以下のような流れ。
btrfsの場合はサブボリュームをマウントするときに使う。
mount_subvol() @fs/btrfs/super.c --> mount_subtree() --> create_mnt_ns() --> alloc_mnt_ns()
nfsの場合はsub treeのマウント時に使う。
nfs_follow_remote_path() @fs/nfs/nfs4super.c --> mount_subtree() --> create_mnt_ns() --> alloc_mnt_ns()
それ以外の場合はfork()系、unshare()、setns()の処理から下記のような流れで行われる。
fork()系。
copy_process() --> copy_namespaces() --> create_new_namespaces() @kernel/nsproxy.c --> copy_mnt_ns() --> alloc_mnt_ns()
unshare()。
unshare() --> unshare_nsproxy_namespaces() --> create_new_namespaces() @kernel/nsproxy.c --> copy_mnt_ns() --> alloc_mnt_ns()
setns()。
setns() --> create_new_namespaces() @kernel/nsproxy.c --> copy_mnt_ns() --> alloc_mnt_ns()
では、alloc_mnt_ns()から処理を見ていきましょう。
2467 /* 2468 * Assign a sequence number so we can detect when we attempt to bind 2469 * mount a reference to an older mount namespace into the current 2470 * mount namespace, preventing reference counting loops. A 64bit 2471 * number incrementing at 10Ghz will take 12,427 years to wrap which 2472 * is effectively never, so we can ignore the possibility. 2473 */ 2474 static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1); 2475 2476 static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns) 2477 { 2478 struct mnt_namespace *new_ns; 2479 int ret; 2480 2481 new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); 2482 if (!new_ns) 2483 return ERR_PTR(-ENOMEM); 2484 ret = proc_alloc_inum(&new_ns->proc_inum); 2485 if (ret) { 2486 kfree(new_ns); 2487 return ERR_PTR(ret); 2488 } 2489 new_ns->seq = atomic64_add_return(1, &mnt_ns_seq); 2490 atomic_set(&new_ns->count, 1); 2491 new_ns->root = NULL; 2492 INIT_LIST_HEAD(&new_ns->list); 2493 init_waitqueue_head(&new_ns->poll); 2494 new_ns->event = 0; 2495 new_ns->user_ns = get_user_ns(user_ns); 2496 return new_ns; 2497 }
ここの処理内容で大切なのは↓ですかね。これは古いmount namespaceをカレントのnamespaceにbind mount使用とした時にmount関係がループしないようにするためっぽい。
2489 new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
copy_mnt_ns()はちょっと長め。
最初のチェックでCLONE_NEWNSフラグが立っていなければ参照カウンタを増やして終了。
2499 struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, 2500 struct user_namespace *user_ns, struct fs_struct *new_fs) 2501 { 2502 struct mnt_namespace *new_ns; 2503 struct vfsmount *rootmnt = NULL, *pwdmnt = NULL; 2504 struct mount *p, *q; 2505 struct mount *old; 2506 struct mount *new; 2507 int copy_flags; 2508 2509 BUG_ON(!ns); 2510 2511 if (likely(!(flags & CLONE_NEWNS))) { 2512 get_mnt_ns(ns); 2513 return ns; 2514 } 2515
nsはfork()系の場合は今作っている子プロセスのstruct task_struct。
2516 old = ns->root; 2517
前述したalloc_mnt_ns()でstruct mnt_namespaceのインスタンスを作成。
2518 new_ns = alloc_mnt_ns(user_ns); 2519 if (IS_ERR(new_ns)) 2520 return new_ns; 2521
copy_tree()に渡すフラグ作り。user namespaceの設定でちょっと変化がある。
2522 namespace_lock(); 2523 /* First pass: copy the tree topology */ 2524 copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; 2525 if (user_ns != ns->user_ns) 2526 copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
copy_tree()の詳細を調べる気はなかったのでざっくりと行くと、mountのツリー構造がコピーされる、
2527 new = copy_tree(old, old->mnt.mnt_root, copy_flags); 2528 if (IS_ERR(new)) { 2529 namespace_unlock(); 2530 free_mnt_ns(new_ns); 2531 return ERR_CAST(new); 2532 }
この時に実際にコピーをしているのはclone_mnt()だと思う。コピーの処理はcopy_flagsの内容によって変わる。例えばこことか。
880 if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE)) 881 mnt->mnt_group_id = 0; /* not a peer of original */ 882 else 883 mnt->mnt_group_id = old->mnt_group_id;
copy_tree()が終わって作成しているmnt_namespaceの/にコピーしたツリーが設定される。
2533 new_ns->root = new; 2534 list_add_tail(&new_ns->list, &new->mnt_list); 2535
次のループはコメントにある通りだと思うので割愛。
2536 /* 2537 * Second pass: switch the tsk->fs->* elements and mark new vfsmounts 2538 * as belonging to new namespace. We have already acquired a private 2539 * fs_struct, so tsk->fs->lock is not needed. 2540 */ 2541 p = old; 2542 q = new; 2543 while (p) { 2544 q->mnt_ns = new_ns; 2545 if (new_fs) { 2546 if (&p->mnt == new_fs->root.mnt) { 2547 new_fs->root.mnt = mntget(&q->mnt); 2548 rootmnt = &p->mnt; 2549 } 2550 if (&p->mnt == new_fs->pwd.mnt) { 2551 new_fs->pwd.mnt = mntget(&q->mnt); 2552 pwdmnt = &p->mnt; 2553 } 2554 } 2555 p = next_mnt(p, old); 2556 q = next_mnt(q, new); 2557 if (!q) 2558 break; 2559 while (p->mnt.mnt_root != q->mnt.mnt_root) 2560 p = next_mnt(p, old); 2561 } 2562 namespace_unlock(); 2563
最後に、必要ならmountしているファイルシステムの解放をする。
2564 if (rootmnt) 2565 mntput(rootmnt); 2566 if (pwdmnt) 2567 mntput(pwdmnt); 2568 2569 return new_ns; 2570 } 2571
プロのための Linuxシステム・10年効く技術 (Software Design plus)
- 作者: 中井悦司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/06/15
- メディア: 大型本
- 購入: 6人 クリック: 88回
- この商品を含むブログ (17件) を見る