資源隔離之 Linux namespace
不同的進程是共享內核資源的。好比說大家住在同一個小區,雖然到家後關起門來誰都不影響誰。但公共場所就沒辦法了,如果有人破壞環境,那麼勢必會影響到其他人。
內核資源在這裡就像是公共場所。ns 就是把公共場所隔離開來,你扔煙頭到地上隻影響你自己,其他人都看不到。這裡的隔離不是說把公共場所分成幾塊兒,每個人分一小塊兒,而是每個人都有一個和原來一樣大的公共場所,就像是每個人都有一個四維空間一樣,是不是有點玄乎啊。不要緊,下面會結合幾個小例子來說明一下 :D。目前有七種 ns 類型:Linux 初始化的時候為 init 進程(進程號為1)為每個 ns 類型創建一個實例。後面其他所有進程都可以創建新的 ns 或者加入已有的 ns。
這些 ns 實例在 /proc/[pid]/ns 下面,比如說 1 號進程的 ns:Mount ns 隔離的是掛載點掛載的是文件系統。子進程創建時(clone 時使用 CLONE_NEWNS),父進程 ns 下的所有掛載點都拷貝到子進程中,Mount ns 隔離之後,Mount Point 的創建或刪除都不會在 ns 之間傳播(除非 mount 時使用了shared subtree) ,媽媽在也不用擔心我的掛載了呢。使用 Docker 啟動一個 Container,可以查看它的掛載,有很多和 Container 所在的 Host 的不一樣,因為它內部做了新的掛載,比如說 aufs 掛載到了根目錄 / 下面:#!/usr/bin/env python#coding=utf-8import osimport re#format: pid, [namespaces], cmdlinedef _get_namespace(pid): path = /proc/{0}/ns/.format(pid) namespaces = [] for ns in os.listdir(path): namespaces.append(os.readlink(path + ns)) cmdline = open(/proc/{0}/cmdline.format(pid)).read() if not cmdline: cmdline = open(/proc/{0}/comm.format(pid)).read() return (pid, namespaces, cmdline)SBIN_INIT = _get_namespace(1)OUTPUT = [SBIN_INIT]for pid in [elt for elt in os.listdir(/proc/) if re.match(d+, elt)]: output = _get_namespace(pid) if output[1] != SBIN_INIT[1]: OUTPUT.append(output)for val in OUTPUT: print {0:>10} {1} {2}.format( val[0], .join(val[1]), .join(val[2].split(x00))[:-1] )
之後我又很好奇,如何才能進入到 Container(其實不算是進入,只是加入 Container 的 ns,看到和 Container 一樣的視圖),於是就又寫了一個腳本 https://gist.github.com/wanzixyz/8dd24aa8882bb983873274a221934cbc:
#!/usr/bin/env python#coding=utf-8import argparseimport ctypesimport osCLONE_NEWNS = 0x00020000 # /* New mount namespace group */CLONE_NEWCGROUP = 0x02000000 # /* New cgroup namespace */CLONE_NEWUTS = 0x04000000 # /* New utsname namespace */CLONE_NEWIPC = 0x08000000 # /* New ipc namespace */CLONE_NEWUSER = 0x10000000 # /* New user namespace */CLONE_NEWPID = 0x20000000 # /* New pid namespace */CLONE_NEWNET = 0x40000000 # /* New network namespace */parser = argparse.ArgumentParser()parser.add_argument(--pid, type = str, help = process id)args = parser.parse_args()if not args.pid: print plz input pid.. exit(1)#setnslibc = ctypes.CDLL(libc.so.6)namespace = [ (ipc, CLONE_NEWIPC), (uts, CLONE_NEWUTS), (net, CLONE_NEWNET), (pid, CLONE_NEWPID), (mnt, CLONE_NEWNS),]for ns_type, ns_flag in namespace: fd = os.open(/proc/{0}/ns/{1}.format(args.pid, ns_type), os.O_RDONLY) ret = libc.setns(fd, ns_flag) os.close(fd) if ret == -1: print libc.setns failed exit(1)#child exec shellpid = os.fork()if pid != 0: #father os.waitpid(pid, 0)else: #child shell = os.getenv(SHELL) os.execl(shell, os.path.basename(shell))
這裡進入進程 15320 看一下:
腫么樣親,是不是很簡單了呢? :D推薦閱讀:
※關機和開機的時候計算機都具體在幹什麼,為什麼需要一段時間而不是瞬間開關機?
※Linux 伺服器有必要開啟 iptables 防火牆么?
※linux伺服器,發現在網上運行總會莫名其妙的主動丟包,向知乎大神們請教?
※2017最新十佳黑客系統
※怎樣才算有自主知識產權的操作系統?研發「自主知識產權」的操作系統對中國的社會和經濟有何意義?