linux常見進程與內核線程


發現大量jdb2進程占用io資源.jdb2進程是一個文件系統的寫journal的進程

kthreadd:這種內核線程只有一個,它的作用是管理調度其它的內核線程。它在內核初始化的時候被創建,會循環運行一個叫做kthreadd的函數,該函數的作用是運行kthread_create_list全局鏈表中維護的kthread。可以調用kthread_create創建一個kthread,它會被加入到kthread_create_list鏈表中,同時kthread_create會weak up kthreadd_task。kthreadd在執行kthread會調用老的接口——kernel_thread運行一個名叫“kthread”的內核線程去運行創建的kthread,被執行過的kthread會從kthread_create_list鏈表中刪除,並且kthreadd會不斷調用scheduler 讓出CPU。這個線程不能關閉。

migration:每個處理器核對應一個migration內核線程,主要作用是作為相應CPU核的遷移進程,用來執行進程遷移操作,內核中的函數是migration_thread()。屬於2.6內核的負載平衡系統,該進程在系統啟動時自動加載(每個 cpu 一個),並將自己設為 SCHED_FIFO 的實時進程,然后檢查 runqueue::migration_queue 中是否有請求等待處理,如果沒有,就在 TASK_INTERRUPTIBLE 中休眠,直至被喚醒后再次檢查。migration_queue僅在set_cpu_allowed() 中添加,當進程(比如通過 APM 關閉某 CPU 時)調用set_cpu_allowed()改變當前可用 cpu,從而使某進程不適於繼續在當前 cpu 上運行時,就會構造一個遷移請求數據結構 migration_req_t,將其植入進程所在 cpu 就緒隊列的migration_queue 中,然后喚醒該就緒隊列的遷移 daemon(記錄在runqueue::migration_thread 屬性中),將該進程遷移到合適的cpu上去在目前的實現中,目的 cpu 的選擇和負載無關,而是"any_online_cpu(req->task->cpus_allowed)",也就是按 CPU 編號順序的第一個 allowed 的CPU。所以,和 load_balance() 與調度器、負載平衡策略密切相關不同,migration_thread() 應該說僅僅是一個 CPU 綁定以及 CPU 電源管理等功能的一個接口。這個線程是調度系統的重要組成部分,也不能被關閉。

watchdog:每個處理器核對應一個watchdog 內核線程,watchdog用於監視系統的運行,在系統出現故障時自動重新啟動系統,包括一個內核 watchdog module 和一個用戶空間的 watchdog 程序。在Linux 內核下, watchdog的基本工作原理是:當watchdog啟動后(即/dev/watchdog設備被打開后),如果在某一設定的時間間隔(1分鍾)內/dev/watchdog沒有被執行寫操作, 硬件watchdog電路或軟件定時器就會重新啟動系統,每次寫操作會導致重新設定定時器。/dev/watchdog是一個主設備號為10, 從設備號130的字符設備節點。 Linux內核不僅為各種不同類型的watchdog硬件電路提供了驅動,還提供了一個基於定時器的純軟件watchdog驅動。如果不需要這種故障處理機制,或者有相應的替代方案,可以在menuconfig的
   Device Drivers —>
      Watchdog Timer Support
處取消watchdog功能。

events:每個處理器核對應一個 events內核線程。用來處理內核事件很多軟硬件事件(比如斷電,文件變更)被轉換為events,並分發給對相應事件感興趣的線程進行響應。用來處理內核事件的重要線程,不能被去掉

kblockd:每個處理器核對應一個 kblockd 內核線程。用於管理系統的塊設備,它會周期地激活系統內的塊設備驅動。如果擁有塊設備,那么這些線程就不能被去掉,要是想去掉,需要在.config中直接將CONFIG_BLOCK設成n,同時在menuconfig中取消
    Device Drivers   —>
           Block devices

khelper:這種內核線程只有一個,主要作用是指定用戶空間的程序路徑和環境變量, 最終運行指定的user space的程序,屬於關鍵線程,不能關閉

