目錄
1.進程優先級
1.1 基本概念
1.2 查看系統進程
1.3 修改進程優先級的命令
2.進程間切換
2.1 相關概念
2.2 Linux2.6內核進程調度隊列(了解即可)
3.命令行參數
1.進程優先級
1.1 基本概念
- cpu資源分配的先后順序,就是指進程的優先權(priority)。
- 優先權高的進程有優先執行權利。配置進程優先權對多任務環境的linux很有用,可以改善系統性能。
- 還可以把進程運行到指定的CPU上,這樣一來,把不重要的進程安排到某個CPU,可以大大改善系統整體性能。?
我們知道進程在內存中是需要排隊的,比如運行隊列,等待隊列。那排隊是干什么的:就是在確認優先級,來確定得到某種資源的先后順序。為什么要確認優先級,本質就是資源不足。那么操作系統是怎么做到呢?我們下面講解:
優先級其實就是PCB中的一個 int 字段,數值越小,優先級越大,跟我們的考試排名一樣。Linux進程的優先級數值范圍:60~99。Linux中默認進程的優先級都是80。
1.2 查看系統進程
我們先編寫一段代碼
在linux或者unix系統中,用ps -la 命令則會類似輸出以下幾個內容:
我們很容易注意到其中的幾個重要信息,有下:
- UID : 代表執行者的身份
- PID : 代表這個進程的代號
- PPID :代表這個進程是由哪個進程發展衍生而來的,亦即父進程的代號
- PRI :代表這個進程可被執行的優先級,其值越小越早被執行
- NI :代表這個進程的nice值
PRI and NI:
PRI也還是比較好理解的,即進程的優先級,或者通俗點說就是程序被CPU執行的先后順序,此值越小進程的優先級別越高
那NI就是我們所要說的nice值了,其表示進程可被執行的優先級的修正數值
PRI值越小越快被執行,那么加入nice值后,將會使得PRI變為:PRI(new)=PRI(old)+nice。pri(old)?,都是從80開始的!
這樣,當nice值為負值的時候,那么該程序將會優先級值將變小,即其優先級會變高,則其越快被執行所以,調整進程優先級,在Linux下,就是調整進程nice值
nice其取值范圍是-20至19,一共40個級別。nice調整最小是:-20,超過部分統一當成-20。nice調整最大是:19,超過部分統一當成19。
PRI vs NI:
- 需要強調一點的是,進程的nice值不是進程的優先級,他們不是一個概念,但是進程nice值會影響到進程的優先級變化。可以理解nice值是進程優先級的修正修正數據
1.3 修改進程優先級的命令
用top命令更改已存在進程的nice:
- top
- 進入top后按“r”–>輸入進程PID–>輸入nice值
我們輸入進程號講 nice 改為19
普通用戶是沒有辦法將優先級調大,讓PRI減小的。需要使用su換成root,或使用sudo命令。?
2.進程間切換
2.1 相關概念
- 競爭性: 系統進程數目眾多,而CPU資源只有少量,甚至1個,所以進程之間是具有競爭屬性的。為了高效完成任務,更合理競爭相關資源,便具有了優先級。
- 獨立性: 多進程運行,需要獨享各種資源,多進程運行期間互不干擾。
- 并行: 多個進程在多個CPU下分別,同時進行運行,這稱之為并行。
- 并發: 多個進程在一個CPU下采用進程切換的方式,在一段時間之內,讓多個進程都得以推進,稱之為并發。
因為要并發,所以必定要考慮進程間切換。
每一個進程不是占有CPU就一直運行,每隔一段時間,會自動被操作系統從cpu上剝離下來。Linux內核是支持進程之間進行cpu資源搶占的!是基于時間片的輪轉式搶占式內核。
為什么我們函數內定義的棧臨時變量,會返回給外部,我們的程序/進程,它怎么知道我們當前運行到哪里里?如何做到函數間跳轉? 因為cpu內存在大量寄存器,比如eip是程序計數器或者叫pc,會自動執行我們要執行的下一行代碼。我們的進程在運行的時候,是會使用這些寄存器的,我們的進程,會產生各種數據,在寄存器中臨時保存。
如果我們有多個進程呢?各個進程在CPU寄存器中形成的臨時數據,都應該是不一樣的!而cpu內寄存器只有一套,我們如何做到讓每個進程使用cpu時能從上次時間片執行到的位置繼續執行?我們可以先簡單的理解為時間片結束后,cpu寄存器中的數據會先保存到進程PCB中,然后下次執行時先讀取PCB中存儲的寄存器的數據,然后繼續執行,本質就是將CPU寄存器的內容,保存到內存中!
2.2 Linux2.6內核進程調度隊列(了解即可)
上圖是Linux2.6內核中進程隊列的數據結構,之間關系也已經給大家畫出來,方便大家理解
一個CPU擁有一個runqueue
- 如果有多個CPU就要考慮進程個數的負載均衡問題
優先級
- 普通優先級:100~139(我們都是普通的優先級,想想nice值的取值范圍,可與之對應!)
- 實時優先級:0~99(我們這里不關心)
活動隊列
- 時間片還沒有結束的所有進程都按照優先級放在該隊列
- nr_active: 總共有多少個運行狀態的進程
- queue[140]: 一個元素就是一個進程隊列,相同優先級的進程按照FIFO規則進行排隊調度,所以,數組下標就是優先級!
- 從該結構中,選擇一個最合適的進程,過程是怎么的呢?
1. 從0下表開始遍歷queue[140]
2. 找到第一個非空隊列,該隊列必定為優先級最高的隊列
3. 拿到選中隊列的第一個進程,開始運行,調度完成!
4. 遍歷queue[140]時間復雜度是常數!但還是太低效了!
- bitmap[5]:一共140個優先級,一共140個進程隊列,為了提高查找非空隊列的效率,就可以用 5*32 個比特位表示隊列是否為空,即位段,這樣,便可以大大提高查找效率!
過期隊列
- 過期隊列和活動隊列結構一模一樣
- 過期隊列上放置的進程,都是時間片耗盡的進程,或者是剛到運行對列中的進程。
- 當活動隊列上的進程都被處理完畢之后,對過期隊列的進程進行時間片重新計算
active指針和expired指針
- active指針永遠指向活動隊列
- expired指針永遠指向過期隊列
- 可是活動隊列上的進程會越來越少,過期隊列上的進程會越來越多,因為進程時間片到期時一直都存在的。
- 沒關系,在合適的時候,只要能夠交換active指針和expired指針的內容,就相當于有具有了一批新的活動進程!
總結
在系統當中查找一個最合適調度的進程的時間復雜度是一個常數,不隨著進程增多而導致時間成本增加,我們稱之為進程調度O(1)算法
3.命令行參數
?命令行參數是什么?
我們看下面一段代碼看下面一段代碼
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{ int i = 0;for(;i<argc;i++){printf("%d: %s\n",i, argv[i]);}printf("hello world\n");return 0;
}
運行輸出:?
可以發現main函數是可以傳參的,shell 會自動講命令行輸入的一大串字符,按空格分割成小的字串,第一參數argc存命令行字串個數,第二個參數argc是一個指針數組存字串內容。
命令行參數,可以支持各種指令級別的命令行選項的設置!這樣就可以理解之前學的指令,選項是什么關系了。
我們來模擬實現一個計算器
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{ if(argc != 4) { printf("Use error\nUsage: %s op[-add|sub|mul|div] d1 d2\n",argv[0]); return 1; } // 你的程序一定有4個命令行參數,第一個是程序名 int x = atoi(argv[2]); int y = atoi(argv[3]); if(strcmp(argv[1],"-add")==0) { int result = x+y; printf("%d+%d=%d\n",x,y,result); } else if(strcmp(argv[1],"-sub")==0) { int result = x-y; printf("%d-%d=%d\n",x,y,result); } else if(strcmp(argv[1],"-mul")==0) { int result = x*y; printf("%d*%d=%d\n",x,y,result); } else if(strcmp(argv[1],"-div")==0) { if(0==y) {printf("%d/%d=error! div zero\n",x,y);}else {int result = x/y;printf("%d/%d=%d\n",x,y,result);}}else {printf("Use error,you should use the right command line\nUsage: %s op[-add|sub|mul|div] d1 d2\n",argv[0]);}return 0;
}
?運行結果:
對于為什么系統命令不用加 ./指定位置?,而我們寫的程序就需要寫 ./?,這就涉及到環境變量了,下一篇文章我們講解環境變量。
本篇結束!