Linux內核 | 進程管理

做者:世至其美
博客地址:hqber.com
轉載須註明以上信息, 更多文章,請訪問我的博客:hqber.comlinux

1. 進程和線程

1.1 定義

進程是處於運行狀態的程序和相關資源的總稱,是資源分配的最小單位。緩存

線程是進程的內部的一個執行序列,是CPU調度的最小單位。多線程

  • 有一段可執行程序代碼
  • 有一段進程專用的系統堆棧空間系統空間堆棧
  • 進程描述符,用於描述進程的相關信息。
  • 獨立的存儲空間,也就是專有的用戶空間,相應的又會有用戶空間堆棧

Linux系統對於線程實現很是特殊,他並不區分線程和進程,線程只是一種特殊的進程罷了。從上面四點要素來看,擁有前三點而缺第四點要素的就是線程,若是徹底沒有第四點的用戶空間,那就是系統線程,若是是共享用戶空間,那就是用戶線程。併發

1.2 主要區別

進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位,因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統多個程序間併發執行的程度。編輯器

進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。ide

總結:linux中,進程和線程惟一區別是有沒有獨立的地址空間。函數

2. 進程描述符及任務結構

32位機器上,大約有1.7KB,進程描述符完整描述一個正在執行的進程的全部信息。spa

任務隊列(雙向循環鏈表)操作系統

1571475290472

進程描述符struct task_struct(源代碼 | linnux/sched.h | v5.4.net

struct task_struct {
    volatile long state;    // -1爲不可運行, 0爲可運行, >0爲已中斷
    int lock_depth;     // 鎖的深度
    unsigned int policy; // 調度策略:通常有FIFO,RR,CFS
    pid_t pid;   // 進程標識符,用來表明一個進程
    struct task_struct *parent; // 父進程
    struct list_head children;  // 子進程
    struct list_head sibling;   // 兄弟進程
}

2.1 分配進程描述符

2.1.1 slab分配器

linux採用slab分配器分配task_struct結構

目的:對象複用和緩存着色。

slab分配器動態生成task_struct,只需在棧底(相對於向下增加的棧)或棧頂(相對於向上增加的棧)建立一個新結構struct thread_info。

1571550696201

2.1.2 進程描述符存放

PID最大值默認爲32768(short int 短整形的最大值<linux/threads.h>)可經過修改/proc/sys/kernel/pid_max提升上限。

current宏查找當前正在運行進程的進程描述符。

x86系統中,current把棧指針後13個有效位屏蔽掉,用來計算出thread_info的偏移。

current_thread_info函數

movl $-8192,%eax
andl %esp,%eax

2.1.3 進程狀態

  • TASK_RUNNING:1. 正在執行 2. 在運行隊列中等待執行
  • TASK_INTERRUPTIBLE:阻塞(可中斷)
  • TASK_UNINTERRUPTIBLE:阻塞(不可中斷)
  • __TASK_TRACED:被其餘進程跟蹤的進程
  • __TASK_STOPPED:進程中止

<img src="https://i.loli.net/2021/01/26/xnVIHZK9qjo8TXd.png" alt="1571556447032" style="zoom: 67%;" />

陷入內核執行

  1. 系統調用
  2. 異常處理程序

2.1.4 進程家族樹

init進程

  • 全部進程都是PID爲1的init進程的後代
  • 內核在系統啓動的最後階段啓動init進程。

init進程目的:讀取系統的初始化腳本,並執行其餘的相關程序,最終完成系統啓動的整個過程。

task_struct中記錄父子進程

  1. parent指針(指向父進程)
  2. children子進程鏈表

3. 進程建立

其餘操做系統提供產生(spawn)進程機制,首先在新地址空間裏建立進程,讀入可執行文件,最後開始執行。

UNIX將上述機制流程分紅兩步fork()和exec()

  • fork()拷貝當前進程建立一個子進程
  • exec()負責讀取可執行文件,並將其入地址空間

3.1 寫時拷貝(copy-on-write)

使地址空間上的頁的拷貝推遲到實際發生寫入的時候才進行。

原理:若是有進程試圖修改一個頁,就會產生一個缺頁中斷。內核處理缺頁中斷的方式就是對該頁進行一次透明覆制。這時會清除頁面的COW屬性,表示着它再也不被共享。

3.2 fork()函數

fork()的實際開銷就是複製父進程的頁表以及給子進程建立惟一的進程描述符。

在如今linux內核中,fork()其實是由clone()系統調用實現的

<img src="https://i.loli.net/2021/01/26/m5xWw9acZnbro4H.png" alt="1571051424824" style="zoom: 80%;" />

3.2.1 copy_process()函數

  1. dup_task_struct()爲新進程建立一個內核棧,thread_info結構和task_struct與當前進程相同。父子進程描述符是徹底相同的。(分配空間)
  2. 檢查並確保新建立這個進程後,當前用戶所擁有的進程數目沒有超出給它分配的資源的限制。(檢查邊界)
  3. 子進程與父進程區別開。進程描述符的許多成員都要被清0或設初始值,那些不是繼承來的進程描述符的成員,主要是統計信息。task_struct中的大多數數據都依然未被修改。(子進程初始化)
  4. 子進程的狀態被設置爲TASK_UNINTERRUPTIBLE(不可中斷,阻塞狀態),以保證它不會投入運行。(設置子進程狀態)
  5. copy_process()調用copy_flags()以更新task_struct的flags成員。(設置標誌位)
    • 代表進程是否擁有超級用戶權限的PF_SUPERPRIV標誌被清0
    • 代表進程尚未調用exec()函數的PF_FORKNOEXEC標誌被設置
  6. 調用alloc_pid()爲新進程分配一個有效的PID。(爲子進程分配pid)
  7. 根據傳遞給clone()的參數,copy_process()拷貝或共享打開的文件、文件系統信息、信號處理函數、進程地址空間和命名空間等。通常狀況下,這些資源會被給定的進程的全部線程共享;不然,這些資源對每一個進程是不一樣的,所以被拷貝到這裏。(將資源參數標誌賦值給結構體)
  8. copy_process()作掃尾工做並返回一個指向子進程的指針,再回到do_fork()函數,若是copy_process()函數成功返回,新建立的子進程被喚醒並讓其投入運行。(返回子進程指針,並喚醒子進程執行)

注:內核有意讓子進程先執行,並不是總能如此,由於通常子進程都會立刻調用exec()函數,這樣能夠避免寫時拷貝的額外開銷。由於父進程先執行,可能往地址空間寫入。

3.3 vfork函數

vfork()和fork()區別:vfork()不拷貝父進程的頁表項。

vfork():子進程做爲父進程的一個單獨線程在它的地址空間裏運行,父進程被阻塞,直到子進程退出或執行exec(),子進程不能向地址空間寫入。

4. 線程建立

線程建立和進程建立基本一致,經過調用clone()函數傳遞的參數標誌,指明須要共享的資源。

建立線程

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

// CLONE_VM : 地址空間
// CLONE_FS : 文件系統
// CLONE_FILES : 文件描述符
// CLONE_SIGHAND : 信號處理程序及被阻斷的信號

建立進程(等同fork()函數)

clone(SIGCHLD,0);

建立進程(等同vfork()函數)

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)

