歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux 內核進程管理之進程ID

Linux 內核使用 task_struct 數據結構來關聯所有與進程有關的數據和結構,Linux 內核所有涉及到進程和程序的所有算法都是圍繞該數據結構建立的,是內核中最重要的數據結構之一。該數據結構在內核文件 include/linux/sched.h 中定義,在Linux 3.8 的內核中,該數據結構足足有 380 行之多,在這裡我不可能逐項去描述其表示的含義,本篇文章只關注該數據結構如何來組織和管理進程ID的。

進程ID類型

要想了解內核如何來組織和管理進程ID,先要知道進程ID的類型:

  • PID:這是 Linux 中在其命名空間中唯一標識進程而分配給它的一個號碼,稱做進程ID號,簡稱PID。在使用 fork 或 clone 系統調用時產生的進程均會由內核分配一個新的唯一的PID值。
  • TGID:在一個進程中,如果以CLONE_THREAD標志來調用clone建立的進程就是該進程的一個線程,它們處於一個線程組,該線程組的ID叫做TGID。處於相同的線程組中的所有進程都有相同的TGID;線程組組長的TGID與其PID相同;一個進程沒有使用線程,則其TGID與PID也相同。
  • PGID:另外,獨立的進程可以組成進程組(使用setpgrp系統調用),進程組可以簡化向所有組內進程發送信號的操作,例如用管道連接的進程處在同一進程組內。進程組ID叫做PGID,進程組內的所有進程都有相同的PGID,等於該組組長的PID。
  • SID:幾個進程組可以合並成一個會話組(使用setsid系統調用),可以用於終端程序設計。會話組中所有進程都有相同的SID。

PID 命名空間

命名空間是為操作系統層面的虛擬化機制提供支撐,目前實現的有六種不同的命名空間,分別為mount命名空間、UTS命名空間、IPC命名空間、用戶命名空間、PID命名空間、網絡命名空間。命名空間簡單來說提供的是對全局資源的一種抽象,將資源放到不同的容器中(不同的命名空間),各容器彼此隔離。命名空間有的還有層次關系,如PID命名空間,圖1 為命名空間的層次關系圖。

圖1 命名空間的層次關系

在上圖有四個命名空間,一個父命名空間衍生了兩個子命名空間,其中的一個子命名空間又衍生了一個子命名空間。以PID命名空間為例,由於各個命名空間彼此隔離,所以每個命名空間都可以有 PID 號為 1 的進程;但又由於命名空間的層次性,父命名空間是知道子命名空間的存在,因此子命名空間要映射到父命名空間中去,因此上圖中 level 1 中兩個子命名空間的六個進程分別映射到其父命名空間的PID 號5~10。

命名空間增大了 PID 管理的復雜性,對於某些進程可能有多個PID——在其自身命名空間的PID以及其父命名空間的PID,凡能看到該進程的命名空間都會為其分配一個PID。因此就有:

  • 全局ID:在內核本身和初始命名空間中唯一的ID,在系統啟動期間開始的 init 進程即屬於該初始命名空間。系統中每個進程都對應了該命名空間的一個PID,叫全局ID,保證在整個系統中唯一。
  • 局部ID:對於屬於某個特定的命名空間,它在其命名空間內分配的ID為局部ID,該ID也可以出現在其他的命名空間中。

進程ID管理數據結構

Linux 內核在設計管理ID的數據結構時,要充分考慮以下因素:

  1. 如何快速地根據進程的 task_struct、ID類型、命名空間找到局部ID
  2. 如何快速地根據局部ID、命名空間、ID類型找到對應進程的 task_struct
  3. 如何快速地給新進程在可見的命名空間內分配一個唯一的 PID

如果將所有因素考慮到一起,將會很復雜,下面將會由簡到繁設計該結構。

一個PID對應一個task_struct

如果先不考慮進程之間的關系,不考慮命名空間,僅僅是一個PID號對應一個task_struct,那麼我們可以設計這樣的數據結構:

struct task_struct {
    //...
    struct pid_link pids;
    //...
};

struct pid_link {
    struct hlist_node node;  
    struct pid *pid;          
};

struct pid {
    struct hlist_head tasks;        //指回 pid_link 的 node
    int nr;                       //PID
    struct hlist_node pid_chain;    //pid hash 散列表結點
};

每個進程的 task_struct 結構體中有一個指向 pid 結構體的指針,pid 結構體包含了 PID 號。結構示意圖如圖2。

圖2 一個task_struct對應一個PID

圖中還有兩個結構上面未提及:

  • pid_hash[]: 這是一個hash表的結構,根據 pid 的 nr 值哈希到其某個表項,若有多個 pid 結構對應到同一個表項,這裡解決沖突使用的是散列表法。這樣,就能解決開始提出的第2個問題了,根據PID值怎樣快速地找到task_struct結構體:
    • 首先通過 PID 計算 pid 掛接到哈希表 pid_hash[] 的表項
    • 遍歷該表項,找到 pid 結構體中 nr 值與 PID 值相同的那個 pid
    • 再通過該 pid 結構體的 tasks 指針找到 node
    • 最後根據內核的 container_of 機制就能找到 task_struct 結構體
  • pid_map:這是一個位圖,用來唯一分配PID值的結構,圖中灰色表示已經分配過的值,在新建一個進程時,只需在其中找到一個為分配過的值賦給 pid 結構體的 nr,再將pid_map 中該值設為已分配標志。這也就解決了上面的第3個問題——如何快速地分配一個全局的PID。

至於上面的第1個問題就更加簡單,已知 task_struct 結構體,根據其 pid_link 的 pid 指針找到 pid 結構體,取出其 nr 即為 PID 號。

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-12/110233p2.htm

Copyright © Linux教程網 All Rights Reserved