Linux內核中namespace之PID namespace


前面看了LInux PCI設備初始化,看得有點暈,就轉手整理下之前寫的筆記,同時休息一下!!~(@^_^@)~


 

這片文章是之前寫的,其中參考了某些大牛們的博客!!

PID框架的設計

一個框架的設計會考慮很多因素,相信分析過Linux內核的讀者來說會發現,內核的大量數據結構被哈希表和鏈表鏈接起來,最最主要的目的就是在於查找。可想而知一個好的框架,應該要考慮到檢索速度,還有考慮功能的划分。那么在PID框架中,需要考慮以下幾個因素.

  • 如何通過task_struct快速找到對應的pid

  • 如何通過pid快速找到對應的task_struct

  • 如何快速的分配一個唯一的pid

這些都是PID框架設計的時候需要考慮的一些基本的因素。也正是這些因素將PID框架設計的愈加復雜。

原始的PID框架

先考慮的簡單一點,一個進程對應一個pid

struct task_struct {

  .....

  pid_t pid;

  .....

}

引入hlist和pid位圖

struct task_struct *pidhash[PIDHASH_SZ];

這樣就很方便了,再看看PID框架設計的一些因素是否都滿足了,如何分配一個唯一的pid呢,連續遞增?,那么前面分配的進程如果結束了,那么分配的pid就需要回收掉,直到分配到PID的最大值,然后從頭再繼續。好吧,這或許是個辦法,但是是不是需要標記一下那些pid可用呢?到此為此這看起來似乎是個解決方案,但是考慮到這個方案是要放進內核,開發linux的那幫家伙肯定會想近一切辦法進行優化的,的確如此,他們使用了pid位圖,但是基本思想沒有變,同樣需要標記pid是否可用,只不過使用pid位圖的方式更加節約內存.想象一下,通過將每一位設置為0或者是1,可以用來表示是否可用,第1位的0和1用來表示pid為1是否可用,以此類推.到此為此一個看似還不錯的pid框架設計完成了,下圖是目前整個框架的整體效果.

 

 

用上面大牛的引言來引入今天的主題:

 其實PID namespace帶來的好處遠不止於此,其中最重要的就是對輕量級虛擬化的支持。當前炙手可熱的docker就是基於Linux 內核中命名空間的原理。有了PID命名空間,我們就可以保證進程的隔離性,至少在進程的角度,可以按照namespace的方式去組織,不同namespace的進程互不干擾。這也是筆者要分析下底層虛擬化支持的初衷之一!

言歸正傳:

引入進程PID命名空間后的PID框架

隨着內核不斷的添加新的內核特性,尤其是PID Namespace機制的引入,這導致PID存在命名空間的概念,並且命名空間還有層級的概念存在,高級別的可以被低級別的看到,這就導致高級別的進程有多個PID,比如說在默認命名空間下,創建了一個新的命名空間,占且叫做level1,默認命名空間這里稱之為level0,在level1中運行了一個進程在level1中這個進程的pid為1,因為高級別的pid namespace需要被低級別的pid namespace所看見,所以這個進程在level0中會有另外一個pid,為xxx.套用上面說到的pid位圖的概念,可想而知,對於每一個pid namespace來說都應該有一個pidmap,上文中提到的level1進程有兩個pid一個是1,另一個是xxx,其中pid為1是在level1中的pidmap進行分配的,pid為xxx則是在level0的pidmap中分配的. 下面這幅圖是整個pidnamespace的一個框架

 

 

可以看到這里出現了一個層次結構,即一個命名空間可以有其子命名空間,子命名空間中的進程同樣會出現在父命名空間中,只是進程ID是不同的。看PID的結構:

1 struct pid
2 {
3     atomic_t count;
4     unsigned int level;
5     /* lists of tasks that use this pid */
6     struct hlist_head tasks[PIDTYPE_MAX];
7     struct rcu_head rcu;
8     struct upid numbers[1];
9 };

 

