Linux Namespace 
@masami256
Table of Contents 
• Namespace overview 
• System calls 
• kernel implementation 
• Namespace Example
namespace 
overview
Namespace? 
• リソース 
• 所謂コンテナ型仮想化を実現する上で重要な機 
能の一つ
Resource 
• Namespaceにおけるリソース 
• cpuやmemoryと言った物理的な計算資源で 
はない 
• ホスト名、ネットワーク設定、pidなどのカーネ 
ルが扱うデータ
Feature 
• プロセス間でカーネルのリソースを共有 
• fork(2)の実行時は親プロセスとリソースを共有 
• namespace毎に独立したリソース 
• 名前空間の状態を変えるような処理を行っても 
別の名前空間に属するプロセスには影響は及ば 
ない
Namespace representation 
• 名前空間はファイルとしてユーザー空間から見え 
る 
• setns(2)で利用 
masami@miko:~$ ls -l /proc/self/ns 
total 0 
dr-x--x--x 2 masami masami 0 Aug 31 00:15 . 
dr-xr-xr-x 8 masami masami 0 Aug 31 00:15 .. 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 ipc -> ipc:[4026531839] 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 mnt -> mnt:[4026531840] 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 net -> net:[4026531957] 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 pid -> pid:[4026531836] 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 user -> user:[4026531837] 
lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 uts -> uts:[4026531838]
Namespaces 
• uts 
• net 
• pid 
• mnt 
• ipc 
• user
uts namespace 
• ホスト名、ドメイン名などのデータ 
• カーネルバージョン等もあるが変更不可 
• ゲストが自ホスト名を変えてもホストOS側には 
影響はでない
net namespace 
• ネットワーク関連のリソース 
• Network device 
• IP address 
• Routing table 
• Filtering table 
• Port number 
• /proc/net 
• etc…
pid namespace 
• 親プロセスとは別のpidを利用可能に 
• namespace Aのpid:1000とnamespace Bの 
pid:1000は別の存在 
• procfsを適切に分ければ他のnamespceのプロ 
セスを参照できなくなる
mnt namespace 
• マウントしているファイルシステムを表す 
• 名前空間分離時は親プロセスのmnt namespace 
をコピー 
• 分離後に親プロセスがusb stickなどをマウン 
トしてもゲスト側からは見えない
ipc namespace 
• System V IPCで使用するリソースを分離 
• 共有メモリ、セマフォ、メッセージキュー
user namespace 
• ホストとは別のuid/gid体系を持てる 
• ホストのuid/gidとゲストのuid/gidマッピングが必要 
• 設定しないと65534が設定される 
• groupsは65534番のgroup入りする 
• 他のnamespaceと違い、独立していない 
• 他のnamespace空間は個々にuser namespaceを持っている 
• 各namespaceのコピー処理関数はuser nsを受け取るので 
get_user_ns()で参照カウントを増やしている
uid/gid mapping 
• マッピングを行うシステムコールは無い 
• 以下のファイルを用いてマッピングを実施 
• /proc/<pid>/uid_map 
• /proc/<pid>/gid_map
uid/gid mapping 
• ゲストのuid0をホストのuid1000にマッピング 
• gidも同様に 
masami@miko:~$ ./a.out -U -M '0 1000 1' -G '0 1000 1' bash 
root@miko:~# id 
uid=0(root) gid=0(root) groups=0(root),65534 
root@miko:~# cat /proc/self/uid_map /proc/self/gid_map 
0 1000 1 
0 1000 1 
root@miko:~# touch test.txt 
root@miko:~# ls -la test.txt 
-rw-r--r-- 1 root root 0 Aug 31 12:00 test.txt 
root@miko:~# 
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 
masami@miko:~$ ls -la test.txt 
-rw-r--r-- 1 masami masami 0 Aug 31 12:00 test.txt 
masami@miko:~$
In an user namespace 
•マッピングなしでuser namespaceをunshare 
I have no name!@miko:/proc/640$ ls -la /usr/bin/sudo 
-rwsr-xr-x 1 65534 65534 142792 May 9 15:58 /usr/bin/sudo 
! 
•通常の状態 
I have no name!@miko:/proc/640$ exit 
logout 
masami@miko:~$ ls -la /usr/bin/sudo 
-rwsr-xr-x 1 root root 142792 May 9 15:58 /usr/bin/sudo
how to uid/gid mapping 
1. clone(2)を呼ぶ 
1. CLONE_NEWUSERをflagsにセット 
2. exec()系のシステムコールを呼ぶ前にマッピングを行う 
1. /proc/<child process pid>/uid_map 
2. /proc/<child process pid>/gid_map 
3. exec()系の関数を呼んで新たなプログラムを実行
system calls
system calls 
• clone(2) 
• unshare(2) 
• setns(2)
clone(2) 
• clone(2)でpid namespaceを分離した場 
• 子プロセスから見ると自分のpidは1として見える 
• 親プロセスからは親プロセスのpid namespaceでの 
pidが振られたように見える 
• clone(2)の戻り値を使ってwaitpid(2)で待つこと 
ができる
clone(2) 
clone(2)で使用するフラグはsched.hをincludeして使用できるが、 
CLONE_NEWUSERは_GNU_SOURCEをdefineする必要あり 
CLONE_NEWUTS uts namespace 
CLONE_NEWPID pid namecpace 
CLONE_NEWNS mnt namespace 
CLONE_NEWNET net namespace 
CLONE_NEWIPC ipc namespace 
CLONE_NEWUSER user namespace
unshare(2) 
• 自分を親プロセスの名前空間から分離させる 
• pid namespaceは分離できない 
• 一時期サポートされていたが2013/03/06から対象外になった 
• pidns: Don't have unshare(CLONE_NEWPID) imply 
CLONE_THREAD 
• https://github.com/torvalds/linux/commit/ 
6e556ce209b09528dbf1931cbfd5d323e1345926 
• unshare(1)の場合エラーにならないが何もおきない
setns(2) 
• 既存の名前空間に自身を参加させる 
• clone(2)、unshare(2)は親の名前空間から分離して新規の 
名前空間を持つようになる 
• 名前空間への参加には対象の名前空間のファイルディスクリプ 
タを使用する 
• 別のpid namespaceに参加する場合、プロセス自身のpidは 
変わらない 
• 子プロセスからpidが変わる
kernel 
implementation
kernel/nsproxy.c 
• namespace共通の処理を行う 
• namespaceの作成、コピーなど 
• 個々のnamespaceについてはそれぞれが実施
kernel/nsproxy.c 
• 前スライドでnamespace共通と説明したけど 
• user namespaceは扱っていない 
• user namespaceはcred.cや個々の 
namespaceが管理
struct nsproxy 
• 各namespaceのデータを保持する構造体 
! 
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_for_children; 
struct net *net_ns; 
}; 
•user namespaceはstruct credにて管理 
•struct task_structのreal_creadからuser namespaceを参照
common namespace 
structure 
• 各namespaceは以下の変数を必ず持つ 
• 参照カウンタ 
• procfsのinode番号 
• user namespace 
• user namespaceの場合は親プロセスへのポインタ
init_nsproxy 
• 最初のプロセスに設定されるnsproxy構造体 
• これ以降はfork系関数の呼び出し時にこの構 
造体の参照カウンタをインクリメント 
• namespaceを分けない場合:)
struct nsproxy 
• mount namespace以外はビルド時に設定 
struct nsproxy init_nsproxy = { 
.count = ATOMIC_INIT(1), 
.uts_ns = &init_uts_ns, 
#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) 
.ipc_ns = &init_ipc_ns, 
#endif 
.mnt_ns = NULL, 
.pid_ns_for_children = &init_pid_ns, 
#ifdef CONFIG_NET 
.net_ns = &init_net, 
#endif 
};
Initialize mnt_ns 
•init_mount_tree()からが実際の処理 
start_kernel() @init/main.c 
--> vfs_caches_init() @fs/dcache.c 
--> mnt_init() @fs/namespace.c 
--> init_mount_tree() @fs/namespace.c 
--> create_mnt_ns() @fs/namespace.c
Initialize user_ns 
• user_nsはstruct credのデータ初期化時に設定 
struct cred init_cred = { 
.usage = ATOMIC_INIT(4), 
~略~ 
.cap_bset = CAP_FULL_SET, 
.user = INIT_USER, 
.user_ns = &init_user_ns,
copy_namespace() 
• int copy_namespaces(unsigned long flags, struct 
task_struct *tsk) 
• copy_process()より呼ばれる 
• flagにCLONE_NEWXXXがセットされていなければカ 
レントプロセスのnsproxy構造体の参照カウンタを+1 
• そうでなければcreate_new_namespace()で該当する 
namespaceを作成する
switch_task_namespace() 
• void switch_task_namespaces(struct 
task_struct *tsk, struct nsproxy *new) 
• カレントプロセスの名前空間切り替えを行う 
• unshare(2)で使用 
• プロセスのexit()時にも使用 
• 切り替え先のnamespaceにNULLを設定
switch_task_namespace 
• namespaceを切り替えた結果、切り替え前の 
namespaceを参照するプロセスがいなくなった 
場合 
• free_nsproxy()を呼んでnamespaceを解放
free_nsproxy() 
• void free_nsproxy(struct nsproxy *ns) 
• namespaceの解放 
• 各namespaceの参照カウンタをデクリメント 
• nsproxy構造体のインスタンス解放 
• 通常はプロセスのexit時に実行される
unshare_nsproxy_namespace() 
• int unshare_nsproxy_namespaces(unsigned 
long unshare_flags, struct nsproxy 
**new_nsp, struct cred *new_cred, struct 
fs_struct *new_fs) 
• unshare(2)の実行時に呼ばれる 
• create_new_namespace()で名前空間を新規に 
作成
procfs operations 
• namaspaceはprocfsで表現されるのでこれらを操作する関数 
を登録 
• setns(2)が使うのがこれら 
• 各namespace毎に登録 
struct proc_ns_operations { 
const char *name; 
int type; 
void *(*get)(struct task_struct *task); 
void (*put)(void *ns); 
int (*install)(struct nsproxy *nsproxy, void *ns); 
unsigned int (*inum)(void *ns); 
};
procfs operations 
• get() 
• 対象namespaceの参照カウンタを+1 
• put() 
• 対象namespaceの参照カウンタを-1 
• install() 
• 現在のnamespaceの参照カウンタを-1し、新しいnamespaceをnsproxy構 
造体にセット 
• inum() 
• namespaceのinode番号を返す
clone() 
• namespaceの操作と言ってもclone()固有で何かがあるわ 
けでは無い 
• copy_process()から以下の関数を呼ぶ 
• copy_creds() 
• user_nsのコピー/新規作成 
• copy_namespace() 
• 既存の各namespaceのコピー/新規作成
setns() 
1.入りたいnamespaceのfile構造体からinodeを取得し、該当 
namespaceのproc_ns_operations構造体取得 
2.create_new_namespace()でnsproxyの作成 
•  flagsには0を渡すので既存のnamespaceの参照カウンタ 
が増えるだけ 
3.proc_ns_operations構造体のinstall()を呼んでnsproxyに対 
象のnamespaceを設定 
4.switch_task_namespaces()でnamespaceの切り替えを実施
unshare() 
1.unshareするnamespaceの取得 
2.ファイルシステムのunshare 
3. currentタスクのfs_struct構造体がコピーされる 
4.ファイルディスクリプタのコピー 
• 開いているファイルディスクリプタをdup_fd()でコピー 
5.user namespaceの分離 
• CLONE_NEWUSERがセットされていなければ何もしない
unshare() 
! 
6.unshare_nsproxy_namespaces()でその他namespaceの分離 
• 実際の処理はcreate_new_namespaces()で実施 
7.上記までの操作で何かしら実行が行われた場合は以下の処理を実施 
• namepace(nsproxy)の変更があった場合はswitch_task_namespaces()で切 
り替え 
• fs_structをコピーした場合はcurrentタスクのfs_struct構造体切り替え 
8. ファイルディスクリプタをコピーした場合はcurrentタスクのファイルディスクリ 
プタ切り替え 
9. user_nsのunshareをした場合はstruct credの切り替え
Namespace Example 
• Namespace機能でどのように変わったか
getpid() - 2.4.37 
• task_struct構造体のメンバ変数(pid)をそのまま 
返却可能 
#define getpid() (current->pid)
getpid() - 3.16 
•プロセスが所属しているpid namespaceのpidを 
返す必要がある 
getpid() 
-> task_tgid_vnr() 
-> task_tgid() 
-> pid_vnr() 
-> task_active_pid_ns() 
-> task_pid() 
-> ns_of_pid() 
-> pid_nr_ns()
chown() - 2.4.37 
static int chown_common(struct dentry * dentry, uid_t user, 
gid_t group) 
{ 
~中略~ 
if (user == (uid_t) -1) 
user = inode->i_uid; 
if (group == (gid_t) -1) 
group = inode->i_gid; 
newattrs.ia_mode = inode->i_mode; 
newattrs.ia_uid = user; 
newattrs.ia_gid = group;
chown() - 3.16 
static int chown_common(struct path *path, uid_t user, gid_t group) 
{ 
~中略~ 
uid = make_kuid(current_user_ns(), user); 
gid = make_kgid(current_user_ns(), group); 
! 
newattrs.ia_valid = ATTR_CTIME; 
if (user != (uid_t) -1) { 
if (!uid_valid(uid)) 
return -EINVAL; 
newattrs.ia_valid |= ATTR_UID; 
newattrs.ia_uid = uid; 
} 
if (group != (gid_t) -1) { 
if (!gid_valid(gid)) 
return -EINVAL; 
newattrs.ia_valid |= ATTR_GID; 
newattrs.ia_gid = gid; 
}
Reference 
• LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術 
• http://gihyo.jp/admin/serial/01/linux_containers 
• Namespaces in Operation series 
• http://lwn.net/Articles/531114/#series_index 
• Professional Linux Kernel Architecture 
• http://www.amazon.co.jp/dp/B004T6ICZ6

Linux Namespace

  • 1.
  • 2.
    Table of Contents • Namespace overview • System calls • kernel implementation • Namespace Example
  • 3.
  • 4.
    Namespace? • リソース • 所謂コンテナ型仮想化を実現する上で重要な機 能の一つ
  • 5.
    Resource • Namespaceにおけるリソース • cpuやmemoryと言った物理的な計算資源で はない • ホスト名、ネットワーク設定、pidなどのカーネ ルが扱うデータ
  • 6.
    Feature • プロセス間でカーネルのリソースを共有 • fork(2)の実行時は親プロセスとリソースを共有 • namespace毎に独立したリソース • 名前空間の状態を変えるような処理を行っても 別の名前空間に属するプロセスには影響は及ば ない
  • 7.
    Namespace representation •名前空間はファイルとしてユーザー空間から見え る • setns(2)で利用 masami@miko:~$ ls -l /proc/self/ns total 0 dr-x--x--x 2 masami masami 0 Aug 31 00:15 . dr-xr-xr-x 8 masami masami 0 Aug 31 00:15 .. lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 ipc -> ipc:[4026531839] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 mnt -> mnt:[4026531840] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 net -> net:[4026531957] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 pid -> pid:[4026531836] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 user -> user:[4026531837] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 uts -> uts:[4026531838]
  • 8.
    Namespaces • uts • net • pid • mnt • ipc • user
  • 9.
    uts namespace •ホスト名、ドメイン名などのデータ • カーネルバージョン等もあるが変更不可 • ゲストが自ホスト名を変えてもホストOS側には 影響はでない
  • 10.
    net namespace •ネットワーク関連のリソース • Network device • IP address • Routing table • Filtering table • Port number • /proc/net • etc…
  • 11.
    pid namespace •親プロセスとは別のpidを利用可能に • namespace Aのpid:1000とnamespace Bの pid:1000は別の存在 • procfsを適切に分ければ他のnamespceのプロ セスを参照できなくなる
  • 12.
    mnt namespace •マウントしているファイルシステムを表す • 名前空間分離時は親プロセスのmnt namespace をコピー • 分離後に親プロセスがusb stickなどをマウン トしてもゲスト側からは見えない
  • 13.
    ipc namespace •System V IPCで使用するリソースを分離 • 共有メモリ、セマフォ、メッセージキュー
  • 14.
    user namespace •ホストとは別のuid/gid体系を持てる • ホストのuid/gidとゲストのuid/gidマッピングが必要 • 設定しないと65534が設定される • groupsは65534番のgroup入りする • 他のnamespaceと違い、独立していない • 他のnamespace空間は個々にuser namespaceを持っている • 各namespaceのコピー処理関数はuser nsを受け取るので get_user_ns()で参照カウントを増やしている
  • 15.
    uid/gid mapping •マッピングを行うシステムコールは無い • 以下のファイルを用いてマッピングを実施 • /proc/<pid>/uid_map • /proc/<pid>/gid_map
  • 16.
    uid/gid mapping •ゲストのuid0をホストのuid1000にマッピング • gidも同様に masami@miko:~$ ./a.out -U -M '0 1000 1' -G '0 1000 1' bash root@miko:~# id uid=0(root) gid=0(root) groups=0(root),65534 root@miko:~# cat /proc/self/uid_map /proc/self/gid_map 0 1000 1 0 1000 1 root@miko:~# touch test.txt root@miko:~# ls -la test.txt -rw-r--r-- 1 root root 0 Aug 31 12:00 test.txt root@miko:~# """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" masami@miko:~$ ls -la test.txt -rw-r--r-- 1 masami masami 0 Aug 31 12:00 test.txt masami@miko:~$
  • 17.
    In an usernamespace •マッピングなしでuser namespaceをunshare I have no name!@miko:/proc/640$ ls -la /usr/bin/sudo -rwsr-xr-x 1 65534 65534 142792 May 9 15:58 /usr/bin/sudo ! •通常の状態 I have no name!@miko:/proc/640$ exit logout masami@miko:~$ ls -la /usr/bin/sudo -rwsr-xr-x 1 root root 142792 May 9 15:58 /usr/bin/sudo
  • 18.
    how to uid/gidmapping 1. clone(2)を呼ぶ 1. CLONE_NEWUSERをflagsにセット 2. exec()系のシステムコールを呼ぶ前にマッピングを行う 1. /proc/<child process pid>/uid_map 2. /proc/<child process pid>/gid_map 3. exec()系の関数を呼んで新たなプログラムを実行
  • 19.
  • 20.
    system calls •clone(2) • unshare(2) • setns(2)
  • 21.
    clone(2) • clone(2)でpidnamespaceを分離した場 • 子プロセスから見ると自分のpidは1として見える • 親プロセスからは親プロセスのpid namespaceでの pidが振られたように見える • clone(2)の戻り値を使ってwaitpid(2)で待つこと ができる
  • 22.
    clone(2) clone(2)で使用するフラグはsched.hをincludeして使用できるが、 CLONE_NEWUSERは_GNU_SOURCEをdefineする必要あり CLONE_NEWUTS uts namespace CLONE_NEWPID pid namecpace CLONE_NEWNS mnt namespace CLONE_NEWNET net namespace CLONE_NEWIPC ipc namespace CLONE_NEWUSER user namespace
  • 23.
    unshare(2) • 自分を親プロセスの名前空間から分離させる • pid namespaceは分離できない • 一時期サポートされていたが2013/03/06から対象外になった • pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD • https://github.com/torvalds/linux/commit/ 6e556ce209b09528dbf1931cbfd5d323e1345926 • unshare(1)の場合エラーにならないが何もおきない
  • 24.
    setns(2) • 既存の名前空間に自身を参加させる • clone(2)、unshare(2)は親の名前空間から分離して新規の 名前空間を持つようになる • 名前空間への参加には対象の名前空間のファイルディスクリプ タを使用する • 別のpid namespaceに参加する場合、プロセス自身のpidは 変わらない • 子プロセスからpidが変わる
  • 25.
  • 26.
    kernel/nsproxy.c • namespace共通の処理を行う • namespaceの作成、コピーなど • 個々のnamespaceについてはそれぞれが実施
  • 27.
    kernel/nsproxy.c • 前スライドでnamespace共通と説明したけど • user namespaceは扱っていない • user namespaceはcred.cや個々の namespaceが管理
  • 28.
    struct nsproxy •各namespaceのデータを保持する構造体 ! 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_for_children; struct net *net_ns; }; •user namespaceはstruct credにて管理 •struct task_structのreal_creadからuser namespaceを参照
  • 29.
    common namespace structure • 各namespaceは以下の変数を必ず持つ • 参照カウンタ • procfsのinode番号 • user namespace • user namespaceの場合は親プロセスへのポインタ
  • 30.
    init_nsproxy • 最初のプロセスに設定されるnsproxy構造体 • これ以降はfork系関数の呼び出し時にこの構 造体の参照カウンタをインクリメント • namespaceを分けない場合:)
  • 31.
    struct nsproxy •mount namespace以外はビルド時に設定 struct nsproxy init_nsproxy = { .count = ATOMIC_INIT(1), .uts_ns = &init_uts_ns, #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) .ipc_ns = &init_ipc_ns, #endif .mnt_ns = NULL, .pid_ns_for_children = &init_pid_ns, #ifdef CONFIG_NET .net_ns = &init_net, #endif };
  • 32.
    Initialize mnt_ns •init_mount_tree()からが実際の処理 start_kernel() @init/main.c --> vfs_caches_init() @fs/dcache.c --> mnt_init() @fs/namespace.c --> init_mount_tree() @fs/namespace.c --> create_mnt_ns() @fs/namespace.c
  • 33.
    Initialize user_ns •user_nsはstruct credのデータ初期化時に設定 struct cred init_cred = { .usage = ATOMIC_INIT(4), ~略~ .cap_bset = CAP_FULL_SET, .user = INIT_USER, .user_ns = &init_user_ns,
  • 34.
    copy_namespace() • intcopy_namespaces(unsigned long flags, struct task_struct *tsk) • copy_process()より呼ばれる • flagにCLONE_NEWXXXがセットされていなければカ レントプロセスのnsproxy構造体の参照カウンタを+1 • そうでなければcreate_new_namespace()で該当する namespaceを作成する
  • 35.
    switch_task_namespace() • voidswitch_task_namespaces(struct task_struct *tsk, struct nsproxy *new) • カレントプロセスの名前空間切り替えを行う • unshare(2)で使用 • プロセスのexit()時にも使用 • 切り替え先のnamespaceにNULLを設定
  • 36.
    switch_task_namespace • namespaceを切り替えた結果、切り替え前の namespaceを参照するプロセスがいなくなった 場合 • free_nsproxy()を呼んでnamespaceを解放
  • 37.
    free_nsproxy() • voidfree_nsproxy(struct nsproxy *ns) • namespaceの解放 • 各namespaceの参照カウンタをデクリメント • nsproxy構造体のインスタンス解放 • 通常はプロセスのexit時に実行される
  • 38.
    unshare_nsproxy_namespace() • intunshare_nsproxy_namespaces(unsigned long unshare_flags, struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs) • unshare(2)の実行時に呼ばれる • create_new_namespace()で名前空間を新規に 作成
  • 39.
    procfs operations •namaspaceはprocfsで表現されるのでこれらを操作する関数 を登録 • setns(2)が使うのがこれら • 各namespace毎に登録 struct proc_ns_operations { const char *name; int type; void *(*get)(struct task_struct *task); void (*put)(void *ns); int (*install)(struct nsproxy *nsproxy, void *ns); unsigned int (*inum)(void *ns); };
  • 40.
    procfs operations •get() • 対象namespaceの参照カウンタを+1 • put() • 対象namespaceの参照カウンタを-1 • install() • 現在のnamespaceの参照カウンタを-1し、新しいnamespaceをnsproxy構 造体にセット • inum() • namespaceのinode番号を返す
  • 41.
    clone() • namespaceの操作と言ってもclone()固有で何かがあるわ けでは無い • copy_process()から以下の関数を呼ぶ • copy_creds() • user_nsのコピー/新規作成 • copy_namespace() • 既存の各namespaceのコピー/新規作成
  • 42.
    setns() 1.入りたいnamespaceのfile構造体からinodeを取得し、該当 namespaceのproc_ns_operations構造体取得 2.create_new_namespace()でnsproxyの作成 •  flagsには0を渡すので既存のnamespaceの参照カウンタ が増えるだけ 3.proc_ns_operations構造体のinstall()を呼んでnsproxyに対 象のnamespaceを設定 4.switch_task_namespaces()でnamespaceの切り替えを実施
  • 43.
    unshare() 1.unshareするnamespaceの取得 2.ファイルシステムのunshare 3. currentタスクのfs_struct構造体がコピーされる 4.ファイルディスクリプタのコピー • 開いているファイルディスクリプタをdup_fd()でコピー 5.user namespaceの分離 • CLONE_NEWUSERがセットされていなければ何もしない
  • 44.
    unshare() ! 6.unshare_nsproxy_namespaces()でその他namespaceの分離 • 実際の処理はcreate_new_namespaces()で実施 7.上記までの操作で何かしら実行が行われた場合は以下の処理を実施 • namepace(nsproxy)の変更があった場合はswitch_task_namespaces()で切 り替え • fs_structをコピーした場合はcurrentタスクのfs_struct構造体切り替え 8. ファイルディスクリプタをコピーした場合はcurrentタスクのファイルディスクリ プタ切り替え 9. user_nsのunshareをした場合はstruct credの切り替え
  • 45.
    Namespace Example •Namespace機能でどのように変わったか
  • 46.
    getpid() - 2.4.37 • task_struct構造体のメンバ変数(pid)をそのまま 返却可能 #define getpid() (current->pid)
  • 47.
    getpid() - 3.16 •プロセスが所属しているpid namespaceのpidを 返す必要がある getpid() -> task_tgid_vnr() -> task_tgid() -> pid_vnr() -> task_active_pid_ns() -> task_pid() -> ns_of_pid() -> pid_nr_ns()
  • 48.
    chown() - 2.4.37 static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { ~中略~ if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) group = inode->i_gid; newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group;
  • 49.
    chown() - 3.16 static int chown_common(struct path *path, uid_t user, gid_t group) { ~中略~ uid = make_kuid(current_user_ns(), user); gid = make_kgid(current_user_ns(), group); ! newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { if (!uid_valid(uid)) return -EINVAL; newattrs.ia_valid |= ATTR_UID; newattrs.ia_uid = uid; } if (group != (gid_t) -1) { if (!gid_valid(gid)) return -EINVAL; newattrs.ia_valid |= ATTR_GID; newattrs.ia_gid = gid; }
  • 50.
    Reference • LXCで学ぶコンテナ入門-軽量仮想化環境を実現する技術 • http://gihyo.jp/admin/serial/01/linux_containers • Namespaces in Operation series • http://lwn.net/Articles/531114/#series_index • Professional Linux Kernel Architecture • http://www.amazon.co.jp/dp/B004T6ICZ6