4.1 內核線程

內核線程只在內核空間執行,從不切換到用戶空間。

內核線程和普通進程的區別:內核線程沒有獨立的地址空間。(task_struct的mm指針被設置爲NULL)

內核線程只能由其餘內核線程建立,經過kthreadd內核線程衍生出全部新的內核線程。(kthreadd是全部內核線程的祖宗)

4.1.1 kthreadd內核線程

kthreadd內核線程是在內核初始化時被建立,循環執行kthreadd函數,它的做用是管理調度其它的內核線程。

kthreadd函數的做用是運行kthread_create_list全局鏈表中維護的kthread。能夠調用kthread_create函數建立一個kthread,它會被加入到kthread_create_list鏈表中,同時kthread_create函數會喚醒kthreadd_task。kthreadd在執行kthread會調用老的接口,kthreadd內核線程在運行kthread時,會調用老接口kernel_thread,它會運行一個名爲「kthread」的內核線程,去運行建立kthread,被執行的kthread會從kthread_create_list鏈表中刪除,而且kthreadd會不斷地調用scheduler讓出CPU,這個線程不能關閉。

建立內核線程,不運行

kthread_create函數(源代碼 | linux/kthread.h | v5.4)是經過clone()系統調用,建立一個內核線程,但新建立的線程處於不可運行狀態。

kthread_create(threadfn, data, namefmt, arg...)

建立內核線程,並運行

kthread_run函數(源代碼 | linux/kthread.h | v5.4),經過調用kthread_create函數建立內核線程,而後調用wake_up_process()進行喚醒。

#define kthread_run(threadfn, data, namefmt, ...)              \
({                                     \
    struct task_struct *__k                        \
        = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    if (!IS_ERR(__k))                          \
        wake_up_process(__k);                      \
    __k;                                   \
})

內核線程中止

int kthread_stop(struct task_struct *k);

5. 進程終結

釋放所佔用的資源,並告知父進程。

通常來講,進程的析構是自身引發的,它發生在進程調用exit()系統調用的時候。

既能夠顯式地調用exit()這個系統調用,也能夠隱性地從某個程序的主函數返回。(C語言編輯器會在main()函數的返回點後面放置調用exit代碼)

終結的任務大部分都靠do_exit()(<kernel/exit.c>)

5.1 do_exit()函數

  1. 將task_struct中標誌成員設置成PF_EXITING
  2. 調用del_timer_sync()刪除任一內核定時器。確保沒有定時器在排隊,也沒有定時器處理程序在運行。
  3. 若是BSD的記帳功能是開啓的,do_exit()調用acct_update_integrals()來輸出記帳信息。
  4. 調用exit_mm()函數釋放進程佔用的mm_struct,若是沒有別的進程同時使用它們(也就是說,這個地址空間沒有被共享),就完全釋放它們。
  5. 調用sem__exit()函數,若是進程排隊等待IPC信號,它則離開隊列。
  6. 調用exit_files()和exit_fs()分別遞減文件描述符,文件系統數據引用計數,若是其中某個引用計數的數值降爲零,那就不用表明沒有進程在使用相應的資源,此時能夠釋放。
  7. 把存放在task_struct的exit_code()成員中的任務退出代碼置爲由exit()提供的退出代碼,或者去完成任何其餘有內核機制規定的退出動做。退出代碼存放在這裏供父進程隨時檢索。
  8. 調用exit_notify向父進程發送信號,給子進程從新找養父(其餘線程或init進程),並將存放在task_struct結構中的exit_state設置爲EXIT_ZOMBIE。
  9. do_exit調用schedule()切換到新的進程,由於處於EXIT_ZOMBIE狀態的進程不會被調度,因此這是進程所執行的最後一段代碼,do_exit()永不返回。

5.2 wait族函數

wait族函數都是經過惟一但很複雜的一個系統調用wait4()來實現的,掛起調用它的進程,直到其中的一個子進程退出,此時函數會返回子進程的PID。此外,調用此函數時提供的指針會包含子函數的退出代碼。

做者:世至其美
博客地址:hqber.com
轉載須註明以上信息, 更多文章,請訪問我的博客:hqber.com

相關文章
相關標籤/搜索