太原網(wǎng)站建設(shè)丿薇怎么推廣網(wǎng)址
不知道大家有沒(méi)有思考過(guò)這樣一個(gè)問(wèn)題:什么是處理器(CPU)的狀態(tài)?🤔
其實(shí)CPU和人一樣,沒(méi)有執(zhí)行程序的時(shí)候,是沒(méi)有什么狀態(tài)的,當(dāng)它執(zhí)行的程序是用戶程序的時(shí)候就叫用戶態(tài),當(dāng)執(zhí)行的程序是操作系統(tǒng)的代碼時(shí)就叫系統(tǒng)態(tài)或者內(nèi)核態(tài).
接下來(lái),我們就來(lái)談?wù)剝?nèi)核態(tài)和用戶態(tài).
目錄
1.內(nèi)核態(tài)和用戶態(tài)的概念?
2.內(nèi)核態(tài)和用戶態(tài)的區(qū)別
3.特權(quán)指令和非特權(quán)指令?
這是一個(gè)最簡(jiǎn)單的HelloWorld程序
1.內(nèi)核態(tài)和用戶態(tài)的概念?
內(nèi)核態(tài):可以訪問(wèn)所有的硬件設(shè)備,也可以執(zhí)行硬件上能夠運(yùn)行的各種指令
用戶態(tài):只能執(zhí)行一部分機(jī)器指令,不可以運(yùn)行I/O命令或者影響機(jī)器控制的命令
?操作系統(tǒng)是運(yùn)行在內(nèi)核態(tài)的,而操作系統(tǒng)提供的用戶接口程序和支持的應(yīng)用程序,是運(yùn)行在用戶態(tài)的.
?
2.內(nèi)核態(tài)和用戶態(tài)的區(qū)別
那么,用戶態(tài)和內(nèi)核態(tài)又有什么區(qū)別呢?
📢我們先看內(nèi)核態(tài).在內(nèi)核態(tài)上運(yùn)行的所有程序,都是可以訪問(wèn)所有的硬件資源的,可以在硬件上執(zhí)行各種指令
📢而用戶態(tài),只能執(zhí)行一部分指令.對(duì)于那些影響系統(tǒng)穩(wěn)定性的指令,還有I/O指令,都是不允許執(zhí)行的.
?
但是有的人可能會(huì)提出疑問(wèn),我可以使用fopen()函數(shù)打開(kāi)一個(gè)文件,并且還能對(duì)這個(gè)文件進(jìn)行讀寫(xiě)操作,我這個(gè)程序也是運(yùn)行在用戶態(tài)的,但是我可以做I/O操作呀.😦
其實(shí)我們不知道的是,C語(yǔ)言他默默幫我們封裝了一個(gè)glibc庫(kù)函數(shù),并在里面調(diào)用了系統(tǒng)函數(shù),然后由操作系統(tǒng)根據(jù)你傳入的指令,比如打開(kāi)文件指令,讀取文件指令,去操作硬盤(pán)上的硬件,因此,glibc內(nèi)部封裝了一個(gè)接口程序,通過(guò)這個(gè)接口程序,去調(diào)用內(nèi)核態(tài)的指令.🤗
3.特權(quán)指令和非特權(quán)指令?
在計(jì)算機(jī)中,存在指令集,指令集中有些指令是用戶態(tài)上可以運(yùn)行,有的只有在內(nèi)核態(tài)上才能運(yùn)行.
?我們將只有操作系統(tǒng)能使用,而用戶不能使用的指令稱為特權(quán)指令。
?而有一部分指令用戶態(tài)和操作系統(tǒng)都能使用的就叫做非特權(quán)指令。
?因?yàn)椴豢赡茏寫(xiě)?yīng)用程序或者程序員去擅自訪問(wèn)某個(gè)扇區(qū)中的二進(jìn)制數(shù)據(jù),必須要經(jīng)過(guò)文件系統(tǒng)才能訪問(wèn)扇區(qū)中的數(shù)據(jù).
🐞我們舉個(gè)簡(jiǎn)單例子來(lái)說(shuō)明:
這是一個(gè)最簡(jiǎn)單的HelloWorld程序
#include<stdio.h>
int main()
{char str[]="Hello World\n";printf("%s",str);return 0;
}
通過(guò)這個(gè)圖我們可以看出來(lái),在這個(gè)程序中,main函數(shù)肯定是運(yùn)行在用戶態(tài)的,在main函數(shù)中,還執(zhí)行了一個(gè)printf函數(shù),將HelloWorld打印輸出到顯示器中.?
它是將內(nèi)存中的HelloWorld輸出到控制臺(tái),目前這個(gè)printf函數(shù)是運(yùn)行在用戶態(tài)的,只不過(guò)打印輸出的時(shí)候,printf肯定要和外部設(shè)備,比如說(shuō)顯示器打交道.
我們都知道操作系統(tǒng)內(nèi)部有一個(gè)out指令,他就可以將內(nèi)存中的數(shù)據(jù)輸出到控制臺(tái),或者說(shuō)輸出到顯示器中,所以這個(gè)時(shí)候,我們一定要做一個(gè)系統(tǒng)調(diào)用,讓這個(gè)printf()跑到內(nèi)核態(tài)中去執(zhí)行.這個(gè)時(shí)候,也就是調(diào)用了操作系統(tǒng)的一個(gè)系統(tǒng)方法,或者說(shuō)叫內(nèi)部接口來(lái)和硬件交互.
我們首先使用gcc對(duì)這段代碼進(jìn)行編譯,然后使用strace工具對(duì)代碼進(jìn)行跟蹤.
?這個(gè)write()函數(shù)就是glibc封裝的系統(tǒng)函數(shù)write(),也就是這個(gè)printf()函數(shù)在內(nèi)部調(diào)用的系統(tǒng)函數(shù)write().
既然printf()調(diào)用的是write()函數(shù),那么我們其實(shí)就可以直接將printf()函數(shù)替換為write()函數(shù)
#include<unistd.h>
int main()
{char msg[]="hello world\n";write(STDOUT_FILENO,msg,sizeof(msg)-1);return 0;
}
我們?cè)俅螌?duì)程序進(jìn)行編譯,并且使用gdb跟蹤調(diào)試.?
?
- ?我們首先在write處打斷點(diǎn)
- 然后run單步運(yùn)行
- 最后進(jìn)行反匯編?
確認(rèn)了在write()函數(shù)的系統(tǒng)調(diào)用中,是通過(guò)syscall指令來(lái)將用戶態(tài)陷入到了內(nèi)核態(tài).
接下來(lái)我們來(lái)看看用戶態(tài)是如何切換到內(nèi)核態(tài)的.?
1.將參數(shù)保存到寄存器中
這里也就是printf()的參數(shù),或者說(shuō),在printf()內(nèi)部調(diào)用的系統(tǒng)函數(shù)write()的參數(shù)
2.根據(jù)系統(tǒng)調(diào)用名稱(也就是write()方法名)找到它的系統(tǒng)調(diào)用號(hào).
這個(gè)系統(tǒng)調(diào)用號(hào)在哪里找呢?有一張系統(tǒng)調(diào)用映射表,這個(gè)映射表不僅在內(nèi)核中維護(hù)了這樣一張表,在glibc的庫(kù)函數(shù)中,也維護(hù)了這樣一張表,因此,我們就能夠找到write()方法的系統(tǒng)調(diào)用號(hào).
內(nèi)核態(tài)和用戶態(tài)之間通信就是通過(guò)系統(tǒng)調(diào)用號(hào)來(lái)進(jìn)行的.
3.通過(guò)匯編指令syscall將用戶態(tài)陷入到內(nèi)核態(tài),通過(guò)調(diào)用系統(tǒng)調(diào)用號(hào)對(duì)應(yīng)的系統(tǒng)方法以及相關(guān)寄存器,來(lái)完成指令.
概括起來(lái)就是說(shuō),從用戶態(tài)切換到內(nèi)核態(tài),就是用戶態(tài)的應(yīng)用程序要向內(nèi)核態(tài)去申請(qǐng)外部資源,這個(gè)外部資源說(shuō)通俗點(diǎn)也就是只有內(nèi)核態(tài)才有權(quán)限執(zhí)行的命令,就是外部資源.
說(shuō)的更直白一點(diǎn),就是當(dāng)我們拆開(kāi)一臺(tái)服務(wù)器或者筆記本,肉眼可見(jiàn)的都屬于外部資源,包括CPU,N=內(nèi)存,網(wǎng)卡,硬盤(pán),USB接口等等,都屬于外部資源?
而系統(tǒng)調(diào)用,就是我們今天講的syscall,就是最常見(jiàn)的陷入方式.
系統(tǒng)調(diào)用還有其它方式,分為5類(lèi)
- 進(jìn)程? exit? fork
- 文件? chomd? chown? open
- 設(shè)備? read? ?write
- 信息? getXXX? setXXX
- 通信? mmap? ?sendfile
我們可以通過(guò)man syscalls命令來(lái)查看具體的系統(tǒng)調(diào)用
man syscalls