Linux内核进程管理基本概念-进程、运行队列、等待队列、进程切换、进程调度
下面簡述一些基本概念,以及對內核代碼做最初步的了解;
?
一 Linux內核進程管理基礎
?
? ? Linux 內核使用 task_struct 數據結構來關聯所有與進程有關的數據和結構,Linux 內核所有涉及到進程和程序的所有算法都是圍繞該數據結構建立的,是內核中最重要的數據結構之一。
? ? 該數據結構在內核文件 include/linux/sched.h 中定義,是個很大的結構體;在 3.8 的內核中,該數據結構有 380 行。
? ? Linux中進程ID有如下類型:PID,TGID,PGID,SID;
? ? 如果考慮進程之間有復雜的關系,如線程組、進程組、會話組,這些組均有組ID;分別為 TGID、PGID、SID;
? ? 內核中使用下面兩個函數來實現分配和回收PID:
? ? static int alloc_pidmap(struct pid_namespace *pid_ns);
? ? static void free_pidmap(struct upid *upid);
? ? Linux中增加命名空間這個概念是為了虛擬化和方便管理。
? ? 進程命名空間;作用:linux通過命名空間管理進程號,在不同的namespace中可以有pid相同的進程;進程命名空間是一個父子結構,子空間對于父空間可見。
?
二 task_struct結構
? ??Linux用task_struct結構表示進程,2.6內核的task_struct結構相對于2.4內核有很大變化。
? ? 該結構記錄了進程的重要信息。
? ? 與進程調度有關的信息包括:
? ? 1 state
? ? 進程狀態;一個進程共有7種可能狀態,分別是:TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、TASK_STOPPED、TASK_TRACED、EXIT_ZOMBIE和EXIT_DEAD;
? ? 2 timestamp
? ? 進程發生調度事件的時間(單位是?nanosecond)。
? ? 3 prio,?static_prio
? ? 進程的優先級和靜態優先級。Prio表示進程的動態優先級,與2.4內核中goodness()的計算結果相當。在0~MAX_PRIO-1之間取值(MAX_PRIO?定義為?140),其中0~MAX_RT_PRIO-1(MAX_RT_PRIO定義為100)屬于實時進程范圍,MAX_RT_PRIO~MX_PRIO-1屬于非實時進程。數值越大,表示進程優先級越小。static_prio表示進程的靜態優先級,相當于2.4內核中的nice。一個進程的初始時間片的大小完全取決于它的靜態優先級。
? ? 4 sleep_avg
? ? 進程的平均等待時間,在0到NS_MAX_SLEEP_AVG之間取值,初值為0,相當于進程等待時間與運行時間的差值。它是動態優先級計算的關鍵因子,sleep_avg越大,計算出來的進程優先級也越高。
? ? 5 interactive_credit
? ? 該變量表示進程的交互程度。在-CREDIT_LIMIT到CREDIT_LIMIT+1之間取值,初始值為0,而后根據不同的條件加1減1,一旦該值超過CREDIT_LIMIT,即表示該進程是交互進程。
? ? 6 array
? ? 指向當前CPU的active就緒進程隊列。
? ? 7 run_list
? ? ?進程通過這個list_head變量連接自己到prio_array數組中queue隊列,這樣相同優先級的進程連接成一個雙向列表,表頭為prio_array結構中的queue變量。
?
三 進程運行隊列(就緒隊列)
?
? ? Linux內核定義了一個list_head數據結構,字段head和prev分別表示通用的雙向鏈表向前和向后的指針元素。每個task_struct包含一個list_head類型的run_list字段。
? ? 在Linux2.4內核中,就緒進程隊列是一個全局數據結構,所有的處理器共享同一個隊列。調度器對它的所有操作都會因全局自旋鎖而導致系統各個處理機之間的等待,使得就緒隊列成為一個明顯的瓶頸。2.6內核重新設計就緒進程隊列為每CPU的數據結構,每個處理器都維護一個自己的就緒隊列,這樣就避免了2.4內核中的SMP性能瓶頸。
?
? ? 每個CPU的就緒進程隊列由一個struct runqueue結構描述,其中最關鍵的子結構是優先級就緒數組。
? ? 描述優先級就緒數組的數據結構是prio_array_t。
?
四 進程等待隊列
?
? ? Linux等待隊列在內核中有很多用途,尤其在中斷處理、進程同步以及定時。例如等待一個磁盤操作的終止,等待釋放資源,或等待時間經過的固定的時間間隔。
? ??
等待隊列由雙向鏈表實現。
?
struct?__wait_queue_head?{??
????????spinlock_t?lock;??
????????struct?list_head?task_list;??
};??//等待隊列的頭結點
typedef?struct__wait_queue_head?wait_queue_head_t;
?
struct?__wait_queue?{
? ? unsigned?int?flags;?//標識是互斥進程還是非互斥進程?
?????? ?#define WQ_FLAG_EXCLUSIVE??????0x01??/*?表示等待進程想要被獨占地喚醒??*/??
????????void?*private; ?/*?指向等待進程的task_struct實例?*/??????????
? ? ? ? wait_queue_func_t?func;??????/*?用于喚醒等待進程?? ? ?*/??????????
? ? ? ?sruct?list_head?task_list;??/*?用于鏈表元素,將wait_queue_t鏈接到wait_queue_head_t?*/?
};
typedef?struct__wait_queue?wait_queue_t;??
?
五 進程切換
?
? ? 內核必須有能力掛起正在CPU上運行的進程,并恢復以前掛起的某個進程執行。這種行為稱為進程切換、任務切換或上下文切換。
? ? 盡管每個進程都擁有屬于自己的地址空間,但所有進程必須共享CPU寄存器。在恢復一個進程的執行之前,內核必須確保每個寄存器裝了掛起進程時的值。
? ? 進程恢復前必須裝入寄存器的一組數據稱為硬件上下文,它的一部分存在TSS段,而剩余部分存放在內核態堆棧中。
? ??
? ? 每個進程描述符包含一個類型thread_struct的thread字段,來保存進程切換時的硬件上下文。
? ? 進程切換主要有兩大步:1、切換全局頁表項;2、切換內核堆棧和硬件上下文。這個切換工作由context_switch()完成。其中switch_mm完成第一步,而switch_to和__switch_to()主要完成第二步。更詳細的,__switch_to()主要完成硬件上下文切換,switch_to主要完成內核堆棧切換。
? ? ? ? schedule() --> context_switch() --> switch_to --> __switch_to()
?
六 進程調度
?
進程調度由schedule()函數實現。
Linux的進程調度是基于分時技術(time-sharing)。允許多個進程“并發”運行就意味著CPU 的時間被粗略地分成“片”,給每個可運行進程分配一片。
調度策略也是基于依照優先級排隊的進程。
在Linux 中,進程的優先級是動態的。調度程序跟蹤進程做了些什么,并周期性地調整它們的優先級。在這種方式下,在較長的時間間隔內沒有使用CPU的進程,通過動態地增加它們的優先級來提升它們。相應地,對于已經在CPU上運行了較長時間的進程,通過減少它們的優先級來處罰它們。每個進程在創建之初有一個基本的優先級,執行期間調度系統會動態調整它的優先級,交互性高的任務會獲得一個高的動態優先級,而交互性低的任務獲得一個低的動態優先級。
Linux操作系統支持多進程,進程控制塊PCB(Process ControlBlock)是系統中最為重要的數據結構之一。用來存放進程所必需的各種信息,PCB用結構task_struct來表示,包括進程的類型、進程狀態、優先級、時鐘信息等。
Linux系統中,進程調度操作由schedule()函數執行,這是一個只在內核態運行的函數,函數代碼為所有進程共享。
?
更多詳情自行參閱如下;
https://www.cnblogs.com/hazir/p/linux_kernel_pid.html
https://blog.csdn.net/sdulibh/article/details/50827911
https://www.jianshu.com/p/c1c5e347d64c
https://blog.csdn.net/lsjseu/article/details/9249665
?
總結
以上是生活随笔為你收集整理的Linux内核进程管理基本概念-进程、运行队列、等待队列、进程切换、进程调度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CPU实模式和保护模式、全局描述符表GD
- 下一篇: linux内核锁机制学习