Linuxで名前空間に関して設定できるのはclone(2)、unshare(2)、setns(2)で、このうち実行中のプロセスの名前空間を変更できるのは後者の2個なんですが、カーネル側ではkernel/nsproxy.cにあるswitch_task_namespaces()が実際の処理を行ってます。そんなわけでこの辺りのコードを見てみます。
まず、switch_task_namespaces()を呼んでいる箇所ですが以下の4つがありました
このうち1、2は実行中のプロセスの名前空間を切り替えるため、3はプロセスの終了処理の一環で使用していた名前空間をプロセスから参照できなくさせるため、最後は名前空間を設定したけどプロセスの生成に失敗したので後始末というところです。
unshare(2)の場合はunshare()でこのように呼び出しています。
1864 err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, 1865 〜略〜 1877 if (new_nsproxy) 1878 switch_task_namespaces(current, new_nsproxy); 1879
setns(2)の場合はsetns()でこのように。tskはcurrentです。
247 new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs); 〜略〜 258 switch_task_namespaces(tsk, new_nsproxy);
さて、switch_task_namespaces()ですがこれは全部で20行ほどの関数です。might_sleep()はデバッグ機能のCONFIG_DEBUG_ATOMIC_SLEEPがyになっていなければ何もしないのでコンパイル時の最適化で消されているはずです。
201 void switch_task_namespaces(struct task_struct *p, struct nsproxy *new) 202 { 203 struct nsproxy *ns; 204 205 might_sleep(); 206 207 ns = p->nsproxy; 208 209 rcu_assign_pointer(p->nsproxy, new); 210 211 if (ns && atomic_dec_and_test(&ns->count)) { 212 /* 213 * wait for others to get what they want from this nsproxy. 214 * 215 * cannot release this nsproxy via the call_rcu() since 216 * put_mnt_ns() will want to sleep 217 */ 218 synchronize_rcu(); 219 free_nsproxy(ns); 220 } 221 } 222
この関数の内容はrcu_assign_pointer()で現在設定されている名前空間のへのポインタへnewをセット。そして、使用していた名前空間がNULLでなく、リファレンスカウンタをデクリメントした結果countが0になった場合、名前空間が設定されていてかつこのプロセスの名前空間(p->nsproxy)を誰も使っていない、場合に名前空間を解放しています。
free_nsproxy()は特に面白いことはやっていなくて、各名前空間(uts、mount等)の参照カウンタを減らし、最後にnsproxy構造体のインスタンスをkmem_cache_free()で解放するだけです。
unshare()、setns()の場合、switch_task_namespaces()の呼び出し前には新しく作ったstruct nsproxyを渡しています。setns()で使っているcreate_new_namespaces()に関してはcreate_new_namespaces()めもで軽くまとめています。
unshare_nsproxy_namespaces()はというと、こちらもそんなに大したことはやっていないので軽く見てしまいます。
176 int unshare_nsproxy_namespaces(unsigned long unshare_flags, 177 struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs) 178 { 179 struct user_namespace *user_ns; 180 int err = 0; 181 182 if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | 183 CLONE_NEWNET | CLONE_NEWPID))) 184 return 0; 185 186 user_ns = new_cred ? new_cred->user_ns : current_user_ns(); 187 if (!ns_capable(user_ns, CAP_SYS_ADMIN)) 188 return -EPERM; 189 190 *new_nsp = create_new_namespaces(unshare_flags, current, user_ns, 191 new_fs ? new_fs : current->fs); 192 if (IS_ERR(*new_nsp)) { 193 err = PTR_ERR(*new_nsp); 194 goto out; 195 } 196 197 out: 198 return err; 199 }
最初に各名前空間のフラグをチェックして1つも設定されていなければ0を返して関数を抜けますが、この場合はnew_nspが設定されないのでunshare()内の以下のif文のチェックに引っかかってswitch_task_namespaces()は呼ばれません。
1877 if (new_nsproxy) 1878 switch_task_namespaces(current, new_nsproxy);
その後、ケーパビリティのチェックをして問題なければcreate_new_namespaces()で新しい名前空間のインスタンスを作成という流れです。
- 作者: Michael Kerrisk,千住治郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/12/01
- メディア: 大型本
- クリック: 14回
- この商品を含むブログ (5件) を見る