今日はLinuxカーネルもくもく会 #3 でsetns(2)のユーザーランド側の挙動を見てみようと思って実験してみたのでメモ。
カーネル側の動作についてはTenForwardさんによる[Linux][Container] setns を UTS Namespace をネタにおっかけるが参考になります。
で、テスト用のコードを書いてみました。 まずこれはclone(2)でuts namespaceとpid namespaceを新規に作ってbashを起動するもの。
#define _GNU_SOURCE #include <sched.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> #define STACK_SIZE 8192 char stack[STACK_SIZE]; int func(void *arg) { #define NEW_HOSTNAME "testhost" sethostname(NEW_HOSTNAME, strlen(NEW_HOSTNAME)); execlp("/bin/bash", "bash", NULL); return 0; } int main(int argc, char **argv) { pid_t pid; char name[256] = { 0 }; gethostname(name, sizeof(name) - 1); printf("current host name is %s\n", name); pid = clone(func, stack + STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL); if (pid > 0) { printf("child's pid %d\n", pid); waitpid(pid, NULL, 0); } else { perror("clone"); exit(-1); } return 0; }
こちらは上のコードで作ったプロセスの名前空間に入るためのコード。setnsでpidとuts名前空間を設定してます。
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> void show_hostname(char *which) { char hostname[256] = { 0 }; gethostname(hostname, sizeof(hostname) - 1); printf("%s hostname: %s\n", which, hostname); } void show_pid(char *which) { pid_t pid = getpid(); printf("%s: pid %d\n", which, pid); } void enter_newns(char *ns, int pid) { char *path; int fd; asprintf(&path, "/proc/%d/ns/%s", pid, ns); fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); exit(-1); } free(path); if (setns(fd, 0)) { perror("setns"); exit(-1); } close(fd); } static char *namespaces[] = { "uts", "pid", }; #define NAMESPACES_SIZE sizeof(namespaces) / sizeof(namespaces[0]) int main(int argc, char **argv) { int pid, forked_pid; int i; if (argc != 2) { printf("usage %s pid\n", argv[0]); return -1; } pid = strtol(argv[1], NULL, 10); show_hostname("before"); show_pid("before"); for (i = 0; i < NAMESPACES_SIZE; i++) enter_newns(namespaces[i], pid); forked_pid = fork(); if (!forked_pid) { show_hostname("afetr child process"); show_pid("afetr child process"); _exit(0); } else if (forked_pid > 0) { waitpid(forked_pid, NULL, 0); show_hostname("after"); show_pid("after"); } else { perror("fork"); exit(-1); } return 0; }
ここからは実行結果。まずは名前空間を作る方。こちらはclone()のCLONE_NEWPIDによりpidが1から始まっているのとCLONE_NEWUTSとsethostname()によりuts名前空間の切り替えとホスト名の設定ができています。clone()を呼び出した方のプロセスからはpid1509が見えていて、clone()で作られたプロセスはpid1と認識してます。
masami@miko:~$ sudo ./create_newuts current host name is miko child's pid 1509 [root@testhost masami]# echo $$ 1 [root@testhost masami]#
こちらは名前空間に入る方。setns()の前と後でホスト名が変わっていますね。あと、これは動かしてみて初めて気づいたところですがsetns()で別のpid名前空間に入ったとしててもpidは変わっていませんが、ここでfork()をすると子プロセスはcreate_newutsのpid名前空間で管理されているのがわかります。
masami@miko:~$ sudo ./enter_ns 1509 before hostname: miko before: pid 1516 afetr child process hostname: testhost afetr child process: pid 2 after hostname: testhost after: pid 1516
やっぱり動かしてみると動きがよくわかりますね。
- 作者: Michael Kerrisk,千住治郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/12/01
- メディア: 大型本
- クリック: 14回
- この商品を含むブログ (5件) を見る