引言:
在Linux的內核的五大組成模塊中,進程管理模塊時非常重要的一部分,它雖然不像內存管理、虛擬文件系統等模塊那樣復雜,也不像進程間通信模塊那樣條理化,但作為五大內核模塊之一,進程管理對我們理解內核的運作、對於我們以後的編程非常重要。同時,作為五大組成模塊中的核心模塊,它與其他四個模塊都有聯系。下面就對進程模塊進行想寫的介紹,首先要了解進程及其相關的概念。其次介紹進程的創建、切換、撤銷等基本操作。除此之外,還給出了Linux內核是如何對進程進行調度管理的。
Linux內核——進程管理與調度 http://www.linuxidc.com/Linux/2014-08/105366.htm
Linux 進程管理的常用命令示例 http://www.linuxidc.com/Linux/2012-12/77169.htm
Linux操作系統第10講_進程管理 PPT http://www.linuxidc.com/Linux/2012-09/70713.htm
Linux 進程管理 [screen/ps/kill] http://www.linuxidc.com/Linux/2011-09/43179.htm
一、進程及其相關概念
進程:進程可以理解為程序執行的一個實例,它包括可執行程序以及與其相關的系統資源,比如打開的文件、掛起的信號、內核內部數據、處理器狀態、內存地址空間及包含全局變量的數據段等。從內核的角度看,進程也可以稱為任務。
進程描述符:與進程相關的事情非常多,比如進程的狀態、進程的優先級、進程的地址空間、允許該進程訪問的文件等等,Linux內核為此專門設計了一個類型為task_struct的結構體,稱之為進程描述符。進程描述符中包含了內核管理進程的所有信息,可以說,只要得到一個進程的進程描述符,就可以知道一個進程的所有信息。
進程狀態:進程描述符task_struct結構體中有一個state字段,表示進程當前的所處狀態。從進程的創建到進程的刪除,它可以經過5種不同的狀態,分別是可運行狀態、可中斷的等待狀態、不可中斷的等待狀態、暫停狀態、跟蹤狀態。除此之外,當進程被終止時,還可能會變為僵死狀態、僵死撤消狀態。內核可以使用宏set_current_state(state)設置當前進程的狀態,用set_task_state(task,state)設置某進程的狀態。
進程標示符:進程描述task_struct結構體中的pid字段可以標識唯一標識一個進程,稱之為進程標識符PID。當創建一個新進程時,PID是按照順序從小到大分配給新進程的。內核通過管理一個pidmap_array位圖來表示當前已分配的PID和閒置的PID號。注意:在多線程組中,所有的線程共享相同的PID。除了進程標識符外,內核對進程的大部分訪問時通過進程描述符指針進行的。
進程關系:進程之間的關系有親屬關系和非親屬關系。親屬關系包括父子關系和兄弟關系等。其中由tast_struct結構體中的parent/children/real_parent/sibling等字段描述。除了親屬關系外,還有其他關系,比如,一個進程是一個進程組或登錄會話的領頭進程,可能是一個線程組的領頭進程,這些關系由group_leader/tgid/signal->pgrp等字段描述。
進程資源:為了防止進程過度的使用系統資源,內核為每個進程使用資源的數量進行了一些限制。其中包括進程地址空間的最大數、進程使用CPU的最大時間、堆的最大值、文件大小的最大值、文件鎖數量的最大值、消息隊列的最大字節數、打開文件描述符的最大數、進程擁有的頁框最大數等。
二、進程的創建、切換、撤銷
進程的創建:在Linux環境編程時,一般采用fork()函數來創建新的進程,當然,那是在用戶空間的函數,它會調用內核中的clone()系統調用,由clone()函數繼續調用do_fork()完成進程的創建。
傳統Unix系統中,創建的子進程復制父進程所擁有的資源,這種方法效率低,因為子進程需要拷貝父進程的整個地址空間。但是,子進程幾乎不必讀或修改父進程擁有的所有資源,因為很多情況下,子進程創建後會立即調用exec()一族的函數,並清除父進程仔細拷貝過來的地址空間。現代Unix系統用三種方式解決了這個問題:1、寫實復制技術允許父子進程讀相同的物理頁。2、輕量級進程允許父子進程共享每進程在內核的很多數據結構。3、vfork()系統調用創建的進程能共享父進程的內存地址空間,為了防止父進程重寫子進程需要的數據,阻塞父進程的執行,一直到子進程退出或執行一個新的程序為止。整個進程創建過程可能涉及到如下函數:
fork()/vfork()/_clone----------->clone()--------->do_fork()---------->copy_process()
上面的創建過程結束之後,就有了處於可運行狀態的完整的子進程,新的子進程有了PID、進程描述符等各種數據結構,要想實際運行它,還需要調度程序把CPU交給新創建的子進程。
除了進程外,還有內核線程(用kernet_thread創建)的概念。在Linux中,內核線程與普通進程存在以下兩個方面的不同:
1、內核線程只運行在內核態,而普通進程既可以運行在內核態,也可運行在用戶態。
2、因為內核線程只運行在內核態,它只使用大於PAGE_OFFSET的線性地址空間。另一方面,不管在用戶態還是在內核態,普通進程可以用4GB的線性地址空間。
撤銷進程:進程終止後,需要通知內核以便內核釋放進程所擁有的資源,包括內存、打開文件以及其他資源,如信號量。進程終止的一般方式是調用exit()庫函數,該函數釋放C函數庫所分配的資源,執行編程者所注冊的每個函數,並結束從系統回收進程的那個系統調用。
除了進程自己終止自己外,內核可以有選擇地強迫整個線程組死掉。這發生在:當進程接收到一個不能處理或忽視的信號時,或者當內核正在代表進程運行時再內核態產生一個不可恢復的CPU異常時。
有兩個終止用戶態應用的系統調用:exit_group()系統調用,它終止整個線程組,即整個基於多線程的應用。do_group_exit()是實現這個系統調用的主要內核函數。exit()系統調用,它終止一個線程,而不管該線程所屬線程組中的所有其他進程。do_exit()是實現這個系統調用的主要內核函數。
進程切換:進程切換又稱為任務切換、上下文切換。它是這樣一種行為,為了控制進程的執行,內核掛起當前在CPU上運行的進程,並恢復以前掛起的某個進程的執行。
跟函數的調用類似,進程切換時,一般要在CPU上裝載要執行進程的進程上下文。進程的硬件上下文指:可執行程序上下文的一個子集,是進程恢復執行前裝入寄存器的一組數據。其中一部分放在TSS段,即任務狀態段,剩余部分存放在內核態堆棧中。進程的切換只發生在內核態,在執行進程切換之前,用戶態進程使用的所有寄存器內容都已保存在內核態堆棧上。
進程的切換有兩種方法,一種是硬件切換,一種是軟件切換。軟件切換就是利用程序逐步執行切換,它的優點是,可以對切換時裝入的數據進行合法性檢查,執行時間雖與硬件切換大致相同,但仍有可改進的地方。
進程切換使用schedule()函數完成,在本質上,每個進程切換由兩部分組成:1、切換頁全局目錄以安裝一個新的地址空間。2、切換內核態堆棧和硬件上下文,因為硬件上下文提供了內核執行新進程所需要的所有信息,包括CPU寄存器,主要有switch_to函數完成。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-08/105687p2.htm