pdflush:這種內核線程共有兩個,線程名都是pdflush,主要作用是回寫內存中的臟頁,回收臟頁占據的空間。由於頁高速緩存的緩存作用,寫操作實際上會被延遲。當頁高速緩存中的數據比后台存儲的數據更新時,那么該數據就被稱做臟數據。在內存中累積起來的臟頁最終必須被寫回。在以下兩種情況發生時,臟頁被寫回:
1.當空閑內存低於一個特定的閾值時,內核必須將臟頁寫回磁盤,以便釋放內存。
2.當臟頁在內存中駐留時間超過一個特定的閾值時,內核必須將超時的臟頁寫回磁盤,以確保臟頁不會無限期地駐留在內存中。
對於第一個目標,pdflush線程在系統中的空閑內存低於一個特定的閾值時,將臟頁刷新回磁盤。該后台回寫例程的目的在於在可用物理 內存過低時,釋放臟頁以重新獲得內存。特定的內存閾值可以通過dirty_background_ratiosysctl系統調用設置。當空閑內存比閾值:dirty_ background_ratio還低時,內核便會調用函數wakeup_bdflush()喚醒一個pdflush線程,隨后pdflush線程進一步 調用函數background_writeout()開始將臟頁寫回磁盤。函數background_ writeout()需要一個長整型參數,該參數指定試圖寫回的頁面數目。函數background_writeout()會連續地寫出數據,直到滿足以下兩個條件:
1. 已經有指定的最小數目的頁被寫出到磁盤。
2. 空閑內存數已經回升,超過了閾值dirty_background_ratio。
上述條件確保了pdflush操作可以減輕系統中內存不足的壓力。回寫操作不會在達到這兩個條件前停止,除非pdflush寫回了所有的臟頁,沒有剩下的臟頁可再被寫回了。
對於第二個目標,pdflush后台例程會被周期性喚醒(和空閑內存是否過低無關),將那些在內存中駐留時間過長的臟頁寫出,確保內存中不會有長期存在的臟頁。如果系統發生崩潰,由於內存處於混亂之中,所以那些在內存中還沒來得及寫回磁盤 的臟頁就會丟失,所以周期性同步頁高速緩存和磁盤非常重要。在系統啟動時,內核初始化一個定時器,讓它周期地喚醒pdflush線程,隨后使其運行函數 wb_kupdate()。該函數將把所有駐留時間超過百分之dirty_expire_centisecs秒的臟頁寫回。然后定時器將再次被初始化為百 分之dirty_expire_ centisecs秒后喚醒pdflush線程。總而言之,pdflush線程周期地被喚醒並且把超過特定期限的臟頁寫回磁盤。
系統管理員可以在/proc/sys/vm中設置回寫相關的參數,也可以通過sysctl系統調用設置它們,cat /proc/sys/vm/dirty_background_ratio
        屬於核心的內存管理線程,這個線程也不能被關閉

kswapd0:這種內核線程只有一個,主要作用是用來回收內存。在kswapd中,有2個閥值,pages_hige和pages_low。當空閑內存頁的數量低於pages_low的時候,kswapd進程就會掃描內存並且每次釋放出32個 free pages,直到freepage的數量到達pages_high。具體回收內存有如下原則:
      1. 如果頁未經更改就將該頁放入空閑隊列;
      2. 如果頁已經更改並且是可備份回文件系統的,就理解將內存頁的內容寫回磁盤;
      3. 如果頁已經更改但是沒有任何磁盤上的備份,就將其寫入swap分區。
        同樣,屬於核心的內存管理線程,這個線程也不能被關閉

nfsiod:這種內核線程只有一個,主要作用是為nfs提供高效的緩沖機制,從而改善nfs文件系統的性能,如果不需nfs,可以取消這一線程,取消這一線程的方法為menuconfig中取消
     File systems  —>
          Network File Systems

rpciod:每個處理器核對應一個rpciod內核線程,主要作用是作為遠過程調用服務的守護進程,用於從客戶端啟動I/O服務,通常啟動NFS服務時要用到它,想要關閉它,需要在.config中把CONFIG_SUNRPC, CONFIG_SUNRPC_GSS, CONFIG_SUNRPC_XPRT_RDMA的值設成n

kpsmoused:這種內核線程只有一個,主要作用是支持ps/2接口的鼠標驅動。如要沒有鼠標,可以取消,取消方法是menuconfig中取消
DeviceDrivers   —>
      Input device support
       Mice

什么是進程遷移?
進程遷移就是將一個進程從當前位置移動到指定的處理器上。它的基本思想是在進程執行過程中移動它,使得它在另一個計算機上繼續存取它的所有資源並繼續運行,而且不必知道運行進程或任何與其它相互作用的進程的知識就可以啟動進程遷移操作,這意味着遷移是透明的。
進程遷移的好處
進程遷移是支持負載平衡和高容錯性的一種非常有效的手段。對一系列的負載平衡策略的研究表明進程遷移是實現負載平衡的基礎,進程遷移在很多方面具有適用性:

動態負載平衡:將進程遷移到負載輕或空閑的節點上,充分利用可用資源,通過減少節點間負載的差異來全面提高性能。
容錯性和高可用性:某節點出現故障時,通過將進程遷移到其它節點繼續恢復運行,這將極大的提高系統的可靠性和可用性。在某些關鍵性應用中,這一點尤為重要。
並行文件IO:將進程遷移到文件服務器上進行IO,而不是通過傳統的從文件服務器通過網絡將數據傳輸給進程。對於那些需向文件服務器請求大量數據的進程,這將有效的減少了通訊量,極大的提高效率。
 
充分利用特殊資源:進程可以通過遷移來利用某節點上獨特的硬件或軟件能力。
 