count表示這個PID的引用計數,同一個PID結構可以為多個進程所共享;

level表示該PID所在的層級;

tasks是一個HASH數組,每一項都是一個鏈表頭。分別是PID鏈表頭,進程組ID表頭,會話ID表頭;

rcu用於保護指針引用;

numbers是一個UPID數組,記錄對應層級的命名空間中的UPID,所以可以想到,該PID處於第幾層,那么這個數組應該有幾項(當然都是從0開始)。

看下UPID結構:

1 struct upid {
2     /* Try to keep pid_chain in the same cacheline as nr for find_vpid */
3     int nr;
4     struct pid_namespace *ns;
5     struct hlist_node pid_chain;
6 };

 

UPID相比之下就簡單的多,或者說這才是真正的PID。

nr表示ID號;

namespace指向該UPID所在命名空間的namespace結構;

pid_chain是一個鏈表,系統會把UPID 的nr和namespace的ns經過某種hash,得到在一個全局的Hash數組中的下標,此UPID便會加入到對應下標的鏈表中。

看下進程中對於PID是如何應用的:

1 struct task_struct{
2 ...
3 struct pid_link pids[PIDTYPE_MAX];
4 ...
5 }

 進程中包含一個pid_link結構的數組,我們還是先看一個pid_link結構:

1 struct pid_link
2 {
3     struct hlist_node node;
4     struct pid *pid;
5 };

 

node作為一個節點加入到所有引用同一PID結構的進程鏈表中;

pid指向該進程引用的PID結構。

也許到這里還是顯得關系有點紊亂,那么看一下下面的圖:

沒錯,這個圖是我畫的!!哈哈,可花了我不少時間了,希望對大家理解進程結構和PID以及UPID之間的關系有所幫助。貼出這個圖突然發現沒有什么好解釋的了,各種關系圖中已經表明。不過需要注意的是最下面的UPID我僅僅用一個框框代表了,其實是Numbers指向UPID結構,而讓UPID加入到鏈表中的是結構中的pid_chain;還有一個問題就是UPID那一組鏈表並不是表示同一PID下的所有UPID,根據內核源代碼,這里只是處理沖突的一種方式,而這里畫成鏈表僅僅是為了表示這里是以鏈表存在的。

下面說下PID位圖:

每一個namespace都對應一個map,用於分配pid號。這里包含兩個成員,nr_free表示可用的ID號數量,第二個是一個指向一個頁面的指針。這么設計有其自身的合理性。默認情況下page指向一個頁面,而一個頁面是4kb的大小,即4096個字節,也就是4096*8bit,每一位表示一個pid號,一個頁面就可以表示32768個進程ID,普通情況下絕對是夠了,即使特殊情況不夠,那么還可以動態擴展,這就是其合理性所在。

1 struct pidmap {
2        atomic_t nr_free;
3        void *page;
4 };

 

 

 這里是pid_namespace結構:

 1 struct pid_namespace {
 2     struct kref kref;
 3     struct pidmap pidmap[PIDMAP_ENTRIES];//命名空間對應的PID位圖
 4     int last_pid;//上次分配的PID,便於下次分配
 5     unsigned int nr_hashed;
 6     struct task_struct *child_reaper;
 7     struct kmem_cache *pid_cachep;
 8     unsigned int level;//命名空間所在層級
 9     struct pid_namespace *parent;//指向父命名空間的指針
10 #ifdef CONFIG_PROC_FS
11     struct vfsmount *proc_mnt;
12     struct dentry *proc_self;
13 #endif
14 #ifdef CONFIG_BSD_PROCESS_ACCT
15     struct bsd_acct_struct *bacct;
16 #endif
17     struct user_namespace *user_ns;
18     struct work_struct proc_work;
19     kgid_t pid_gid;
20     int hide_pid;
21     int reboot;    /* group exit code if this pidns was rebooted */
22     unsigned int proc_inum;
23 };

 

 

 下一節會結合LInux內核源代碼分析下PID的具體分配情況

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM