1. 進程標識
在Linux中,在同一命名空間中,進程總是會被分配唯一的號碼來標識它們,該號碼被稱作進程ID號,簡稱PID。對於進程和線程來說,他們都是用同樣的結構體task_struct來表示,可以都被看做是進程,在內核中,他們也有自己的進程ID,也就是說會有唯一的進程ID來標識它們;但是對於線程來說,它還會有自己的線程ID,在task_struct 結構體中,用字段tgid來表示,pid代表線程所在線程組組長的PID,對於進程來說,pid和tgid是一樣的。
struct task_struct {
...
pid_t pid;----------進程ID
pid_t tgid;---------線程ID
...
}
結合線程組和進程組的知識,現在舉例來介紹進程相關PID的關係,假設進程a是進程組組長,進程a用fork產生了進程b、c,然後進程c用clone產生了線程c0、c1、c2,則c、c0、c1和c2組成一個線程組,組長是c,那麼他們相關的PID關係如下:
comm | PID | PPID | PGID | TGID |
---|---|---|---|---|
a | 3 | 1 | 1 | |
b | 10 | 3 | 1 | |
c | 11 | 3 | 1 | 11 |
c0 | 20 | 3 | 11 | |
c1 | 21 | 3 | 11 | |
c2 | 22 | 3 | 11 |
2. PID結構體
在linux中,有進程組、會話組這些關係,那麼就得有個結構體來把相關進程都串聯起來,這個結構體就是struct pid。
struct pid
{
atomic_t count;
unsigned int level;-----------------------命名空間層
/* lists of tasks that use this pid */
struct hlist_head tasks[PIDTYPE_MAX];-----鏈表
struct rcu_head rcu;
struct upid numbers[1];-------------------對應命名空間的upid
};
上述結構體有個關鍵的字段是tasks數組,這個數組有PIDTYPE_MAX項,分別是PID(是PIDTYPE_PID的簡寫,以下相同)、PGID、SID,tasks[PID]鏈表鏈接的是創建此pid結構體的進程,tasks[PGID]鏈接的是這個進程組中的所有進程,tasks[SID]鏈接的是這個會話組中的所有進程。相關關係如下圖:
在上圖中有前提條件是:A是一個進程組組長。如果A不是進程組組長,那麼進程A的pids[PID]->pid就不會指向pid:A,而是會指向他所在的進程組組長創建的pid:X(X代表進程組組長)。
從圖中可以看出進程A用fork創建了兩個進程B和C,然後B和C都加入了結構體pid:A的鏈表tasks[PGID]中,在task_struct結構體中有一個專門的結構體字段pids來管理這些關係
struct task_struct {
...
struct pid_link pids[PIDTYPE_MAX];------有三個數組
...
}
struct pid_link
{
struct hlist_node node;----------鏈表節點,加入進程組和會話組的結點
struct pid *pid;-----------------pid結構體
};
3. 命名空間
命名空間提供了虛擬化的一種輕量級形式,使得我們可以從不同的方面來查看運行系統的全局屬性。
創建進程的時候,根據相應的命名空間,就會創建對應空間的upid,而且會分配一個唯一的數字id,在字段upid->nr中表示。假如進程所在的命名空間有3層,那麼就會在每一個空間創建一個upid,並且會把這三個upid都加入全局散列表pid_hash。雖然有3個upid,但是都是對應這同一個struct pid。在最頂端的層level0,是全局層,每一個子空間層最終都會映射到此層,子空間會層層映射父空間。
struct upid {
/* Try to keep pid_chain in the same cacheline as nr for find_vpid */
int nr;----------------------------在對應空間的id號
struct pid_namespace *ns;----------所在的空間
struct hlist_node pid_chain;-------加入到全局pid_hash
};
命名空間與upid的關係如下圖: