1. 程式人生 > >Linux核心的namespace機制分析

Linux核心的namespace機制分析

原文地址:http://blog.chinaunix.net/uid-20788636-id-4479145.html

1.  Linux核心namespace機制

Linux Namespaces機制提供一種資源隔離方案。PID,IPC,Network等系統資源不再是全域性性的,而是屬於某個特定的Namespace。每個namespace下的資源對於其他namespace下的資源都是透明,不可見的。因此在作業系統層面上看,就會出現多個相同pid的程序。系統中可以同時存在兩個程序號為0,1,2的程序,由於屬於不同的namespace,所以它們之間並不衝突。而在使用者層面上只能看到屬於使用者自己

namespace下的資源,例如使用ps命令只能列出自己namespace下的程序。這樣每個namespace看上去就像一個單獨的Linux系統。

2 .  Linux核心中namespace結構體

Linux核心中提供了多個namespace,其中包括fs (mount), uts, network, sysvipc, 等。一個程序可以屬於多個namesapce,既然namespace和程序相關,那麼在task_struct結構體中就會包含和namespace相關聯的變數。在task_struct 結構中有一個指向namespace結構體的指標nsproxy

struct task_struct {

……..

/* namespaces */

         struct nsproxy *nsproxy;

…….

}

再看一下nsproxy是如何定義的,在include/linux/nsproxy.h檔案中,這裡一共定義了5個各自的名稱空間結構體,在該結構體中定義了5個指向各個型別namespace的指標,由於多個程序可以使用同一個namespace,所以nsproxy可以共享使用,count欄位是該結構的引用計數。

/* 'count' is the number of tasks holding a reference.

 * The count for each namespace, then, will be the number

 * of nsproxies pointing to it, not the number of tasks.

 * The nsproxy is shared by tasks which share all namespaces.

 * As soon as a single namespace is cloned or unshared, the

 * nsproxy is copied

*/

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;

};

(1)     UTS名稱空間包含了執行核心的名稱、版本、底層體系結構型別等資訊。UTSUNIX Timesharing System的簡稱。

(2)     儲存在struct ipc_namespace中的所有與程序間通訊(IPC)有關的資訊。

(3)     已經裝載的檔案系統的檢視,在struct mnt_namespace中給出。

(4)     有關程序ID的資訊,由struct pid_namespace提供。

(5)     struct net_ns包含所有網路相關的名稱空間引數。

系統中有一個預設的nsproxyinit_nsproxy,該結構在task初始化是也會被初始化。#define INIT_TASK(tsk)  \

{

         .nsproxy   = &init_nsproxy,      

}

其中init_nsproxy的定義為:

static struct kmem_cache *nsproxy_cachep;

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

};

對於         .mnt_ns   沒有進行初始化,其餘的namespace都進行了系統預設初始。

3. 使用clone建立自己的Namespace

如果要建立自己的名稱空間,可以使用系統呼叫clone(),它在使用者空間的原型為

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg)

這裡fn是函式指標,這個就是指向函式的指標,, child_stack是為子程序分配系統堆疊空間,flags就是標誌用來描述你需要從父程序繼承那些資源, arg就是傳給子程序的引數也就是fn指向的函式引數。下面是flags可以取的值。這裡只關心和namespace相關的引數。

CLONE_FS          子程序與父程序共享相同的檔案系統,包括root、當前目錄、umask

CLONE_NEWNS     clone需要自己的名稱空間時設定這個標誌,不能同時設定CLONE_NEWSCLONE_FS

Clone()函式是在libc庫中定義的一個封裝函式,它負責建立新輕量級程序的堆疊並且呼叫對程式設計者隱藏了clone系統條用。實現clone()系統呼叫的sys_clone()服務例程並沒有fnarg引數。封裝函式把fn指標存放在子程序堆疊的每個位置處,該位置就是該封裝函式本身返回地址存放的位置。Arg指標正好存放在子程序堆疊中的fn的下面。當封裝函式結束時,CPU從堆疊中取出返回地址,然後執行fn(arg)函式。

#includeint clone(int (*fn)(void *), void *child_stack,int flags, void *arg, .../* pid_t *ptid, struct user_desc *tls, pid_t *ctid

long clone(unsigned long flags, void *child_stack,void *ptid, void *ctid,struct pt_regs *regs);<span color:#181818;"="" style="word-wrap: break-word; font-size: 12pt;">

我們在Linux核心中看到的實現函式,是經過libc庫進行封裝過的,在Linux核心中的fork.c檔案中,有下面的定義,最終呼叫的都是do_fork()函式。

#ifdef __ARCH_WANT_SYS_CLONE

#ifdef CONFIG_CLONE_BACKWARDS

SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,

                    int __user *, parent_tidptr,

                    int, tls_val,

                    int __user *, child_tidptr)

#elif defined(CONFIG_CLONE_BACKWARDS2)

SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,

                    int __user *, parent_tidptr,

                    int __user *, child_tidptr,

                    int, tls_val)

#elif defined(CONFIG_CLONE_BACKWARDS3)

SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,

                   int, stack_size,

                   int __user *, parent_tidptr,

                   int __user *, child_tidptr,

                   int, tls_val)

#else

SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,

                    int __user *, parent_tidptr,

                    int __user *, child_tidptr,

                    int, tls_val)

#endif

{

         return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);

}

#endif

3.1  do_fork函式

clone()函式中呼叫do_fork函式進行真正的處理,在do_fork函式中呼叫copy_process程序處理。

long do_fork(unsigned long clone_flags,

               unsigned long stack_start,

               unsigned long stack_size,

               int __user *parent_tidptr,

               int __user *child_tidptr)

{

         struct task_struct *p;

         int trace = 0;

         long nr;

         /*

          * Determine whether and which event to report to ptracer.  When

          * called from kernel_thread or CLONE_UNTRACED is explicitly

          * requested, no event is reported; otherwise, report if the event

          * for the type of forking is enabled.

          */

         if (!(clone_flags & CLONE_UNTRACED)) {

                   if (clone_flags & CLONE_VFORK)

                            trace = PTRACE_EVENT_VFORK;

                   else if ((clone_flags & CSIGNAL) != SIGCHLD)

                            trace = PTRACE_EVENT_CLONE;

                   else

                            trace = PTRACE_EVENT_FORK;

                   if (likely(!ptrace_event_enabled(current, trace)))

                            trace = 0;

         }

         p = copy_process(clone_flags, stack_start, stack_size,

                             child_tidptr, NULL, trace);

         /*

          * Do this prior waking up the new thread - the thread pointer

          * might get invalid after that point, if the thread exits quickly.

          */

         if (!IS_ERR(p)) {

                   struct completion vfork;

                   struct pid *pid;

                   trace_sched_process_fork(current, p);

                   pid = get_task_pid(p, PIDTYPE_PID);

                   nr = pid_vnr(pid);

                   if (clone_flags & CLONE_PARENT_SETTID)

                            put_user(nr, parent_tidptr);

                   if (clone_flags & CLONE_VFORK) {

                            p->vfork_done = &vfork;

                            init_completion(&vfork);

                            get_task_struct(p);

                   }

                   wake_up_new_task(p);

                   /* forking complete and child started to run, tell ptracer */

                   if (unlikely(trace))

                            ptrace_event_pid(trace, pid);

                   if (clone_flags & CLONE_VFORK) {

                            if (!wait_for_vfork_done(p, &vfork))

                                     ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);

                   }

                   put_pid(pid);

         } else {

                   nr = PTR_ERR(p);

         }

         return nr;

}

3.2  copy_process函式

copy_process函式中呼叫copy_namespaces函式。

static struct task_struct *copy_process(unsigned long clone_flags,

                                               unsigned long stack_start,

                                               unsigned long stack_size,

                                               int __user *child_tidptr,

                                               struct pid *pid,

                                               int trace)

{

          int retval;

          struct task_struct *p;

/*下面的程式碼是對clone_flag標誌進行檢查,有部分表示是互斥的,例如CLONE_NEWNSCLONENEW_FS*/

          if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))

                   return ERR_PTR(-EINVAL);

          if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS))

                   return ERR_PTR(-EINVAL);

          if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))

                   return ERR_PTR(-EINVAL);

          if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))

                   return ERR_PTR(-EINVAL);

          if ((clone_flags & CLONE_PARENT) &&

相關推薦

linux核心kallsyms機制分析

轉載自:http://blog.chinaunix.net/uid-27717694-id-3985448.html 一、前言 Linux核心是一個整體結構,而模組是插入到核心中的外掛。儘管核心不是一個可安裝模組,但為了方便起見,Linux把核心也看作一個模組。那麼模組與模組之間如何進行互

Linux核心namespace機制分析

原文地址:http://blog.chinaunix.net/uid-20788636-id-4479145.html 1.  Linux核心namespace機制 Linux Namespaces機制提供一種資源隔離方案。PID,IPC,Ne

linux RCU鎖機制分析

nbsp -i html 都在 而且 content 服務器 單詞 插入 openVswitch(OVS)源代碼之linux RCU鎖機制分析 分類: linux內核 | 標簽: 雲計算,openVswitch,linux內核,RCU鎖機制 | 作者: yuzh

Linux之poll機制分析

for 可用 報告 超時時間 程序 訪問 events blank linux 應用程序訪問1個設備文件時可用阻塞/非阻塞方式.如果是使用阻塞方式,則直接調用open()、read()、write(),但是在驅動程序層會判斷是否可讀/可寫,如果不可讀/不可寫,則將當前進程休

2018-2019-1 20189206 《Linux核心原理與分析》第二週作業

Linux核心分析 第二週學習 知識總結 作業系統與核心 作業系統 指在整個系統中負責完成最基本功能和系統管理的那些部分 核心 實際是作業系統的內在核心 核心獨立於普通應用程式,擁有受保護的記憶體空間和訪問硬體裝置的所有許可權,這種空間被稱為核心空間 當核心執行的時

2018-2019-1 20189219《Linux核心原理與分析》第四周作業

1. 首先設定斷點在start_kernel函式處,使用c命令之後提示進入了該啟動函式,如圖: 圖 2. 進入函式之後發現這裡面的大多數函式並不能從名字上看出它們的意義,只能一步一步的試,於是我在init_task這個重要的程序變數處設定斷點b 510,然後c,發現menuOS竟然開始跑了。但是結果卻不盡

2018-2019-1 20189221《Linux核心原理與分析》第四周作業

2018-2019-1 20189221《Linux核心原理與分析》第四周作業 教材學習:《庖丁解牛Linux核心分析》 第 3 章 MenuOS的構造 計算機三大法寶:儲存程式計算機,函式呼叫堆疊,中斷 作業系統兩把寶劍:中斷上下文,程序上下文 Linux核心原始碼: Linux核心使用的是第二週

2018-2019-1 20189203《Linux核心原理與分析》第四周作業

第一部分 課本學習 核心版本號:Linux核心自2013年12月起,就以A.B.C.D的方式命名。A和B變得無關緊要,C是核心的真實版本。每一個版本的變化都會帶來新的特性,如內部API的變化等,改動的程式碼數量常常上萬行。D是安全補丁和bug修復。 幾個關鍵的目錄: Arch:與體系結構相關的子目

Linux核心原理與分析》第四周作業

課本:第3章 MenuOS的構造 內容總結 計算機的“三大法寶” 儲存程式計算機 函式呼叫堆疊 中斷 作業系統的“兩把寶劍” 中斷上下文切換:儲存現場和恢復現場 程序上下文切換 在接觸linux核心原始碼時,linux是基於一個穩定版

2018-2019-1 20189206 《Linux核心原理與分析》第四周作業

linux核心分析學習筆記 ——第三章 MenuOS的構造 計算機的“三大法寶”和作業系統的“兩把寶劍” 三大法寶 程式儲存計算機 即馮諾依曼體系結構,基本上是所有計算機的基礎性的邏輯框架 函式呼叫堆疊 高階語言可以執行的起點就是函式呼叫堆疊 中斷機制 中斷上下文 儲存

2018-2019-1 20189204《Linux核心原理與分析》第四周作業

《庖丁解牛》第3章——MenuOS的構造 3.1Linux核心原始碼簡介 計算機三大法寶:儲存程式計算機、系統呼叫堆疊、中斷 作業系統兩把寶劍:中斷切換上下文、程序切換上下文 Linux核心原始碼的目錄結構 其中,arch目錄是與體系結構相關的子目錄列表,裡面存放了許多CPU體系結構的相關程式碼,使

2018-2019-1 20189205 《Linux核心原理與分析》 第四周作業

MenuOS的構造 Linux核心 本週學習了Linux核心的基本目錄結構,通過qemu構建了簡單的Linux核心,並利用gdb工具進行除錯,瞭解了核心的啟動過程。 Linux的目錄結構 關鍵的目錄 arch:與體系結構相關的子目錄列表。 block:存放Linux儲存體系中關於塊裝置管理的

2018-2019-1 20189210 《LInux核心原理與分析》第四周作業

第三章 這一章接觸核心原始碼,對核心原始碼進行編譯和除錯跟蹤 一、預備知識: 核心:整個作業系統的最底層,它負責了整個硬體的驅動以及提供各種系統所需的核心功能。核心實質上是系統上面的一個檔案而已,這個檔案包含了驅動主機各項硬體的檢測程式與驅動模組。當系統讀完BIOS並載入MBR內的引導裝載程式後,就能夠載入核

2018-2019-1 20189215《Linux核心原理與分析》第二週作業

本週學習了《庖丁解牛》第1章,以及《Linux核心設計與實現》第1、2、18章。通過視訊和實驗,學會了反彙編一個簡單的C程式,也學習了Linux核心除錯的一些小技巧和printk函式。 反彙編一個簡單的C程式 程式編寫及編譯 使用vi編輯原始碼 返回值是15,我學號的後兩位。 使用

20189205 《Linux核心原理與分析》第二週作業

反編譯 在實驗樓中我編寫了如下程式碼: 通過gcc編譯,得到了如下彙編程式碼: 將其簡化為可見部分後可得到如下彙編程式碼: 5 g: 8 pushl %ebp 11 movl %esp,%ebp 13 movl 8(%eb

20189210牟健 《Linux核心原理與分析》第二週作業

本週學習了彙編指令以及通過反彙編一個小程式來了解棧的變化 寫了一個簡單的C程式,如圖所示: 通過gcc -s -o main.s main.c -m32指令將其編譯成彙編程式 開啟該彙編檔案並刪除不重要的資訊,如圖所示: 分析該彙編指令(為了方便直接用手寫畫圖,為了區分不同時期的暫存器,將其後面加了個

20189220 餘超《Linux核心原理與分析》第二週作業

計算機如何工作的 一.儲存程式計算機工作模型 馮諾依曼體系結構:核心思想為儲存程式計算機。兩個層面: (1)硬體的角度(計算機主機板):一個CPU,一塊記憶體,之間有匯流排連線。CPU內部有一個IP計算器,IP指向記憶體中的指令,並依次加一執行; (2)另一個層面,程式設計師的角度:儲存程式計算機工作模

2018-2019-1 20189213《Linux核心原理與分析》第五週作業

第四章:系統呼叫的三層機制(上) 系統呼叫的"三層皮" 分別指的是:使用者態函式(API)、system_call(中斷服務程式入口)以及sys_xyz()系統呼叫處理函式封裝例程。它們各自的作用如下: API 第一層是指Libc中定義的API,這些API封裝了系統呼叫,使用int0x80觸發一個系統

2018-2019-1 20189218《Linux核心原理與分析》第五週作業

系統呼叫的三層機制 使用者態、核心態和中斷 使用者態。較低的執行級別,只能訪問一部分記憶體,只能執行一部分指令。 核心態。高階執行級別,可以訪問任意實體記憶體,可以執行特權指令。 中斷。系統從使用者態進入核心態的主要方式。有硬體中斷和軟中斷。系統呼叫就是通過軟中斷進入核心態。 上下文切

Linux核心原理與分析》第二週作業

反彙編一個簡單的C程式 1、實驗要求 使用: gcc –S –o test.s test.c -m32 命令編譯成彙編程式碼,對彙編程式碼進行分析總結。其中test.c的具體內容如下: int g(int x) { return x + 3; } int f(int x) { return