內存導引(Memory Ushering)機制:當一個節點耗盡它的主存時,Memory Ushering機制將允許進程遷移到其它擁有空閑內存的節點,而不是讓該節點頻繁地進行分頁或和外存進行交換。這種方式適合於負載較為均衡,但內存使用存在差異或內存物理配置存在差異的系統。
進程遷移的實現角度
進程遷移的實現復雜性及對OS的依賴性阻礙了進程遷移的廣泛使用 ,尤其是對透明的進程遷移實現。根據應用的級別,進程遷移可以作為OS的一部分、用戶空間、系統環境的一部分或者成為應用程序的一部分。

用戶級遷移:用戶級實現較為簡單,軟件開發和維護也較為容易,因此,現有的很多系統都是采用用戶級實現,如Condor和Utopia。但由於在用戶級無法獲得Kernel的所有狀態,因此,對於某類進程,無法進行遷移。另外,由於Kernel空間和User空間之間存在着壁壘,打破這個邊界獲得 Kernel提供的服務需要巨大的開銷。因此,用戶級實現效率遠遠低於內核級實現。
應用級遷移:應用級遷移實現較為簡單,可移植性好,但是需要了解應用程序語義並可能需對應用程序進行修改或重編譯,透明性較差,這方面的系統有Freedman、Skordos等。
內核級遷移:基於內核的實現可以充分利用OS提供的功能,全面的獲取進程和OS狀態,因此實現效率較高,能夠為用戶提供很好的透明性。但是由於需要對OS進行修改,實現較為復雜。這方面的典型系統有MOSIX和 Sprite系統。

進程狀態
進程遷移的主要工作就在於提取進程狀態,然后在目的節點根據進程狀態再生該進程。在現實中,一個進程擁有很多狀態,並且隨着操作系統的演化,進程狀態也越來越多樣。一般來說,一個進程的狀態可以分為以下幾類:

進程執行狀態(Execution State):表示當前運行進程的處理器狀態,和機器高度相關。包括內核在上下文切換時保存和恢復的信息,如通用和浮點寄存器值、棧指針、條件碼等。
進程控制(Process Control):操作系統系統用來控制進程的所有信,一般包括進程優先級、進程標識,父進程標識等。一旦系統編排了進程控制信息,進程遷移系統必須凍結該進程的運行。
進程Memory狀態和進程地址空間:包括進程的所有虛存信息,進程數據和進程的堆棧信息等,是進程狀態的最主要的一部分。
進程的消息(Message)狀態:包括進程緩沖的消息和連接(Link)的控制信息。進程遷移中通訊連接的保持以及遷移后連接的恢復是進程遷移中一項較有挑戰意義的問題。
文件狀態:進程的文件狀態包括文件描述符和文件緩沖快。保持文件的Cache一致性和進程間文件同步訪問也是進程遷移機制需要着重考慮的。
由於在同構的環境下(相同或兼容的機器體系結構和指令集以及操作系統)提取和恢復進程狀態相對容易,現有的工作大多是以同構環境為前提的。不過,越來越多的人開始研究異構環境下的進程遷移機制,如TUI 系統。

pid=0
1. idle是什么
簡單的說idle是一個進程,其pid號為0。 其前身是系統創建的第一個進程,也是唯一一個沒有通過fork()產生的進程。在smp系統中,每個處理器 單元有獨立的一個運行隊列,而每個運行隊列上又有一個idle進程,即有多少處理器單元,就有多少idle進程。系統的空閑時間,其實就是指idle進程 的"運行時間"。既然是idle是進程,那我們來看看idle是如何被創建,又具體做了哪些事情?
2. idle的創建
我們知道系 統是從BIOS加電自檢,載入MBR中的引導程序(LILO/GRUB),再加載linux內核開始運行的,一直到指定shell開始運行告一段落,這時 用戶開始操作Linux。而大致是在vmlinux的入口startup_32(head.S)中為pid號為0的原始進程設置了執行環境,然后原是進程 開始執行start_kernel()完成Linux內核的初始化工作。包括初始化頁表,初始化中斷向量表,初始化系統時間等。繼而調用 fork(),創建第一個用戶進程:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
這 個進程就是著名的pid為1的init進程,它會繼續完成剩下的初始化工作,然后execve(/sbin/init), 成為系統中的其他所有進程的祖先。關於init我們這次先不研究,回過頭來看pid=0的進程,在創建了init進程后,pid=0的進程調用 cpu_idle()演變成了idle進程。
current_thread_info()->status |= TS_POLLING;

pid=1

pid=2
Linux內核線程之父pid=2的kthreadd線程
因為所涉及的話題在代碼的實現上是體系架構相關的,所以本貼基於ARM架構。
這里所謂的內核線程,實際上是由kernel_thread函數創建的一個進程,有自己獨立的task_struct結構並可被調度器調度,這種進程的特殊之處在於它只在內核態運行。
在Linux source code中, init/main.c中的rest_init()中就開始調用kernel_thread來構造內核線程了,比如:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);


免責聲明!

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



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