一、進程的優(yōu)先級
-
什么是優(yōu)先級?
- 指定一個進程獲取某種資源的先后順序
- 本質(zhì)是進程獲取cpu資源的優(yōu)先順序
-
為什么要有優(yōu)先級
- 進程訪問的資源(CPU)是有限的
操作系統(tǒng)關(guān)于調(diào)度和優(yōu)先級的原則:分時操作系統(tǒng),基本的公平,如果進程因為長時間不被調(diào)整,就造成了饑餓問題
- Linux的優(yōu)先級特點以及查看方式
- 查看進程的優(yōu)先級
ps -lA
- PRI:進程優(yōu)先級
- NI:進程優(yōu)先級的修正數(shù)據(jù)
-
其中PRI值越低優(yōu)先級越高
-
因為PRI值是由操作系統(tǒng)內(nèi)核動態(tài)調(diào)整的,我們無法直接去調(diào)整這個值,所以我們必須通過nice值去調(diào)整它。nice值就是上圖PRI后面NI。
-
因為PRI是系統(tǒng)內(nèi)核去動態(tài)調(diào)整的,我們修改后需要經(jīng)過內(nèi)核的允許,如果這個PRI值超過了內(nèi)核的最大限度,那么這個值就會保留在臨界值。
-
我們的計算公式為:新的PRI = 進程默認(rèn)PRI + nice值,這個nice值有正負(fù)數(shù),我們可以舉一個例子:一個進程的PRI為80,我們給NI值為-10,再根據(jù)上面的公式得出新的PRI為70
- 那么如何修改呢?我們可以寫一個代碼來看一下:
#include<stdio.h>
int main()
{
while(1){
}
return 0;
}
- 我們可以查看它的PRI和NI值
-
我們可以通過下面的命令進行修改
-
number為想要的nice值,PID為要操作的進程
renice [number] [PID]
- 首先查看一下該進程的id
- 然后進行修改~
- 修改后就變成了PRI是
70
,NI是-10
二、進程的四個重要概念
- 競爭性:因為cpu資源優(yōu)先,所以進程難免會存在競爭行為,具體體現(xiàn)在優(yōu)先級上。
- 獨立性:進程運行期間,各個進程是不會相互干擾的,即使是父子進程。
- 并行:當(dāng)有多個cpu時,這些cpu同時處理多個進程的行為叫做并行。
- 并發(fā):在一段時間內(nèi),每個進程都可以被cpu處理一部分指令,這種行為稱為并發(fā)。
假設(shè)cpu處理一個進程的時間為1秒,那么1個cpu處理99個進程的時間就是99秒。但是當(dāng)有一臺擁有3個cpu的計算機處理這99個進程時,只需要33秒。這就是并行,多個cpu同時處理多個進程。
三、上下文切換
- 每個任務(wù)運行前,CPU 都需要知道任務(wù)從哪里加載、又從哪里開始運行,這就涉及到 CPU 寄存器 和 程序計數(shù)器(PC):
如何切換?
- 將前一個 CPU 的上下文(也就是 CPU 寄存器和程序計數(shù)器里邊的內(nèi)容)保存起來;
- 然后加載新任務(wù)的上下文到寄存器和程序計數(shù)器;
- 最后跳轉(zhuǎn)到程序計數(shù)器所指的新位置,運行新任務(wù)。
被保存起來的上下文會存儲到系統(tǒng)內(nèi)核中,等待任務(wù)重新調(diào)度執(zhí)行時再次加載進來。
CPU 的上下文切換分三種:進程上下文切換、線程上下文切換、中斷上下文切換。
- 把臨時數(shù)據(jù)轉(zhuǎn)存到操作系統(tǒng)的行為叫做上下文保護,把臨時數(shù)據(jù)寫回寄存器內(nèi)的行為叫做上下文恢復(fù)。
四、環(huán)境變量
- 我們對于Linux的理解,指令就是程序,我們寫的C語言代碼也是一個程序,那么有一個問題,為什么Linux的指令他直接就可以在bash(終端)上運行,為什么我們寫的代碼生成的可執(zhí)行文件
-
在我們運行程序的時候,需要知道此程序在哪個位置
-
在Linux的中的命令,它為什么不需要指定路徑來執(zhí)行呢?是因為有個叫【PATH】的環(huán)境變量,在我們輸入指令后,會在指定路徑下查找,如果找不到要執(zhí)行的指令就會返回錯誤【command not found】
- 因為【PATH】變量沒有記錄我們輸入的指令的位置信息,所以我們必須手動指定指令的位置。那么我們可以總結(jié)出指令(程序)是如何執(zhí)行的
- 我們可以查看一下PATH下有哪些路徑
echo $PATH
- 可以看到上面是有各種路徑每個路徑是一下【:】分割,我們可以看到有一個
/usr/bin
目錄,那么我們寫的這個程序也就可以拷貝到這個目錄下就可以不指定路徑直接執(zhí)行了 - 第二個方法是將我當(dāng)前這個目錄的路徑添加到這個環(huán)境變量中,這樣也可以
- 我們可以用下面的這條指令來修改系統(tǒng)變量
export PATH=路徑
- 發(fā)現(xiàn)我們剛剛查看的變量不在了,ls也無法執(zhí)行了
- 這個時候不要慌,我們可以另外再開一個終端再看
- 那么我們?nèi)绾握_的向[PATH]添加一個路徑呢?我們用到下面的指令:
export PATH=$PATH:路徑
- 這就完成了添加一個環(huán)境變量的操作
-
那么為什么新開了一個終端它就又恢復(fù)了呢?
- 這是因為在我們默認(rèn)查看的環(huán)境變量是內(nèi)存級的
- 最開始的環(huán)境變量不是在內(nèi)存中,是在對應(yīng)的配置文件中,登錄Linux系統(tǒng)的時候它會首先加載到bash進程中(內(nèi)存)
-
那么這個配置文件在哪?
.bash_profile # 當(dāng)前登錄用戶環(huán)境變量
.bashrc # 當(dāng)前登錄用戶環(huán)境變量
/etc/bashrc # 全局環(huán)境變量
查看當(dāng)前shell環(huán)境下的環(huán)境變量與內(nèi)容
env
-
環(huán)境變量是隨著啟動操作系統(tǒng)時生成的,也就是說,環(huán)境變量是屬于bash的。
-
指令是一個程序,在bash上執(zhí)行,那么這個程序就是bash的子進程
-
我們平時所用的pwd命令就是有一個環(huán)境變量叫pwd,這個環(huán)境變量存儲著用戶當(dāng)前的所在位置
-
我們也可以自己實現(xiàn)一個pwd指令
-
在實現(xiàn)的時候需要了解一個函數(shù)getenv,我們用man手冊查看一下
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* ret = getenv("PWD");
printf("%s\n",ret);
return 0;
}
- 可以看到我們就實現(xiàn)了這個
- 我們在bash上運行的程序,是bash的子進程,而環(huán)境變量是屬于bash的,子進程為什么能用父進程的環(huán)境變量?這是因為,子進程可以繼承父進程的環(huán)境變量!并且,環(huán)境變量一定是全局屬性的!
- 在子進程是如何繼承環(huán)境變量的?子進程是不是有一個主函數(shù)?這個主函數(shù)我們平時使用時是沒有參數(shù)的,但實際上它是可以帶參數(shù)的!還能帶三個!
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[], char* environ[])
{
return 0;
}
- 第一個參數(shù)代表的意思為:指令參數(shù)的個數(shù)(包括指令);
- 第二個參數(shù)代表的意思為:指令參數(shù)的指針數(shù)組(因為指令參數(shù)是一個字符串);
- 第三個參數(shù)代表的意思為:環(huán)境變量的指針數(shù)組(因為環(huán)境變量是一個字符串)。我們一般不使用第三個參數(shù),而是使用操作系統(tǒng)提供的外部的指針數(shù)組指針【char** environ】或者是系統(tǒng)提供的接口函數(shù)
getenv()
。
- 我們就可以實現(xiàn)一個帶參數(shù)的指令,就像ls類似的
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
if(argc < 2)
{
printf("指令參數(shù)太少!\n");
return 1;
}
if(strcmp(argv[1],"-a")==0)
{
printf("執(zhí)行-a\n");
}
else if(strcmp(argv[1],"-b")==0)
{
printf("執(zhí)行-b\n");
}
else
{
printf("指令有誤!\n");
}
return 0;
}
- 我們可以再寫一個代碼來驗證一下
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
printf("%d\n",argc);
int i=0;
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
return 0;
}
-
從上面 可以看出 [argc]是存儲指令參數(shù)的個數(shù)的(包括指令),[char* argv[]]這個指針數(shù)組是存儲指令參數(shù)的(包括指令)
-
對于第三個參數(shù),是一個指針數(shù)組,存儲的是各個環(huán)境變量的內(nèi)容,因為這些內(nèi)容是字符串常量,而表示字符串常量通常使用其首字符地址
-
我們是很少使用第三個參數(shù)的,因為這個數(shù)組存儲了所有的環(huán)境變量,想要找到特定的環(huán)境變量還是挺困難的,那么我們使用這段代碼,證明第三個參數(shù)存儲了環(huán)境變量:
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[],char* environ[])
{
int i = 0;
for(i = 0; environ[i]; i++)
{
printf("[%d]-->%s\n",i, environ[i]);
}
return 0;
}
- 或者使用另一種寫法可以完成
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
extern char** environ;
int i=0;
for( i=0;environ[i];i++)
{
printf("[%d]-->%s\n",i,environ[i]);
}
return 0;
}
-
環(huán)境變量是具有全局屬性的,也就意味著子進程只能繼承父進程的具有全局屬性的環(huán)境變量。稱作本地變量。如何設(shè)置本地變量呢?我們只需要在bash上面按這個格式敲指令:
-
變量中間不能有空格
[變量名]=[內(nèi)容]
-
我們發(fā)現(xiàn)使用env來查看我們設(shè)置的變量,并不能顯示出結(jié)果,證明了我們剛剛設(shè)置的變量是本地變量
-
但是使用【echo】命令還可以查看到,因為echo是可以操作環(huán)境變量的,所用echo命令是可以操作所有的變量的,不管是本地變量還是環(huán)境變量。
-
子進程并沒有繼承父進程的本地變量,那我們?nèi)绾问贡镜刈兞孔兂森h(huán)境變量呢?我們輸入下面這個指令:
export [變量名稱]
- 現(xiàn)在我們學(xué)會了如何設(shè)置本地變量和如何把本地變量轉(zhuǎn)換成環(huán)境變量了。那么如何查看本地變量呢,或者說如何查看所有的變量呢?我們使用下面這條命令:
set
- 取消變量可以使用下面這條命令
unset [變量名]
五、Linux2.6內(nèi)核進程調(diào)度隊列
一個CPU擁有一個runqueue
如果有多個CPU就要考慮進程個數(shù)的父子均衡問題。
優(yōu)先級
queue下標(biāo)說明:
- 普通優(yōu)先級:100~139。
- 實時優(yōu)先級:0~99。
我們進程的都是普通的優(yōu)先級,前面說到nice值的取值范圍是-20~19,共40個級別,依次對應(yīng)queue當(dāng)中普通優(yōu)先級的下標(biāo)100~139。
注意: 實時優(yōu)先級對應(yīng)實時進程,實時進程是指先將一個進程執(zhí)行完畢再執(zhí)行下一個進程,現(xiàn)在基本不存在這種機器了,所以對于queue當(dāng)中下標(biāo)為0~99的元素我們不關(guān)心。
活動隊列
時間片還沒有結(jié)束的所有進程都按照優(yōu)先級放在活動隊列當(dāng)中,其中nr_active代表總共有多少個運行狀態(tài)的進程,而queue[140]數(shù)組當(dāng)中的一個元素就是一個進程隊列,相同優(yōu)先級的進程按照FIFO規(guī)則進程排隊調(diào)度。
調(diào)度過程如下:
- 從0下標(biāo)開始遍歷queue[140]。
- 找到第一個非空隊列,該隊列必定為優(yōu)先級最高的隊列。
- 拿到選中隊列的第一個進程,開始運行,調(diào)度完成。
- 接著拿到選中隊列的第二個進程進行調(diào)度,直到選中進程隊列當(dāng)中的所有進程都被調(diào)度。
- 繼續(xù)向后遍歷queue[140],尋找下一個非空隊列。
bitmap[5]:queue數(shù)組當(dāng)中一共有140個元素,即140個優(yōu)先級,一共140個進程隊列,為了提高查找非空隊列的效率,就可以用5 × \times× 32個比特位表示隊列是否為空,這樣一來便可以大大提高查找效率。
總結(jié): 在系統(tǒng)當(dāng)中查找一個最合適調(diào)度的進程的時間復(fù)雜度是一個常數(shù),不會隨著進程增多而導(dǎo)致時間成本增加,我們稱之為進程調(diào)度的O(1)算法。文章來源:http://www.zghlxwxcb.cn/news/detail-845281.html
過期隊列
- 過期隊列和活動隊列的結(jié)構(gòu)相同。
- 過期隊列上放置的進程都是時間片耗盡的進程。
- 當(dāng)活動隊列上的進程被處理完畢之后,對過期隊列的進程進行時間片重新計算。
active指針和expired指針
- active指針永遠(yuǎn)指向活動隊列。
- expired指針永遠(yuǎn)指向過期隊列。
由于活動隊列上時間片未到期的進程會越來越少,而過期隊列上的進程數(shù)量會越來越多(新創(chuàng)建的進程都會被放到過期隊列上),那么總會出現(xiàn)活動隊列上的全部進程的時間片都到期的情況,這時將active指針和expired指針的內(nèi)容交換,就相當(dāng)于讓過期隊列變成活動隊列,活動隊列變成過期隊列,就相當(dāng)于又具有了一批新的活動進程,如此循環(huán)進行即可。文章來源地址http://www.zghlxwxcb.cn/news/detail-845281.html
到了這里,關(guān)于Linux_進程的優(yōu)先級&&環(huán)境變量&&上下文切換&&優(yōu)先級隊列的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!