ネームスペース
ネームスペースという概念はカーネル2.6から導入されたもので、task_struct->nsproxyで参照される各リソースの管理単位となる。ここではファイルシステムについて。mountはvfsmount構造体をmnt_ns下で管理されるマウントツリーのリストを作成する。すなわちこのmnt_nsがプロセス間で異なれば、あるプロセスでmountしたファイルシステムはもう一つのプロセスでは参照できないということだ。
プロセスの作成はdo_fork関数そしてcopy_process関数へと渡っていき、そこでプロセスの複製を行う。ネームスペースに関しては、通常親プロセスを継承する。しかしCLONE_NEWNSが設定されていると、copy_namespaces関数がコールされ、新規のnsproxy(下位のメンバーについても)が確保され、そこに親の内容を複写する。clone関数から戻って子プロセスが起動し始める段においては、それは親プロセスと同じものであるが、子プロセスでmount処理を行うと、その処理は子プロセスのnsproxyに施される。従って親プロセスのnsproxyに反映されることはない。
通常プロセスは初期プロセスinitのnsproxyを引き継ぐわけで、あるプロセスのネームスペース変更はすぺでのプロセスに反映されることになる。
/dir1/dir2のディレクトリを作成する。そして1方の端末から/dir1にマウントする。当然dir2は見えなくなる。しかしもう1方の端末ではdir2が現れて、マウントしたファイルシステムは参照されないことが確認できる。
d_entryはネームスペースに依存していない。パスのルックアップはどうしているのだろうかと疑問がでてくる。確かマウントされているかはd_entry->d_mountedをインクリメントするだけで、どのvfsmountにマウントしているかの情報は設定されていない。0でないd_entry->d_mountedのd_entryに出っくわすたびに、vfsmountリストを辿ってそのマウント先を検索していた。たぶんこのマウント先検索においてvfsmountが見つからなければマウントされていないとして処理される(たぶん)。
なお、マウント先が反映されなシェルでmountでマウント状態をみると/dir1がマウントされていることになっている。これはmountコマンドでのマウント情報は/ets/mtabを参照しているからである。
プロセスの作成はdo_fork関数そしてcopy_process関数へと渡っていき、そこでプロセスの複製を行う。ネームスペースに関しては、通常親プロセスを継承する。しかしCLONE_NEWNSが設定されていると、copy_namespaces関数がコールされ、新規のnsproxy(下位のメンバーについても)が確保され、そこに親の内容を複写する。clone関数から戻って子プロセスが起動し始める段においては、それは親プロセスと同じものであるが、子プロセスでmount処理を行うと、その処理は子プロセスのnsproxyに施される。従って親プロセスのnsproxyに反映されることはない。
通常プロセスは初期プロセスinitのnsproxyを引き継ぐわけで、あるプロセスのネームスペース変更はすぺでのプロセスに反映されることになる。
struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns; struct user_namespace *user_ns; struct net *net_ns; };
int copy_namespaces(unsigned long flags, struct task_struct *tsk) { struct nsproxy *old_ns = tsk->nsproxy; struct nsproxy *new_ns; int err = 0; : : : new_ns = create_new_namespaces(flags, tsk, tsk->fs); if (IS_ERR(new_ns)) { err = PTR_ERR(new_ns); goto out; } tsk->nsproxy = new_ns; out: put_nsproxy(old_ns); return err; }ネームスペースの動作を確認するサンプルである。まず2つの端末を立ち上げる。この状態では2つとも同じネームスペースのシェルが起動していることになる。ここで1方の端末の下記サンプルを動作させる。CLONE_NEWNSを引数でcloneをコールし、その後にシェルと差し替えるものである。これで2つの端末は異なるネームスペースを持つシェルということになる。
/dir1/dir2のディレクトリを作成する。そして1方の端末から/dir1にマウントする。当然dir2は見えなくなる。しかしもう1方の端末ではdir2が現れて、マウントしたファイルシステムは参照されないことが確認できる。
#include <stdio.h> #include <unistd.h> #include <sched.h> char stack[4096*2]; int shell(); void main() { clone(shell, stack + 4096*2, CLONE_NEWNS, NULL); while (1) sleep(100); } int shell() { int ret; ret = execlp("/bin/sh","/bin/sh", NULL); return ret; }
[root@KURO-BOXHG kitamura]# mount -o loop -t ext3 disk /dir1 [root@KURO-BOXHG kitamura]# ls /dir1/ lost+found
sh-4.0# ls /dir1 dir2
d_entryはネームスペースに依存していない。パスのルックアップはどうしているのだろうかと疑問がでてくる。確かマウントされているかはd_entry->d_mountedをインクリメントするだけで、どのvfsmountにマウントしているかの情報は設定されていない。0でないd_entry->d_mountedのd_entryに出っくわすたびに、vfsmountリストを辿ってそのマウント先を検索していた。たぶんこのマウント先検索においてvfsmountが見つからなければマウントされていないとして処理される(たぶん)。
static void follow_mount(struct vfsmount **mnt, struct dentry **dentry) { while (d_mountpoint(*dentry)) { struct vfsmount *mounted = lookup_mnt(*mnt, *dentry); if (!mounted) break; <- マウント先が見つからなければそのまま dput(*dentry); mntput(*mnt); <- マウント先が見つかればdentryにマウント先 *mnt = mounted; *dentry = dget(mounted->mnt_root); } }マウント時のdentryのd_mountedをインクリメントするだけで、パスルックアップで再検索するというのは、上記処理を実装する上でも都合がよいというわけだ。(上の推測が当たっているならの話だが)
なお、マウント先が反映されなシェルでmountでマウント状態をみると/dir1がマウントされていることになっている。これはmountコマンドでのマウント情報は/ets/mtabを参照しているからである。
sh-4.0# mount /dev/sda3 on / type ext3 (rw,noatime,errors=remount-ro) proc on /proc type proc (rw) none on /dev/pts type devpts (rw,gid=5,mode=20) /dev/sda4 on /mnt type ext3 (rw,noatime) /dev/sdb1 on /mnt/usbhd type ext2 (rw) /dev/loop0 on /dir1 type ext3 (rw)