進程
1.進程是什么
1.1概念
計算機上所有可運行的軟件,通常也包括操作系統,被組織成若干順序進程(sequential process),簡稱進程(process)。
一個進程就是一個正在執行程序的實例,包括程序設計器、寄存器和變量的當前值。一個進程是某種類型的一種活動,它有程序、輸入、輸出以及狀態。單個處理器可以被若干進程共享,它使用某種調度算法決定何時停止一個進程的工作,並轉而為了另一個進程服務功能。
2.進程如何組織
2.1進程控制塊(PCB)
Linux系統中主要的活動實體就是進程。
每個進程執行一段獨立的程序並且在進程初始化的時候擁有一個獨立的控制線程。換句話說,每一個進程都擁有一個獨立的程序計數器,用這個這個程序計數器可以追蹤下一條將要被執行的指令。
所有的進程都被放在一個叫做進程控制塊(PCB),的數據結構中,可以理解為進程屬性的集合,該控制塊由操作系統創建和管理。每個進程在內核中都有一個進程控制塊來維護進程相關的信息,Linux內核的進程控制塊是(task_struct)結構體。
2.2進程標識符(PID)
進程標識符在task_struct下定義
pid_t pid; //內核中用以標識進程的id pid_t tgid; //用來實現線程機制
struct pid { atomic_t count; unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; struct upid numbers[1]; };
每個進程都有一個唯一的標識符(PID),內核通過這個標識符來識別不同的進程,同時,進程標識符(PID)也是內核提供給用戶程序的接口,用戶程序通過PID對進程發號施令。
PID是32位的無符號整數,它被順序編號:新創建進程的PID通常是前一個進程的PID加1。然而,為了與16位硬件平台的傳統Linux系統保持兼容,在Linux上允許的最大PID號是32767,當內核在系統中創建第32768個進程時,就必須重新開始使用已閑置的PID號。在64位系統中,PID可擴展到4194303。
3.進程狀態
3.1六種狀態
#define TASK_RUNNING
1.表示進程要么正在執行,要么正在准備執行。
#define TASK_INTERRUPTIBLE
2.表示進程被阻塞(睡眠),只有當某個條件是TRUE時,其狀態相應的設置為 TASK_RUNNING。它可以被信號和wake_up喚醒。
#define TASK_UNINTERRUPTIBLE
3.表示進程被阻塞(睡眠),只有當某個條件是TRUE時,其狀態相應的設置為 TASK_RUNNING。它只能被wake_up喚醒。
#define TASK_STOPPED
4.表示進程被停止執行。
#define TASK_TRACED
5.表示進程被 debugger 等進程監視着。
#define EXIT_ZOMBIE
6.表示進程的執行被終止,但是其父進程還沒有使用 wait() 等系統調用來獲知它的終止信息。
#define EXIT_DEAD
7.表示進程的最終狀態。
3.2狀態轉換圖
4.Linux下的O(1)調度算法
Linux O(1)調度器(O(1) scheduler) 是歷史上一個流行的Linux系統調度程序。命名為這個名字是因為它能夠在常數時間內執行任務調度,例如從執行隊列中選擇一個任務或將一個任務加入執行隊列,這與系統中的任務總數有關。
4.1 O(1)調度器
在O(1)調度中,要問最重要的數據結構是運行隊列。運行隊列描繪了進程隊列的結構,在內核源碼中用runqueue結構體表示。
struct runqueue { unsigned long nr_running; task_t *curr; prio_array_t *active,*expired,arrays[2]; };
4.2優先級數組
O(1)算法的另一個核心數據結構即為prio_array結構體。該結構體中有一個用來表示進程動態優先級的數組queue,它包含了每一種優先級進程所形成的鏈表。
#define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO #define MAX_PRIO (MAX_RT_PRIO + 40) typedef struct prio_array prio_array_t; struct prio_array { unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; };
4.3靜態優先級和動態優先級
進程有兩個優先級,一個是靜態優先級,一個是動態優先級.靜態優先級是用來計算進程運行的時間片長度的,動態優先級是在調度器進行調度時用到的,調度器每次都選取動態優先級最高的進程運行.
靜態優先級的計算: nice值和靜態優先級之間的關系是:靜態優先級=100+nice+20 而nice值的范圍是-20~19,所以普通進程的靜態優先級的范圍是100~139
動態優先級的計算: 動態優先級=max(100 , min(靜態優先級 – bonus + 5 , 139))
4.4時間片
O(1)算法采用過期進程數組和活躍進程數組解決以往調度算法所帶來的O(n)復雜度問題。過期數組中的進程都已經用完了時間片,而活躍數組的進程還擁有時間片。當一個進程用完自己的時間片后,它就被移動到過期進程數組中,同時這個過期進程在被移動之前就已經計算好了新的時間片。可以看到O(1)調度算法是采用分散計算時間片的方法,並不像以往算法中集中為所有可運行進程重新計算時間片。當活躍進程數組中沒有任何進程時,說明此時所有可運行的進程都用完了自己的時間片。那么此時只需要交換一下兩個數組即可將過期進程切換為活躍進程,進而繼續被調度程序所調度。兩個數組之間的切換其實就是指針之間的交換,因此花費的時間是恆定的。
struct prop_array *array = rq->active; if (array->nr_active != 0) { rq->active = rq->expired; rq->expired = array; }
上面的代碼說明了兩個數組之間的交換,通過分散計算時間片、交換過期和活躍兩個進程集合的方法可以使得O(1)算法在恆定的時間內為每個進程重新計算好時間片。
進程運行的時間片長度的計算 靜態優先級<120,基本時間片=max((140-靜態優先級)*20, MIN_TIMESLICE) 靜態優先級>=120,基本時間片=max((140-靜態優先級)*5, MIN_TIMESLICE)
4.5調度算法
在每次進程切換時,內核依次掃描就緒隊列上的每一個進程,計算每個進程的優先級,再選擇出優先級最高的進程來運行;盡管這個算法理解簡單,但是它花費在選擇優先級最高進程上的時間卻不容忽視。系統中可運行的進程越多,花費的時間就越大,時間復雜度為O(n)。
//偽代碼 for (系統中的每個進程) { 重新計算時間片; 重新計算優先級; }
5.對操作系統進程模型的看法
進程是操作系統最核心的概念,這是對正在運行的程序的一個抽象。所有操作系統其他的所有內容都是圍繞着進程的概念展開的。
從概念上說,每個進程都擁有它自己的虛擬CPU。當然,實際上的真正的CPU在各自進程之間來回切換。
進程管理即是操作系統對CPU的管理,為了提升CPU利用率,使用多道編程,便有了多進程維護管理。操作系統已實現了各管理功能,硬件CPU及一系列進程資源抽象成為了進程的概念,可以說進程算是操作系統的“無中生有”,應用程序的編程人員直接利用進程的機制,達到讓應用程序高效利用硬件資源的目的。
6.參考
https://blog.csdn.net/a2796749/article/details/47101533
Modern Operating Systems (Fourth Edition) Andrew S. Tanenbaum and Herbert Bos 著