1、進(jìn)程優(yōu)先級
排隊(duì)的本質(zhì)就是確認(rèn)優(yōu)先級。
優(yōu)先級是什么?它也是PCB中的一個(gè)整型字段,數(shù)值越小,優(yōu)先級越高。是得到某種資源的先后順序。
Linux進(jìn)程的優(yōu)先級數(shù)值范圍:60~99。
Linux中默認(rèn)進(jìn)程的優(yōu)先級都是80。
為什么要有優(yōu)先級?本質(zhì)是資源不足。
談到優(yōu)先級,就不得不說我們以前學(xué)的權(quán)限,它兩區(qū)別是什么呢?
權(quán)限是能不能得到某種資源。優(yōu)先級是保證能申請到某種資源,只不過需要等一等。就如現(xiàn)實(shí)中,我們?nèi)コ燥?,人很多,但是做飯師傅只有那么幾個(gè),看到能給你做好端上來,只不過是有順序的,講究先來后到。
Linux下進(jìn)程的優(yōu)先級概念:
- cpu資源分配的先后順序,就是指進(jìn)程的優(yōu)先權(quán)(priority)。
- 優(yōu)先權(quán)高的進(jìn)程有優(yōu)先執(zhí)行權(quán)利。配置進(jìn)程優(yōu)先權(quán)對多任務(wù)環(huán)境的linux很有用,可以改善系統(tǒng)性能。
- 還可以把進(jìn)程運(yùn)行到指定的CPU上,這樣一來,把不重要的進(jìn)程安排到某個(gè)CPU,可以大大改善系統(tǒng)整體性能。
1.1 Linux下查看進(jìn)程優(yōu)先級
首先我們先寫一份C語言代碼:
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("I am process, pid: %d\n", getpid());
sleep(1);
}
return 0;
}
編譯運(yùn)行,我們查看一下該進(jìn)程的優(yōu)先級
ps -la
Linux下進(jìn)程的優(yōu)先級是由兩部分組成的:PRI(priority) + NI(nice)
那么優(yōu)先級可不可以改呢? 可以,下面我們就學(xué)習(xí)一下怎么改。
1.2 Linux 進(jìn)程優(yōu)先級的修改
修改進(jìn)程的優(yōu)先級,并不是直接修改 PRI 而是修改NI值,從而達(dá)到修改PRI值的。
PRI and NI
- PRI也還是比較好理解的,即進(jìn)程的優(yōu)先級,或者通俗點(diǎn)說就是程序被CPU執(zhí)行的先后順序,此值越小進(jìn)程的優(yōu)先級別越高。
- 那NI呢?就是我們所要說的nice值了,其表示進(jìn)程可被執(zhí)行的優(yōu)先級的修正數(shù)。
這里修改PRI值的公式:PRI(new) = PRI(old) + nice
top命令配合操作更改優(yōu)先級
輸入top命令 -> 輸入r -> 輸入PID -> 輸入nice值 就完成了進(jìn)程優(yōu)先級的修改,我們來試一下:
我們輸入top后回車,來到此界面,再輸入r后就提示我們輸入PID
接下來我們輸入PID:
輸入完P(guān)ID,它提示我們輸入nice值,我們輸入10:
完成后,按q退出。
我們來查看一下13312進(jìn)程的優(yōu)先級有沒有被改:
我們可以看到進(jìn)程的優(yōu)先級已經(jīng)被改,并且nice值確實(shí)也變了。修改后PRI值變大了,意味著進(jìn)程優(yōu)先級順序變小了。
我們在此基礎(chǔ)上來修改一下nice值,這次我們輸入-10,看看優(yōu)先級會(huì)如何變化:
這里顯示將PID為13312的進(jìn)程nice值設(shè)置成-10是不被允許的操作,這是怎么回事?
進(jìn)程的優(yōu)先級修改時(shí),如果我們只是將優(yōu)先級調(diào)小,普通用戶身份就可以完成。但是如果我們想要調(diào)大,就需要以管理員身份/普通用戶身份提權(quán)來操作,因此我們 top前加sudo 來操作。
這下確實(shí)能修改了,但是我們是在之前的基礎(chǔ)上修改的呀,為什么nice值改為-10后,PRI值卻是70呢?
因?yàn)槊看涡薷腜RI值的時(shí)候,PRI(old)值都是以80為基礎(chǔ),加上設(shè)置的nice值,得出的數(shù)值再賦給PRI(new)的。
我們再來試試,直接將nice設(shè)置為100,再看看PRI值會(huì)被改為多少。
這里我們發(fā)現(xiàn),nice值并不是-100,而是-20,這又是為什么呢?
因?yàn)槲覀儍?yōu)先級最高是60,不管我們怎么去調(diào)整,最高60就限制了nice最小只能到-20,只要我們輸入的nice值小于等于-20,都會(huì)被當(dāng)-20來處理。
那么PRI的值最大是99,由此我們可以推測出,nice值最大被限制在19了,輸入nice值大于等于19,都會(huì)被當(dāng)作19來處理。
我們來測試一下:
確實(shí)不出我們所料呀。
我們看到有疑惑,為什么要把優(yōu)先級限定在一定的范圍內(nèi)呢?
os調(diào)度的時(shí)候,要做到公平,較為均衡的讓每一個(gè)進(jìn)程都要得到調(diào)度。因?yàn)閮?yōu)先級可以改,用戶惡意讓自己的進(jìn)程不斷在最前面執(zhí)行,那么容易導(dǎo)致優(yōu)先級較低的進(jìn)程,長時(shí)間得不到CPU資源,導(dǎo)致進(jìn)程饑餓。
總結(jié):
- PRI值越小越快被執(zhí)行,那么加入nice值后,將會(huì)使得PRI變?yōu)椋?PRI(new)=PRI(old)+nice。
- 這樣,當(dāng)nice值為負(fù)值的時(shí)候,那么該程序?qū)?huì)優(yōu)先級值將變小,即其優(yōu)先級會(huì)變高,則其越快被執(zhí)行。
- 所以,調(diào)整進(jìn)程優(yōu)先級,在Linux下,就是調(diào)整進(jìn)程nice值。
- nice其取值范圍是-20至19,一共40個(gè)級別。
- 需要強(qiáng)調(diào)一點(diǎn)的是,進(jìn)程的nice值不是進(jìn)程的優(yōu)先級,他們不是一個(gè)概念,但是進(jìn)程nice值會(huì)影響到進(jìn)程的優(yōu)先級變化。
- 可以理解nice值是進(jìn)程優(yōu)先級的修正修正數(shù)據(jù)。
1.3 競爭 獨(dú)立 并行 并發(fā)
- 競爭性: 系統(tǒng)進(jìn)程數(shù)目眾多,而CPU資源只有少量,甚至1個(gè),所以進(jìn)程之間是具有競爭屬性的。為了高效完成任務(wù),更合理競爭相關(guān)資源,便具有了優(yōu)先級。
- 獨(dú)立性: 多進(jìn)程運(yùn)行,需要獨(dú)享各種資源,多進(jìn)程運(yùn)行期間互不干擾。
- 并行: 多個(gè)進(jìn)程在多個(gè)CPU下分別,同時(shí)進(jìn)行運(yùn)行,這稱之為并行。
- 并發(fā): 多個(gè)進(jìn)程在一個(gè)CPU下采用進(jìn)程切換的方式,在一段時(shí)間之內(nèi),讓多個(gè)進(jìn)程都得以推進(jìn),稱之為并發(fā)。
調(diào)整系統(tǒng)優(yōu)先級有很多方法,這里我們只說了這一種方式,我們學(xué)習(xí)了本質(zhì),有了底層的打底,操作再去學(xué)習(xí)就會(huì)變的簡單了,并且輕易我們是不需要去改優(yōu)先級的。
2、進(jìn)程間切換
我們要知道,一個(gè)進(jìn)程并不是占有CPU就一直運(yùn)行,每隔一段時(shí)間,自動(dòng)被從CPU上剝離下來,所以這就會(huì)存在進(jìn)程切換的過程。
Linux內(nèi)核支持進(jìn)程之間進(jìn)行CPU資源搶占的。
如果一個(gè)進(jìn)程的PRI值是80,它正在CPU上運(yùn)行,但是又來一個(gè)進(jìn)程,它的PRI是60,意味著它需要被運(yùn)行,這時(shí)就把PRI為80的進(jìn)程拿下來,先讓PRI為60的進(jìn)程跑,可能此時(shí)時(shí)間還沒到。
所以Linux調(diào)度的原則全程:基于時(shí)間片的輪轉(zhuǎn)式搶占式內(nèi)核。
CPU運(yùn)行的時(shí)間特別快,我們感知不到,在一段時(shí)間內(nèi),進(jìn)程是高速的的不斷的切換,我們所感知到的進(jìn)程是并發(fā)的。
問題: 進(jìn)程切換的時(shí)候,可能一份代碼并沒有跑完,但是需要切換另外一個(gè)進(jìn)程了,那下一次再運(yùn)行此進(jìn)程的時(shí)候,CPU怎么知道是從哪開始呢?
CPU中有寄存器,eip就是專門記錄程序運(yùn)行到哪一行的。
由此,我們也可以知道,一個(gè)CPU雖然只有一套寄存器硬件,但是寄存器的數(shù)據(jù)不僅僅是一套。寄存器 != 寄存器內(nèi)容。并且進(jìn)程切換后,進(jìn)程的數(shù)據(jù)存在CPU的寄存器中。
問題: CPU內(nèi)的寄存器數(shù)據(jù)保存在哪呢?
CPU內(nèi)的寄存器數(shù)據(jù)保存在進(jìn)程PCB中(簡單理解),本質(zhì):CPU寄存器的內(nèi)容,保存在內(nèi)存中。
保存完之后,寄存器中的數(shù)據(jù)不會(huì)清空,下個(gè)進(jìn)程被調(diào)度運(yùn)行時(shí),直接覆蓋即可。
3、Linux2.6內(nèi)核進(jìn)程調(diào)度隊(duì)列
這部分是選學(xué),讀者看自己情況來決定。
下圖就是CPU維護(hù)的運(yùn)行隊(duì)列,我們主要盯著藍(lán)色框和紅色框來看:
3.1 活躍進(jìn)程
首先,我們要知道,Linux下進(jìn)程優(yōu)先級范圍為60~99共計(jì)40個(gè)等級,其次,運(yùn)行隊(duì)列是分活躍隊(duì)列和過期隊(duì)列,他們兩個(gè)的功能后面我們就知道了。
CPU在運(yùn)行時(shí),會(huì)從上到下去掃描隊(duì)列,優(yōu)先級為n的位置不為空就會(huì)去運(yùn)行它指向的PCB,為空就往后走,去找不為空的位置,直到遍歷完隊(duì)列。
3.2 過期進(jìn)程
問題: 如果CPU正在運(yùn)行隊(duì)列中,運(yùn)行優(yōu)先級為99的進(jìn)程,而現(xiàn)在一個(gè)優(yōu)先級為70的進(jìn)程加載進(jìn)來,會(huì)直接放入活躍進(jìn)程隊(duì)列中,與正在運(yùn)行的隊(duì)列搶占資源嗎?
肯定是不會(huì)的,如果我們不斷加載進(jìn)來進(jìn)程,原本優(yōu)先級低的進(jìn)程,遲遲得不到CPU資源,導(dǎo)致進(jìn)程饑餓!
所以新加載進(jìn)來的進(jìn)程,會(huì)先去過期進(jìn)程數(shù)組按照優(yōu)先級順序排隊(duì)。
在活躍進(jìn)程數(shù)組沒有被全部運(yùn)行完時(shí),CPU不會(huì)去調(diào)度過期進(jìn)程的數(shù)組,這就保證了公平性。
當(dāng)活躍隊(duì)列的進(jìn)程全部運(yùn)行完后,CPU不是去過期進(jìn)程數(shù)組調(diào)度,而是swap(active, expired),改變指向后,依然運(yùn)行活躍進(jìn)程數(shù)組。
由此我們也就知道,在進(jìn)程間切換的時(shí)候,我們就是把換出的進(jìn)程放入了過期進(jìn)程的數(shù)組中,等待CPU下一次的調(diào)度。
4 main函數(shù)參數(shù) — 命令行參數(shù)
main函數(shù)是有兩個(gè)參數(shù)的,int argc,char* argv[]。
我們來介紹一下:
int main(int argc, char* argv[]);
// 這兩個(gè)參數(shù)叫做命令行參數(shù)
// argv是一個(gè)數(shù)組,類型是char*,也就是指針數(shù)組
// argc表示這個(gè)數(shù)組的元素個(gè)數(shù)
我們現(xiàn)在還不知道這個(gè)數(shù)組中存放的是什么內(nèi)容,我們在Linux下寫一個(gè)main函數(shù),打印一下數(shù)組內(nèi)容看看:
#include <stdio.h>
int main(int argc, char* argv[])
{
int i = 0;
for( ; i < argc; i++)
{
printf("argv[%d]: %s\n", i, argv[i]);
}
return 0;
}
我們發(fā)現(xiàn),它把我們的命令打印出來了,我們多寫點(diǎn)看看如何。
這里原來是把我們的命令以空格分割開,存入到字符串指針數(shù)組中的!
問題: 那是誰把命令放入到數(shù)組中的?
將命令行輸入的字符串放入數(shù)組是os干的。
4.1 利用main函數(shù)的參數(shù)實(shí)現(xiàn)一個(gè)計(jì)算器
既然main函數(shù)可以拿到命令行輸入的字符串,那我們可以用其寫出一個(gè)簡易的計(jì)算器,代碼如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[]) // argc代表參數(shù)個(gè)數(shù),argv是字符串?dāng)?shù)組參數(shù)
{
// 簡易版計(jì)算器
if(argc != 4)
{
printf("Use Error!\nUsage %s op[-add|-sub|-mul|-div] d1 d2\n", argv[0]);
return 0;
}
// 將字符數(shù)字轉(zhuǎn)為
int x = atoi(argv[2]);
int y = atoi(argv[3]);
int result = 0;
// 你的程序一定有4個(gè)命令行參數(shù),第一個(gè)是程序名
if(strcmp("-add", argv[1]) == 0)
{
result = x + y;
printf("%d + %d = %d\n", x, y, result);
}
else if(strcmp("-sub", argv[1]) == 0)
{
result = x - y;
printf("%d - %d = %d\n", x, y, result);
}
else if(strcmp("-mul", argv[1]) == 0)
{
result = x * y;
printf("%d * %d = %d\n", x, y, result);
}
else if(strcmp("-div", argv[1]) == 0)
{
if(y == 0) printf("%d / %d = error! div zero\n", x, y);
else
{
result = x / y;
printf("%d / %d = %d\n", x, y, result);
}
}
else
{
printf("Use Error!\nThe second parameter should is [-add|-sub|-mul|-div] !\n");
return 0;
}
return 0;
}
使用方法:
1、先輸入可執(zhí)行程序;
2、第二個(gè)字符串輸入 -add/-sub/-mul/-div;
3、第三個(gè),第四個(gè)輸入操作數(shù);
4.2 模擬實(shí)現(xiàn)touch命令
在使用完我們寫的計(jì)算器后,有沒有感覺到,跟我們的Linux下指令很類似。
比如:ls命令,我們可以配選項(xiàng):ls -l -a。
這些指令就是用C語言寫的,加上一些選項(xiàng)在顯示器上顯示不同結(jié)果,它們的本質(zhì)不就是用到了命令行參數(shù)么。
下面我們自己在實(shí)現(xiàn)一份touch指令代碼:
#include <stdio.h>
int main(int argc, char* argv[])
{
if(argc != 2) // 輸入的字符串不規(guī)范
{
printf("touch missing file operand!\n");
return 1;
}
FILE* fp = fopen(argv[1], "w"); // 以"w"方式打開,當(dāng)文件不存在時(shí),會(huì)自動(dòng)創(chuàng)建
if(fp != NULL)
fclose(fp);
return 0;
}
我們使用自己寫的touch來創(chuàng)建文件試一下:
5、環(huán)境變量
系統(tǒng)中有很多的環(huán)境變量,我們可以使用 env 命令來查看一下:
其他我們不知道,今天我們挑著來學(xué)幾個(gè)。
5.1 PATH的認(rèn)識
我們命令行參數(shù)算是學(xué)明白了,但是大家有沒有想過這樣幾個(gè)問題。
問題:
- 為什么我們寫出來的程序在執(zhí)行的時(shí)候需要加 ./
- 為什么在執(zhí)行系統(tǒng)的指令的時(shí)候不需要加 ./
執(zhí)行一個(gè)程序的前提是先找到這個(gè)可執(zhí)行程序,我們寫的程序在執(zhí)行的時(shí)候,加上 ./ 本質(zhì)是告訴bash在當(dāng)前目錄下找該程序。
系統(tǒng)指令也是 C/C++ 寫出來的程序,那為什么系統(tǒng)指令不需要加呢?
一般的系統(tǒng)指令我們可以直接用,也可以加上它的路徑執(zhí)行:
這里系統(tǒng)指令不用加 ./ 就得提一下一個(gè)概念了:環(huán)境變量。
記錄系統(tǒng)當(dāng)中默認(rèn)搜索路徑的環(huán)境變量叫做:PATH(一般環(huán)境變量名是全大寫的)。
在執(zhí)行系統(tǒng)指令時(shí),系統(tǒng)會(huì)去PATH中找當(dāng)前的可執(zhí)行程序在不在這些路徑中,如果在正常執(zhí)行,不在就報(bào)錯(cuò)。
查看環(huán)境變量內(nèi)容指令:
echo $xxx // 例如查看PATH,echo $PATH
這些路徑按 : 分割開
先在/usr/local/bin找,沒有去下一個(gè)路徑找,找到執(zhí)行,沒找到繼續(xù)下一個(gè),找完都沒有就返回錯(cuò)誤信息。
因?yàn)槲覀冏约旱目蓤?zhí)行程序并不在這些路徑中,所以我們的可執(zhí)行程序在執(zhí)行的時(shí)候需要加上 ./ 來執(zhí)行。
5.2 修改環(huán)境變量PATH
想要我們自己的程序像系統(tǒng)指令一樣運(yùn)行,我們可以將可執(zhí)行程序的路徑追加到環(huán)境變量PATH中去!
命令如下:
PATH = $PATH:要添加的路徑
注意: 這里不能直接等于要添加的路徑,直接等于就會(huì)覆蓋掉原本的PATH,因?yàn)槲覀円凑丈厦孀芳拥男问降膩韺憽?br>
此時(shí),我們再去運(yùn)行我們自己的mytouch程序就不用再帶 ./ 了。我們來試試:
注意: 默認(rèn)更改環(huán)境變量,只限于本次登錄,重新登陸,環(huán)境變量自動(dòng)被恢復(fù)。想要一勞永逸,可以將自己的可執(zhí)行程序放入到默認(rèn)的路徑中(這個(gè)過程就做程序安裝)。
5.3 HOME的認(rèn)識
問題: 大家有沒有疑惑過,我們在登錄xshell的時(shí)候,我們以不同身份登錄,進(jìn)入的是不同的目錄呢 ?
問題:
- 憑什么普通用戶,登錄后默認(rèn)所處的目錄是/home/XXX ?
- 憑什么root,登錄后默認(rèn)所處的目錄是/root ?
因?yàn)橄到y(tǒng)中有一個(gè)環(huán)境變量HOME,在我們輸入用戶名與密碼的時(shí)候,識別出登錄者身份,初始化HOME,它能識別出登錄者是普通用戶還是root,并完成正確的初始化。
系統(tǒng)在登錄的最后一刻,cd HOME所記錄的路徑,因此就實(shí)現(xiàn)了不同身份,進(jìn)入不同的目錄。
總結(jié):
1、輸入用戶名 && 密碼;
2、認(rèn)證;
3、形成環(huán)境變量(不止一個(gè));
?3.1 根據(jù)用戶名,初始化
H
O
M
E
=
/
r
o
o
t
,
HOME=/root,
HOME=/root,HOME=/home/XXX
4、最后,cd $HOME。
5.4 如何獲取環(huán)境變量
我們知道了環(huán)境變量,以及查看環(huán)境變量命令,加之對HOME這個(gè)環(huán)境變量的學(xué)習(xí)后
我們明白了,系統(tǒng)中會(huì)存在大量的環(huán)境變量,每一個(gè)環(huán)境變量都有自己的特殊用途,用來完成特定的系統(tǒng)功能!
問題: 那么如何獲取環(huán)境變量呢?
我們這里講一個(gè)函數(shù),getenv(),并配合一個(gè)場景來講。
這里講了,getenv()函數(shù)是以變量名來查找的,返回相應(yīng)的字符串指針。
我們是不是可以按照用戶名來對用戶做甄別,不同用戶去執(zhí)行不同的代碼。
假如,我們寫一段代碼,只想讓root來執(zhí)行,普通用戶不能執(zhí)行:文章來源:http://www.zghlxwxcb.cn/news/detail-810166.html
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* user = getenv("USER"); // 獲取環(huán)境變量USER的內(nèi)容
if(strcmp(user, "root") == 0)
{
printf("myself command!\n");
}
else
{
printf("%s是一個(gè)非法用戶!\n", user);
}
return 0;
}
編譯后我們以普通用戶身份運(yùn)行:
我們再切換到root身份運(yùn)行:在這里插入代碼片
文章來源地址http://www.zghlxwxcb.cn/news/detail-810166.html
到了這里,關(guān)于[Linux 進(jìn)程(三)] 進(jìn)程優(yōu)先級,進(jìn)程間切換,main函數(shù)參數(shù),環(huán)境變量的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!