1. 程式人生 > >linux常見程序與核心執行緒

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);