🎁個人主頁:工藤新一1
🔍系列專欄:C++面向對象(類和對象篇)
🌟心中的天空之城,終會照亮我前方的路
🎉歡迎大家點贊👍評論📝收藏?文章
文章目錄
- 進程狀態
- 一、Linux內核源代碼
- 1.1運行狀態
- 1.2阻塞狀態
- 1.3進程的掛起
- 1.4理解Linux內核鏈表話題
- 二、Linux的進程狀態
- 2.1運行狀態(R/R+)
- 2.2阻塞狀態(S)
- 2.3暫停狀態(t && T)
- 2.4 D狀態(深度睡眠、不可中斷睡眠)
- 2.5死亡狀態(結束狀態)
- 三、補充
- 3.1內存泄漏問題
- 3.2關于內核結構申請(slab 復用機制)
- 總結
進程狀態
前提回顧:進程概念
? 進程狀態指的是一個進程在其生命周期中所處的階段
? 對于進程來講,進程狀態用于決定,當前進程要被調渡?運行?正在休眠?正在進行等待?在系統層面,當前進程狀態決定 OS 應該如何處理這個進程
進程狀態本質:task_struct
內部的一個整形變量(整數),后續 OS 會根據這個 “整數”(標志位) 進行判斷
一、Linux內核源代碼
- 運行 && 阻塞 && 掛起
1.1運行狀態
? 一個進程在 CPU 上運行時,其就是運行狀態。在當代的計算機中,只要一個進程在調渡隊列當中,我們就稱該進程為運行狀態 [并不是一個進程持有 CPU,才能叫做運行]
運行:進程在調渡隊列中,進程的狀態都是 running!
處于 running 狀態的進程,要么正在被 CPU運行,要么已經完全準備完畢,隨時等待被調渡
OS
運行隊列(Run Queue):
1.2阻塞狀態
? 在學習變成語言中,我們會遭遇的阻塞經歷:cin/scanf
。**傳統意義上:**我們自己寫的 C++ 代碼,編譯起來就是一個進程,當代碼從上至下執行至 cin/scanf
我們的程序就停下來了[等待用戶輸入],輸入數據后,當程序拿到了這份數據后,程序就會繼續向后運行了
? 現代計算機理解中:當我們 cin/scanf
時,其實程序并不是在等待用戶輸入,而是等待鍵盤硬件就緒。在用戶未按下鍵盤時,我們稱之為鍵盤硬件未就緒,此時 cin/scanf
也就無法讀取數據。因此,阻塞的意義:等待某種設備/資源就緒。在此期間,設備/資源未就緒,那么進程就不會被調渡
OS
設備隊列(struct_device device):*
運行:進程在調渡隊列中你進程的狀態都是 running
阻塞:等待某種設備或資源就緒 [OS 會管理系統中的各種硬件資源:“先描述,在組織!”]
結論:進程狀態的變化,表現之一是,要在不同的隊列中進行流動。本質是數據結構的增刪查改!
1.3進程的掛起
1、阻塞掛起狀態
問題:如果在阻塞掛起狀態,鍵盤突然就緒了呢?
因為OS是設備(軟硬件設備)的管理者,所以OS會將對應進程,曾經換入到磁盤中的歷史上的數據和代碼,重新加載到內存,重新構建指針映射,最后將這些代碼和數據換入內存后,形成完整進程,最后將進程放入運行隊列中 —— 這一過程,我們稱之為 swap 對應的 “換入和喚出” 操作
其中,
進程所對應的代碼數據被放進交換分區:進程阻塞掛起
當進程換入時,將阻塞掛起進程重新改為運行狀態,該進程就可以直接繼續被調渡
掛起:將對應進程的代碼和數據掛到外設(磁盤)上
換句話說,OS是一個非常智能的軟件,當內存資源不足時,OS需要在整個系統層面上對內存資源進行 “輾轉騰挪”
2、運行掛起狀態
OS內存資源,“相當
” 吃緊,阻塞的進程資源全部掛起到外設上,內存仍然短缺,那么 OS只能打 運行隊列 中進程的主意了。OS 甚至會將處于運行隊列末端的進程,交換給 swap 中,當真正真正調渡該進程時,再把對應進程換入
掛起的本質:暫時中止進程/線程的執行,將進程數據換入/喚出到磁盤的 swap 分區中,并保存其當前狀態[PCB],以便后續恢復
1.4理解Linux內核鏈表話題
如何遍歷整個進程?
數據結構中的雙鏈表:
Linux 內核[kernel]中的雙鏈表結構:
task_struct
內部嵌套鏈表:
鏈表差異:
C 語言地址轉化(宏 - offset):獲取結構體中所有字段地址
**最終結論:**以 offset
宏 遍歷鏈表,即可以訪問進程(PCB)中的所有結構體中的所有字段
**結論:我們所對應的PCB在kernel中只存在一份,但同一個PCB可以同時映射于多種數據結構中。 **如此獨特的性能歸結于 kernel 中獨有的雙鏈表結構[list_head],以 list_head
為中心設計獨有的算法
kernel 中的數據結構不是單一結構,如進程[PCB]可能即屬于運行隊列,又屬于全局鏈表;還可能即被放在運行隊列中,又被放在等待隊列中,這意味著:Linux中很多數據結構是網狀的!
二、Linux的進程狀態
2.1運行狀態(R/R+)
“R - 0”
2.2阻塞狀態(S)
“S - 1”
- Linux角度 - 休眠狀態
- 操作系統角度 - 阻塞狀態(等待某種資源準備就緒:等待鍵盤輸入)
在《操作系統》理論中,阻塞狀態被稱為:阻塞。但在 Linux 內核中阻塞狀態我們稱之為 S
我們在用戶層直觀的看到一個程序卡住不動了,說到底就是進程不被調渡了(有可能是在等待硬件設備的輸入),因此這就是阻塞狀態
在操作系統理論課程中,我們無從得知阻塞的標志位具體是什么的(0?1?…),但具體操作系統可以告訴我們 Linux的阻塞狀態是 ”S - 1“
2.3暫停狀態(t && T)
t - 追蹤狀態
在先前我們的操作系統理論中并沒有暫停狀態;但在 Linux 當中是存在暫停狀態的
T - 暫停狀態
- T (Stopped):進程的執行被暫停,通常是由于收到了一個信號(例如
SIGSTOP
信號或調試器的斷點信號) - 可以被重新喚醒,繼續運行(例如通過發送
SIGCONT
信號)
什么是暫停狀態?暫停狀態又是什么呢?暫停狀態又如何與 kernel狀態對應起來呢?
一般暫停狀態[T]與阻塞狀態[S]不一樣(**進程阻塞S:**一個進程在等待某種資源;**進程暫停T:**不具備某種條件或進程做了非法操作,那么操作系統就將對應的進程暫停了,這屬于Linux特有的一種狀態)
- 暫停狀態(t && T):止損;操作系統懷疑進程有問題,將進程暫停[t - 斷點調試;T - Ctrl + Z]交給用戶,讓用戶裁決是否繼續進程
2.4 D狀態(深度睡眠、不可中斷睡眠)
進程在等待某個事件(如等待輸入輸出完成、等待信號量、等待網絡數據等)而暫停運行。睡眠態分為兩種:
- 可中斷睡眠態
- S (Interruptible Sleep):進程在等待一個事件的完成。在這種狀態下,進程可以被信號喚醒(例如用戶按了 Ctrl+C)或中斷
- 常見例子:等待用戶鍵盤輸入、等待套接字連接
- 不可中斷睡眠態
- D (Uninterruptible Sleep):進程也在等待事件(通常是I/O操作),但不能被信號喚醒或中斷。這是為了保護某些關鍵的進程,確保它們在完成特定任務前不會被打斷
- 常見例子:等待磁盤I/O操作。如果系統上有大量
D
狀態的進程,通常意味著硬件(如磁盤)可能遇到了問題
休眠狀態S,我們稱之為可 中斷睡眠(也叫淺度睡眠)[Linux中具體個性化的概念]。可終端休眠:如果一個進程處于 S狀態,我們直接殺掉這個進程,這個進程會 響應 我們殺掉它的動作[給出反應]
”D“ 狀態的進程不可被 kill掉!那什么時候 ”D“狀態就結束了呢?:一旦進程處于 ”D“狀態,那就只能等待該進程自己醒來,OS 無權殺掉它!;或者重啟,甚至只能斷電!
2.5死亡狀態(結束狀態)
- X (Dead):進程已經完全死亡并被回收。這個狀態只是一個瞬間狀態,用戶無法在
ps
或top
等工具中看到這個狀態的進程。(注意區分 僵尸狀態 與 死亡狀態 的時間階段:(先)僵尸狀態—>(再)死亡狀態[此時,OS瞬間將其釋放掉了 - 造就了無法觀測的情況])
三、補充
3.1內存泄漏問題
問題:進程結束,內存泄漏問題是否還存在?
如果進程直接退出了,曾經 new/malloc
的堆空間,會被系統自動回收(即內存泄漏問題是不存在的)
而僵尸進程所占用的資源空間基于系統級,new/malloc
只是基于編程語言層面上的資源空間[但本身依舊是向 OS申請的]
常駐進程:常駐扎在內存中的進程
我們所用的大部分軟件都是啟動后,不退出的,即死循環(while(1);
);這就是常駐進程;OS啟動后本身就是一個常駐內存的軟件,所以其內部一旦有內存泄漏問題會影響甚大
3.2關于內核結構申請(slab 復用機制)
總結
狀態 | 符號 | 含義 | 是否消耗資源 |
---|---|---|---|
運行/可運行 | R | 正在或即將使用CPU | 是 |
可中斷睡眠 | S | 等待事件,可被信號喚醒 | 否(等待中) |
不可中斷睡眠 | D | 等待I/O,不可被信號喚醒 | 否(等待中) |
僵尸 | Z | 已終止,等待父進程回收 | 否(但占用PID) |
停止 | T | 被信號暫停執行 | 否 |
到此,我們體會到了:《操作系統》中的理論狀態與具體的《Linux》的理論狀態在概念上是一致的;在實踐上是有個性化的一面的
《操作系統》這門學科將所有 OS 的屬性/共性抽取出來,從而提煉了一套方法論,我們用這一套方法論就可以理解所有的 OS
但沒有實踐,不了解具體的 OS,就無法理解抽象的《操作系統》學科,更不可能將知識遷移到其他的個性化的 OS上(Linux),所以我們在學校中學習的《操作系統》叫做:“計算機學科的哲學”
🌟 各位看官好,我是工藤新一1呀~
🌈 愿各位心中所想,終有所致!