一鹅髓、用戶態(tài)舞竿、內(nèi)核態(tài)
內(nèi)核空間是共享的,存在整個(gè)內(nèi)核的代碼和所有的內(nèi)核模塊以及內(nèi)核所維護(hù)的數(shù)據(jù)迈勋。
進(jìn)程在運(yùn)行時(shí)一般會(huì)處于兩種狀態(tài):用戶態(tài)炬灭,內(nèi)核態(tài)。
用戶態(tài)是指進(jìn)程在用戶代碼中運(yùn)行靡菇。
內(nèi)核態(tài)是指進(jìn)程進(jìn)入內(nèi)核代碼重归,執(zhí)行內(nèi)核的代碼。
用戶態(tài):Ring3運(yùn)行于用戶態(tài)的代碼則要受到處理器的諸多檢查厦凤,它們只能訪問映射其地址空間的頁表項(xiàng)中規(guī)定的在用戶態(tài)下可訪問頁面的虛擬地址鼻吮,且只能對(duì)任務(wù)狀態(tài)段(TSS)中I/O許可位圖(I/O Permission Bitmap)中規(guī)定的可訪問端口進(jìn)行直接訪問。
內(nèi)核態(tài):Ring0在處理器的存儲(chǔ)保護(hù)中较鼓,核心態(tài)椎木,或者特權(quán)態(tài)(與之相對(duì)應(yīng)的是用戶態(tài)),是操作系統(tǒng)內(nèi)核所運(yùn)行的模式博烂。運(yùn)行在該模式的代碼香椎,可以無限制地對(duì)系統(tǒng)存儲(chǔ)、外部設(shè)備進(jìn)行訪問禽篱。
現(xiàn)在我們從特權(quán)級(jí)的調(diào)度來理解用戶態(tài)和內(nèi)核態(tài)就比較好理解了畜伐,當(dāng)程序運(yùn)行在3級(jí)特權(quán)級(jí)上時(shí),就可以稱之為運(yùn)行在用戶態(tài)躺率,因?yàn)檫@是最低特權(quán)級(jí)玛界,是普通的用戶進(jìn)程運(yùn)行的特權(quán)級(jí)万矾,大部分用戶直接面對(duì)的程序都是運(yùn)行在用戶態(tài);反之慎框,當(dāng)程序運(yùn)行在級(jí)特權(quán)級(jí)上時(shí)良狈,就可以稱之為運(yùn)行在內(nèi)核態(tài)。
雖然用戶態(tài)下和內(nèi)核態(tài)下工作的程序有很多差別笨枯,但最重要的差別就在于特權(quán)級(jí)的不同薪丁,即權(quán)力的不同。運(yùn)行在用戶態(tài)下的程序不能直接訪問操作系統(tǒng)內(nèi)核數(shù)據(jù)結(jié)構(gòu)和程序猎醇。
當(dāng)我們?cè)谙到y(tǒng)中執(zhí)行一個(gè)程序時(shí)窥突,大部分時(shí)間是運(yùn)行在用戶態(tài)下的,在其需要操作系統(tǒng)幫助完成某些它沒有權(quán)力和能力完成的工作時(shí)就會(huì)切換到內(nèi)核態(tài)硫嘶。
從用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)換情況一般有以下三種:
?(1)發(fā)生系統(tǒng)調(diào)用
(2)CPU執(zhí)行異常
(3)外圍設(shè)備發(fā)來中斷請(qǐng)求
進(jìn)程切換,是指CPU執(zhí)行一個(gè)進(jìn)程A梧税,進(jìn)程A可能因?yàn)槟承┰虮蛔枞ū热缏偌玻却M(jìn)程B給它發(fā)送數(shù)據(jù)),那么CPU會(huì)被其他處于就緒狀態(tài)的進(jìn)程C搶占CPU第队,CPU執(zhí)行進(jìn)程C的相關(guān)指令哮塞。 CPU執(zhí)行進(jìn)程A的指令到執(zhí)行進(jìn)程C指令的過程就是進(jìn)程切換。
??? 在由進(jìn)程A切換到進(jìn)程C時(shí):
(1)內(nèi)核首先將進(jìn)程A的狀態(tài)以及在CPU寄存器存儲(chǔ)的數(shù)據(jù)狀態(tài)指令等凳谦,保存在進(jìn)程A描述符指向的內(nèi)存區(qū)域中忆畅。
?(2)內(nèi)核然后將進(jìn)程C描述符指向的相應(yīng)狀態(tài)數(shù)據(jù),裝載到CPU的相應(yīng)寄存器中尸执。完之后家凯,CPU開始執(zhí)行進(jìn)程B的相應(yīng)指令。
?因?yàn)檫M(jìn)程是資源分配的基本單位如失, 因此進(jìn)程之間切換時(shí)绊诲,需要保存、裝載各種狀態(tài)數(shù)據(jù)等資源褪贵, 所需的代價(jià)較高掂之。
?線程是CPU調(diào)度的基本單位,同一個(gè)進(jìn)程內(nèi)的線程共享OS給該進(jìn)程分配的資源脆丁,因此線程切換比進(jìn)程間切換所需的代價(jià)較小世舰。
二、內(nèi)核線程
我們知道槽卫,在 Linux 中跟压,用戶態(tài)進(jìn)程的“祖先”,都是 PID 號(hào)為 1 的 init 進(jìn)程晒夹。比如裆馒,現(xiàn)在主流的 Linux 發(fā)行版中姊氓,init 都是 systemd 進(jìn)程;而其他的用戶態(tài)進(jìn)程喷好,會(huì)通過 systemd 來進(jìn)行管理翔横。
稍微想一下 Linux 中的各種進(jìn)程,除了用戶態(tài)進(jìn)程外梗搅,還有大量的內(nèi)核態(tài)線程禾唁。按說內(nèi)核態(tài)的線程,應(yīng)該先于用戶態(tài)進(jìn)程啟動(dòng)无切,可是 systemd 只管理用戶態(tài)進(jìn)程荡短。那么,內(nèi)核態(tài)線程又是誰來管理的呢哆键?
實(shí)際上掘托,Linux 在啟動(dòng)過程中,有三個(gè)特殊的進(jìn)程籍嘹,也就是 PID 號(hào)最小的三個(gè)進(jìn)程闪盔。
要查找內(nèi)核線程,我們只需要從 2 號(hào)進(jìn)程開始辱士,查找它的子孫進(jìn)程即可泪掀。比如,你可以使用 ps 命令颂碘,來查找 kthreadd 的子進(jìn)程:
要查找內(nèi)核線程异赫,我們只需要從 2 號(hào)進(jìn)程開始,查找它的子孫進(jìn)程即可头岔。
比如塔拳,你可以使用 ps 命令,來查找 kthreadd 的子進(jìn)程:
# ps -f --ppid 2 -p 2
從上面的輸出切油,你能夠看到蝙斜,內(nèi)核線程的名稱(CMD)都在中括號(hào)里,所以澎胡,更簡(jiǎn)單的方法孕荠,就是直接查找名稱包含中括號(hào)的進(jìn)程。
# ps -ef | grep "\[.*\]"
了解內(nèi)核線程的基本功能攻谁,對(duì)我們排查問題有非常大的幫助稚伍。比如,我們?cè)?jīng)在軟中斷案例中提到過 ksoftirqd戚宦。它是一個(gè)用來處理軟中斷的內(nèi)核線程个曙,并且每個(gè) CPU 上都有一個(gè)。
如果你知道了這一點(diǎn),那么垦搬,以后遇到 ksoftirqd 的 CPU 使用高的情況呼寸,就會(huì)首先懷疑是軟中斷的問題,然后從軟中斷的角度來進(jìn)一步分析猴贰。
三对雪、參考
Linux中的程序和進(jìn)程,PID和PPID
https://qastack.cn/unix/82724/ps-switches-to-display-pid-ppid-pgid-and-sid-collectively
'ps' arguments to display PID, PPID, PGID, and SID collectively
https://unix.stackexchange.com/questions/82724/ps-arguments-to-display-pid-ppid-pgid-and-sid-collectively