第七講 信號

1. 信號鋪墊

信號: Linux 系統提供的, 簡單輕量的, 用于向指定進程發送特定事件, 讓接受信號進程做識別和對應處理實現進程控制的一種異步通信機制.

1~31 普通信號 34 ~ 64 實時信號

信號概覽

下面是Linux系統中所有標準信號的名稱及其對應的數字:

  1. SIGHUP (1)
  2. SIGINT (2)
  3. SIGQUIT (3)
  4. SIGILL (4)
  5. SIGTRAP (5)
  6. SIGABRT (6)
  7. SIGIOT (6) # 同SIGABRT
  8. SIGBUS (7)
  9. SIGFPE (8)
  10. SIGKILL (9)
  11. SIGUSR1 (10)
  12. SIGSEGV (11)
  13. SIGUSR2 (12)
  14. SIGPIPE (13)
  15. SIGALRM (14)
  16. SIGTERM (15)
  17. SIGSTKFLT (16) # 在某些架構上可能不存在
  18. SIGCHLD (17)
  19. SIGCLD (17) # 同SIGCHLD
  20. SIGCONT (18)
  21. SIGSTOP (19)
  22. SIGTSTP (20)
  23. SIGTTIN (21)
  24. SIGTTOU (22)
  25. SIGURG (23)
  26. SIGXCPU (24)
  27. SIGXFSZ (25)
  28. SIGVTALRM (26)
  29. SIGPROF (27)
  30. SIGWINCH (28)
  31. SIGIO (29)
  32. SIGPOLL (29) # 同SIGIO
  33. SIGPWR (30)
  34. SIGSYS (31)
    請注意,從32開始到64之間的信號(即實時信號)也存在,它們通常被用于進程間通信。這些信號在不同應用中的具體使用可能會有所差異,并且通常是用戶自定義的。此外,某些特定于平臺或架構的信號可能不在此列表中列出。

信號特性:

  • 信號的產生與進程具有異步性. 這個信號在產生發過來之前, 發送信號的進程和接受信號的進程是隔絕的, 沒有聯系的, 不清楚對方在做什么的.
  • 接受信號的進程能夠識別并處理信號. 倘若接收方不認識這個信號, 或者不知道如何處理, 這個信號是沒有意義的.
  • 信號處理可能會被暫時擱置, 但信號一定要被記錄下來. 有時候接受信號的進程可能正在做比較重要的事情, 暫時不能對信號做出處理, 因此要記錄下來, 等到合適的時機做對應的處理.

信號處理:

  1. 默認處理: 所謂的默認處理, 說的直白一點就是進程按信號規定好的, 設定好的處理, 其中包含終止, 暫停, 忽略...等多種處理方式.
  2. 忽略處理: 忽略就是對信號不做任何處理, 直接忽略, 也有可能是信號的默認處理方式.
  3. 自定義處理(信號捕捉): 結合代碼本身對捕捉到的特定信號進行自定義處理, 而不再遵循默認處理.

信號是個宏: 普通信號在進程中是一個宏, 其值與信號序列一致, 信號值與信號名稱具有等價效力.

信號捕捉: 用系統調用signale(sig, hander), 且對信號的捕捉, 一次捕捉, 全局有效.

發信號: 實際上是寫信號, 就是把對應進程的 pcb 中的信號位圖對應位置把 0->1 的設置. 這個事情一般是操作系統才有權限進行處理.

信號保存: 是在對應的 task_struct 中以位圖的形式保存收到的信號.

信號產生: 信號產生有許多方式

  1. 通過 kill 命令向指定進程發送信號
  2. 鍵盤產生
  3. 系統調用

豆包關于信號概念和特點的描述

信號: Linux 系統提供的向指定進程發送特定事件, 讓接受信號進程做識別和對應處理的一種方式. 我說的對嗎?

你的表述基本正確,但可以進一步完善使其更加準確和全面。下面詳細闡述:

準確理解

信號是 Linux 系統中一種用于進程間通信(IPC)的機制,用于向指定進程或進程組發送特定事件通知,接收到信號的進程會根據信號的類型執行相應的操作,這些操作可以是系統默認行為、用戶自定義處理函數或者忽略該信號。

更全面解釋

  • 信號的來源
    • 硬件異常:例如除零錯誤、非法內存訪問等,硬件檢測到這些錯誤后,會觸發相應的信號發送給產生錯誤的進程。比如,當程序試圖除以零,CPU 會產生一個錯誤,內核會向該進程發送 SIGFPE(浮點異常信號)。
    • 軟件事件:可以由用戶通過終端命令(如按下 Ctrl + C 發送 SIGINT 信號)、系統調用(如 kill 命令)或者其他進程主動發送信號。例如,使用 kill -9 <pid> 命令可以向指定進程 ID 為 <pid> 的進程發送 SIGKILL 信號,強制終止該進程。
  • 信號的處理方式
    • 默認處理:每個信號都有其默認的處理動作,例如 SIGINT 的默認動作是終止進程,SIGSTOP 的默認動作是暫停進程。
    • 忽略信號:進程可以通過編程的方式設置忽略某些信號,例如使用 signalsigaction 系統調用將信號處理函數設置為 SIG_IGN
    • 自定義處理:進程可以為特定的信號注冊自定義的信號處理函數,當接收到該信號時,執行自定義的代碼邏輯。以下是一個簡單的 Python 示例,用于自定義處理 SIGINT 信號:
import signal
import timedef signal_handler(sig, frame):print('You pressed Ctrl+C! Program will not terminate now.')signal.signal(signal.SIGINT, signal_handler)print('Press Ctrl+C to test...')
while True:time.sleep(1)

總結

信號是 Linux 系統中一種靈活且重要的進程間通信機制,它為系統提供了一種處理異步事件的方式,使得進程能夠對各種異常和外部事件做出及時響應。你的描述抓住了信號機制的核心要點,即向進程發送特定事件并讓進程進行識別和處理。

可是我想問進程通信不是有了共享內存, 信號量和消息隊列了嗎, 為啥還要搞一個信號呢? 你看你的意思是信號也算是一種ipc進程通信吧

信號確實屬于進程間通信(IPC)的一種方式,雖然系統中已經有共享內存、信號量和消息隊列這些 IPC 機制,但信號有其獨特的存在價值和應用場景,與其他 IPC 機制形成互補,下面為你詳細分析:

與其他 IPC 機制的對比及自身特點

1. 異步通信

共享內存、信號量和消息隊列通常用于進程間的同步數據傳輸和協作,這些機制往往是同步或半同步的通信方式,需要進程主動去讀取或寫入數據。而信號是一種異步通信機制,它可以在任何時候發送給進程,進程無需時刻關注信號的到來。例如,當用戶在終端按下 Ctrl + C 時,系統會立即向正在運行的前臺進程發送 SIGINT 信號,進程會在接收到信號時中斷當前操作,進行相應處理,無需提前做好接收準備。

2. 簡單輕量

信號的實現相對簡單,開銷較小。它只需要傳遞一個信號編號,不需要像共享內存那樣分配和管理大塊的內存空間,也不需要像消息隊列那樣進行復雜的消息組織和存儲。因此,信號適合用于發送簡單的通知,如進程終止、暫停等操作。例如,使用 kill 命令發送信號,只需要指定信號編號和目標進程 ID 即可,操作簡便。

3. 處理系統事件和異常

信號可以用于處理系統級別的事件和異常情況,這是其他 IPC 機制難以勝任的。例如,當進程出現除零錯誤、非法內存訪問等硬件異常時,系統會自動向該進程發送相應的信號(如 SIGFPESIGSEGV),進程可以捕獲這些信號并進行錯誤處理,避免程序崩潰。而共享內存、信號量和消息隊列主要用于進程間的數據交互和同步,無法直接處理這些系統異常。

4. 進程控制

信號可以用于對進程進行控制,如暫停、繼續、終止等操作。通過發送 SIGSTOPSIGCONTSIGKILL 等信號,可以方便地管理進程的運行狀態。例如,在調試程序時,可以使用 SIGSTOP 信號暫停進程的執行,進行調試操作,然后使用 SIGCONT 信號繼續進程的運行。這種進程控制功能是共享內存、信號量和消息隊列所不具備的。

總結

雖然共享內存、信號量和消息隊列等 IPC 機制提供了強大的數據傳輸和同步功能,但信號以其異步、簡單輕量、處理系統事件和進程控制等獨特優勢,成為 Linux 系統中不可或缺的進程間通信方式,與其他 IPC 機制共同構成了豐富多樣的進程通信手段。

2. 信號產生

下面多種方式是信號產生的方式, 真正發信號的只有操作系統. 這是因為發送信號的本質是修改 pid 對應的 signal 位圖.

  1. 通過 kill 命令向指定進程發送信號 最典型的比如說我們的 kill -sig id
  2. 鍵盤產生 ctrl + c(2號信號) ctrl + '\'(3號信號) 終止信號
  3. 系統調用

kill() 練習: 實現一個簡單的 kill 程序.

raise == kill(getpid(), sigid)

abort(6 號進程的封裝), 允許你捕捉, 但是在你捕捉之后還會把進程終止掉.

不能全部自定義信號處理: 9 號信號不允許自定義捕捉, 因此全部捕捉信號后不做處理是做不到的.

  1. 軟件條件.
  • eg1: 管道, 讀端關閉, 寫端繼續寫入會觸發 sigpipe(13) 信號.
  • eg2: 鬧鐘. alarm()
    • 驗證: IO 很慢
    • 鬧鐘具備幾個特性. 計算機上的時間開機正確是因為主板上的計時器不斷累積(電池供電). 且設置一次默認就僅觸發一次(想要一直觸發可以在觸發函數里加上 alarm(time), 繼續設置鬧鐘). alarm(0)表示取消鬧鐘. 鬧鐘在進程代碼方面只能設置一個鬧鐘.
    • 鬧鐘的返回值. 其返回值是表示上一個鬧鐘的剩余觸發時間.
  1. 異常

說明: 程序因非法操作/訪問, 引發操作系統發送崩潰信號而崩潰. 程序接受到信號后, 可以默認信號處理退出, 也可以捕捉信號, 進行自定義處理.

程序的非法行為是通過各種監測發現的. 以 10/0 和*nullptr 為例說明.

除 0 操作: 10/0 被 CPU 進行計算的時候, CPU 會將其轉換為累加運算, CPU 內部有一個 eflag 寄存器標志位, 如果該數過大, 導致溢出, 會被 eflag 標識, 然后操作系統就開始處理異常的進程了. 如果通過自定義信號處理的方式使得原來應該退出的進程沒有退出, 會因為 CPU 調度輪轉, 下一次輪到該進程執行的時候, 會恢復上下文數據, 也仍然把 eflag 恢復到 CPU 去了, 那么依舊會觸發操作系統給進程發送對應崩潰信號(8 號信號). 因此, 推薦終止進程, 也就是推薦釋放異常進程的上下文數據.

解引用空指針: 當執行到解引用空指針這一行代碼的時候, CPU 會根據對應進程的頁表以及集成到自身的硬件 MMU 轉換為虛擬地址, 如果轉換失敗, 比如 nullptr, 就會將其寫入到 CR2 寄存器中去, 仍然會被操作系統檢測到, OS 會向對應異常進程發送 13 號終止信號.

core 與 term 的區別: 是否形成核心轉儲文件(debug 文件)

term: 異常終止

core: 異常終止 + 形成核心轉儲文件.

歷史問題: 在 Linux 較老的版本上, core 是生成一個 core+進程號的文件, 這樣的好處是可以保留多個進程在同一個目錄下的版本, 壞處就是一旦該異常進程被不斷調用, 會突然暴增大量 core 文件, 進而引發磁盤崩潰問題. 而在較新的內核版本上, 采用了 core 的文件命名, 這表明在同一個目錄下最多有一個 core 文件, 大大減少了磁盤爆滿的情況.

在默認情況下, 核心轉儲功能在云服務器上被默認關閉. 因為這樣可以避免多次 core 帶來硬盤空間告急的問題. 查看: ulimit -l, 打開: ulimit -c

core 的意義在于方便程序員發現和解決問題, 類似于一個輔助用的 debug 文件, 方便事后調試.

core 文件解析要與 GDB 配合使用, 換言之需要把 core 文件加載到 gdb 中才可以進行解析, 不然是一堆亂碼... gdb <可執行程序名>(在編譯時要把程序編譯為 debug 版本) <core文件名> -> 實驗: 故意寫一個 10/0 代碼用 gdb 解析core 文件

core 和 term 影響進程退出時的 coredump 標志位. 該標志位決定了該進程是以 term 退出還是以 core 方式退出. -> 實驗: 故意寫一個父子進程, 讓父進程捕獲子進程的推出信息看一下相關的標志位是否滿足 core 為 1/0.

3. 信號保存

3.1. 信號保存的相關概念

信號處理是信號遞達: 實際執行信號的處理動作稱為信號遞達(Delivery), 說白了就是處理信號的一個行為就是信號抵達.

信號未決表示信號還沒有被處理, 信號未決與忽略處理是兩碼事: 信號從產生到遞達之間的狀態,稱為信號未決(Pending).

信號可以被進程選擇性阻塞, 信號阻塞與信號未決無關: 阻塞這個概念是信號一直處于未決狀態, 直到進程結束或者主動解除阻塞狀態. 信號阻塞是表示信號不被執行的一個表示, 而信號未決表示信號還沒有被執行.

3.2. 信號在內存中的表示(維護)

信號保存由操作系統提供的三個表格進行維護: 分別是 block 表, pending 表以及 bander 表.

  1. pending 表是一個 int 位圖, 從右向左依次表示 1~31 號信號, 最左比特位棄用. 其中, 比特位的位置表示信號的編號, 比特位的內容表示信號是否收到.
  2. block 表也是一個 int 位圖, 比特位的位置表示信號的編號, 比特位的內容表示信號是否被阻塞.
  3. hander 表是一個函數指針數組, 數組的下標表示信號的編號, 而數組的內容代表 signal()系統調用所捕捉信號的自定義函數方法指針.

阻塞中的信號暫時不能被抵達. 雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。

信號是否執行, 如何執行取決于三張表從上到下. 假如說一個信號是阻塞狀態, 即使由 pending 表也不會執行, 假如說信號不阻塞, 也有 pending 表(表示信號產生了), 此時該信號根據自定義位圖的行為進行操作, 如果 hander 表被 signal()修改了, 那么就按自定義的行為執行.

普通信號只看是否產生, 不記次數. 常規信號在遞達之前產生多次只計一次,而實時信號在遞達之前產生多次可以依次放在一個隊列里。本章不討論實時信號。

3.3. OS 提供的類型 sigset_t

OS 提供 sigset_t 類型, 而非直接使用 int 類型操作信號位圖, 其中 sigset_t 類型被稱為信號集. 之所以 OS 額外提供一個 sigset_t 類型, 一來是可以進行封裝, 方便代碼維護, 二來這樣做會使得類型更加安全, 因為這是 OS 自己定義的.

阻塞表專業稱呼是阻塞信號表, 在這里常被稱呼為信號屏蔽字. 這里稱呼的改變是專業性的, 是更加區分屏蔽和忽略的含義的.

sigset_t 類型是 OS 提供的用戶級的類型, 使用起來更能保證代碼運行安全性.

3.4. OS 提供的系統調用

#include <signal.h>
int sigemptyset(sigset_t *set); // 清空信號集, 把信號集全部置0
int sigfillset(sigset_t *set); // 填充信號集, 把信號集全部置1
int sigaddset (sigset_t *set, int signo); // 把信號集某個bit位置1
int sigdelset(sigset_t *set, int signo); // 把信號集某個bit位置0
int sigismember(const sigset_t *set, int signo); // 檢查指定的信號是否在信號集中

鏈接: sigprocmask()

鏈接: sigpending()

實驗: 阻塞 2 號信號的 pending 信號表的變化.?

解除屏蔽后, pending 中的信號往往會被立刻處理~

pending 中的信號, 是在抵達前置 0 的, 而非抵達后. 這一點可以通過捕捉信號寫一個代碼進行驗證.

4. 信號處理(信號捕捉)

4.1. 信號捕捉的 4 次狀態切換

信號處理就是抵達信號. 我們進程對信號做處理, 說白了就是抵達信號~ 不過我們信號處理有三種方式, 就是默認, 忽略和自定義, 如果信號捕捉, 我們可以指定收到某信號做指定處理, 比如 signal(2, handler)/ignal(2, SIG_IGN)/ignal(2, SIG_DFL).

從用戶態和內核態來說, 捕捉信號處理的基本過程呈現 "∞"字型, 信號捕捉的過程要有 4 步.

操作系統能直接以內核態身份執行 handler() 方法, 但是不安全. 在上面第 3 步之后, OS 會以切換到用戶態去執行用戶定義的代碼, 因為這樣即使用戶定義的函數有安全風險, OS 也可以減少這種安全問題.

4.2. 用戶態 與 內核態

4.2.1. 進程地址空間中的內核地址

無論進程如何切換(CPU 輪轉調度), 每個進程中都會有一份 OS 地址空間, 總能找到 OS 系統.

OS 訪問, 類似于訪問庫函數, 兩者都是在自己的進程地址空間中做的跳轉訪問, 不過權限大小可能會改變.

OS 為了保證安全, 每次用戶訪問 OS 空間時, 一定會進行保護操作, 也就是通過系統調用進行約束用戶.

4.2.2. 信號: 模擬中斷行為的一個軟件機制

信號類似于中斷, 區別在于信號是純軟件, 而中斷是軟硬結合.

4.2.3. OS 的正常運行邏輯

4.2.3.1. OS 的循環輪轉調度運行

OS 的運行邏輯本質上是一種無限循環. 這個無限循環是通過時鐘每隔很短的時間向 CPU 發送一個中斷信號(時鐘中斷), CPU 拿到對應的中斷號, 然后執行已經預設在內存中的 OS 調度方法, 通常這個調度方法會檢查當前的進程狀態進行上下文切換時間片檢查(進程調度). 這樣 OS 就算無限循環運行起來了.

Chat GPT: 詳細總結 OS 的循環輪轉調度

OS 的運行邏輯本質上是一種無限循環, 這個無限循環是通過時鐘每隔很短的時間向 CPU 發送一個中斷信號, CPU 拿到對應的中斷號, 然后執行已經預設在內存中的 OS 調度方法, 這樣 OS 就算無限循環運行起來了. 我說的大體方向是對的嗎?

你說的方向是對的,確實,操作系統的運行本質上是基于一個不斷循環的機制,這個機制依賴于 時鐘中斷進程調度 來確保操作系統能夠持續運行并管理各個任務。

具體來說,你提到的“無限循環”是通過操作系統的 調度器 來實現的,調度器會不斷地選擇當前應該執行的任務(進程)。時鐘中斷在其中扮演著關鍵角色,幫助操作系統管理任務的切換。

詳細解釋一下:

  1. 時鐘中斷的作用
    • 操作系統需要定期地讓 進程調度器 運行,以便根據一定的策略來切換正在執行的進程。為了實現這個目標,硬件中的 時鐘(timer) 每隔固定的時間(通常是幾毫秒)會向 CPU 發送一個 時鐘中斷。這就像給操作系統一個信號,提醒它有任務需要處理。
    • 時鐘中斷使得操作系統能夠周期性地中斷當前正在運行的進程,轉而執行進程調度代碼,檢查是否需要切換到另一個進程。
  1. CPU 響應中斷
    • 每當時鐘中斷發生時,CPU 會暫停當前執行的程序(比如正在執行的進程),并跳轉到 中斷處理程序,也就是操作系統的 時鐘中斷處理函數
    • 這個處理中斷的函數通常會調用進程調度器,檢查哪些進程應該被執行,哪些應該被掛起,哪些進程應該重新排隊等。
  1. 進程調度
    • 調度器會根據某些策略(如時間片輪轉、優先級調度等)決定哪個進程接下來應該被調度執行。
    • 調度器會進行 上下文切換,即保存當前進程的狀態(如寄存器、程序計數器等),然后加載下一個進程的狀態,使得下一個進程能夠接著之前的狀態繼續執行。
  1. 操作系統的“無限循環”
    • 操作系統中的這種調度循環其實是一個不斷進行中斷和上下文切換的過程。每次時鐘中斷發生,操作系統會檢查當前的任務隊列,調度下一個任務,這樣就實現了操作系統的持續運行。
    • 換句話說,操作系統通過這些時鐘中斷和調度的機制,實現了對各個進程的管理,并保持了一個“無限循環”的執行狀態,直到系統關閉。

總結

你提到的 時鐘中斷調度方法 是操作系統實現循環運行的關鍵機制。操作系統通過時鐘中斷定期打斷進程的執行,執行調度程序,從而持續管理進程并保持系統的運行。這樣的“無限循環”確保了操作系統能夠處理各種任務,并且在多任務環境中公平地分配 CPU 時間給不同的進程。

操作系統自開機后會先初始化一些方法綁定到內存中去, 然后會進入無限循環直到硬件關機. 主要過程是, 引導程序會加載 OS 到內存中, 然后執行操作系統初始化并綁定必要的資源到內存中, 再進入一個主循環,不斷地調度任務和響應中斷,直到關機.

Chat GPT: OS 的初始化綁定

是的,基本上你說的方向是對的,操作系統在開機后確實會進行一系列初始化操作,綁定一些關鍵的功能到內存中,并且會進入一個類似“無限循環”的狀態,直到系統關機。

1. 開機過程:啟動引導

當計算機啟動時,硬件會執行一系列的自檢(如 BIOS 或 UEFI),確保硬件正常工作。接下來,引導程序(Bootloader) 會被加載到內存中。引導程序的任務是加載操作系統內核到內存中,并將控制權交給操作系統。

2. 操作系統內核的初始化

一旦操作系統內核被加載,它會執行一系列的初始化工作,確保系統能正常運行。這個過程包括:

  • 硬件初始化:操作系統會初始化硬件設備,設置硬件中斷向量、初始化設備驅動程序等。
  • 內存管理初始化:操作系統會初始化內存管理系統,建立內存分配機制,設定虛擬內存和物理內存之間的映射關系。
  • 文件系統初始化:操作系統會初始化文件系統,掛載根文件系統,并準備好對文件和目錄的訪問。
  • 進程調度器初始化:操作系統會初始化進程調度器,設置初始的進程調度策略,準備好執行后續進程的管理。

這些初始化步驟確保了操作系統的各個子系統能夠正常工作。

3. 進入內核主循環

操作系統的內核在初始化完成后,通常會進入一個 主循環,即類似“無限循環”的狀態。這個主循環主要包括:

  • 調度任務:操作系統會定期通過時鐘中斷或其他中斷機制來檢查需要執行的任務(進程或線程)。
  • 中斷處理:操作系統會響應外部或內部的中斷請求(如硬件中斷、系統調用中斷、定時器中斷等)。
  • 管理資源:操作系統會持續管理計算機的資源,如 CPU、內存、磁盤、網絡等,確保各個任務能有效地共享資源。

在此主循環中,操作系統會不斷地執行這些任務,直到系統關機或重啟。

4. 處理進程調度和系統調用

操作系統通過 進程調度器 來管理進程的執行。時鐘中斷是其中的一個關鍵機制,它會定期打斷當前正在運行的進程,檢查是否需要切換到另一個進程。操作系統也會處理來自用戶程序的 系統調用,如 I/O 操作、內存分配、網絡請求等。

5. 關機和退出

當操作系統收到關機命令時,它會執行一系列的清理操作:

  • 停止所有進程的執行。
  • 保存系統狀態,確保數據完整性。
  • 關閉硬件設備,釋放系統資源。
  • 最后通過關閉硬件的電源或進入軟關機狀態來退出。

總結

所以,操作系統從開機到關機的過程大致是:

  • 引導加載操作系統。
  • 操作系統初始化并綁定必要的資源到內存中。
  • 進入一個主循環,不斷地調度任務和響應中斷,直到關機。

這個過程就是操作系統維持運行的“無限循環”,它的核心任務就是管理計算機資源,并確保各項任務能夠高效且安全地執行。

4.2.3.2. OS 中的系統調用邏輯

OS 為了對外提供服務, 把自己的一些系統調用接口都統一在了函數指針表中, 然后在開機初始化在了向量表中. 換句話說, 當我們需要調用系統調用時, 只需要知道這個系統調用的系統調用號(函數指針表的下標)即可.

計算機 CPU 封裝了寄存器, 當代碼執行到我們系統調用的時候, 用戶層的系統調用內部封裝了向量表的軟中斷, 然后并把這個軟中斷和系統調用的系統調用號給到了 CPU, CPU 可以根據這個軟中斷找到向量表, 并知道對應的方法.

Chat GPT: OS 的系統調用詳細過程

你說的方向是對的,基本上準確地描述了 Linux 系統調用的流程。不過,細節部分可以再稍微澄清一下,讓我們更好地理解整個過程。

1. 系統調用接口和函數指針表

是的,操作系統會把所有的系統調用接口組織成一個表格,通常被稱作 系統調用表。這個表里存儲了每個系統調用對應的內核函數的地址。在 Linux 內核中,通常使用一個 函數指針數組 來實現這個表。每個系統調用都有一個唯一的系統調用號,類似于一個索引,可以通過該索引找到對應的內核函數。

2. 系統調用號與向量表

在 Linux 中,當操作系統初始化時,它會創建一個 系統調用向量表,這個表在內存中對應著每個系統調用的唯一編號(系統調用號)與實際的內核函數之間的映射關系。簡單來說,就是將每個系統調用號映射到對應的處理函數。

  • 系統調用號通常是一個 整數值,它充當了在系統調用表中的“索引”,通過這個系統調用號,我們可以找到對應的內核實現。
  • 向量表實際上是一種“地址表”,它包含了函數的地址。每次發生系統調用時,CPU 根據系統調用號從這個表中查找并跳轉到對應的內核函數。

3. 軟中斷和系統調用的觸發

當用戶程序需要發起系統調用時,它并不會直接執行內核代碼,而是通過一個特殊的機制,觸發一個 軟中斷,通常是通過 syscall 指令(在 x86_64 架構上)來實現。

  • 軟中斷 是一種機制,允許用戶程序以安全的方式從用戶模式切換到內核模式。它通過一個特殊的中斷向量(比如 0x80 或者 syscall 指令)觸發。
  • 這個軟中斷會通知操作系統進入內核模式,CPU 會暫停用戶程序的執行,保存當前上下文(比如寄存器和程序計數器等),然后跳轉到內核的中斷處理程序。

4. 系統調用的執行過程

當軟中斷發生時,操作系統的內核會做如下處理:

  1. 進入內核模式:CPU 會從用戶模式切換到內核模式,并執行中斷服務程序。這個過程通常會涉及到一個上下文切換。
  2. 讀取系統調用號:系統調用的參數(包括系統調用號)通常會被放在寄存器中或者棧上。內核通過讀取這些信息來知道用戶程序請求了哪個系統調用。
  3. 查找對應的處理函數:通過系統調用號(它是一個整數),內核可以查找系統調用表,找到對應的內核處理函數。
  4. 執行內核函數:內核會執行相應的系統調用處理程序,完成用戶程序請求的服務(例如文件操作、內存分配、網絡通信等)。
  5. 返回用戶模式:處理完成后,操作系統會通過上下文切換恢復用戶程序的執行狀態,返回用戶模式,并繼續執行用戶程序。

總結

你理解的基本流程是正確的。操作系統通過一個系統調用號索引到系統調用表,利用軟中斷機制觸發內核處理。這個過程通過寄存器和中斷向量來實現。這樣,用戶程序就能夠通過系統調用號來訪問操作系統提供的服務,而不需要直接操作內核的實現細節。

簡化來說,你可以把整個過程理解為:

  1. 用戶程序調用系統調用接口。
  2. 通過軟中斷進入內核,系統調用號作為參數傳遞。
  3. 內核根據系統調用號查找系統調用表,執行對應的內核函數。
  4. 執行完畢后,返回用戶程序。

這種設計確保了操作系統能夠安全、穩定地處理用戶請求,同時保持用戶空間和內核空間的隔離。

外部中斷: 由于外部中斷而形成的中斷信號.

陷阱(缺陷): CPU 內部自身形成的中斷信號.

4.2.3.3. 用戶態到內核態

計算機通過 CPU 的硬件配合和軟件支持保證用戶態無法跳轉到內核空間, 而想要執行相關代碼, 必須先陷入內核轉變成內核態才能執行. CPU 有大量寄存器, 有個比較典型的是, 它以末尾兩個比特位來標識當前是什么狀態(0 標識內核態, 3 標識用戶態).

4.3. sigaction 的使用

4.3.1. sigaction 的簡單使用

鏈接: 實驗: sigaction系統調用的使用

4.3.2. sigaction 參數-> sa_mask 的意義

如果當前信號正在被處理, 那么 OS 會暫時屏蔽對該信號的 pending, 直到處理完畢. 實驗: 驗證處理對信號的屏蔽狀態.?操作系統之所以這樣做, 是防止突然一瞬間收到大量信號, 而無限遞歸, 導致棧溢出~

而 sigaction 不光可以捕獲信號, 還可以在抵達信號的時候自定義屏蔽的信號.

當然, sigaction 并不能屏蔽所有信號(比如 8 號信號), 因為系統設計者早已考慮了這一點.

5. 相關拓展概念

5.1. 可重入函數

可重入: 指的是函數的一種特性, 相對應的是不可重入, 可重入表明這個函數當一個執行流執行該函數的部分代碼后, 另一個執行流再次進入該函數又執行了一遍依然可以保證邏輯正確(達到預期效果)的函數, 而不可重入是指在前面那種情況下再次進入該函數會打亂該函數邏輯, 造成內存泄漏等問題.

5.2. volatile 關鍵字

volatile: 一個 C 語言關鍵字, 這玩意是在編譯器無論如何優化的情況下, 都會去保證內存的可見性的一個關鍵字.

實驗: 當 g++編譯器優化效果過高時, 特定代碼會出現非預期狀態.?

5.3. SIGCHLD 信號

子進程退出會主動給父進程發送 SIGCHLD(17 號) 信號, 默認狀態下父進程對此信號的遞達動作是 ignore. 實驗: 實驗: 父進程用信號等待子進程

系統對 SIGCHLD 的 ignore 與用戶主動設置的 ignore 效果不一樣, 系統默認的會讓沒有父進程等待的子進程變成僵尸進程, 而用戶主動設置的 ignore 行為會刪除子進程最后的退出信息(子進程不會變成僵尸進程).

實驗: 系統的 ignore 與用戶主動設置的 ignore 差異驗證.?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85766.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85766.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85766.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年滲透測試面試題總結-2025年HW(護網面試) 02(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 2025年HW(護網面試) 02 1. 有趣的挖洞經歷 2. 高頻漏洞及修復方案 3. PHP/Java反序列化漏洞 4. 服務器入…

Odoo 18進階開發:打造專業級list,kanban視圖Dashboard

&#x1f3af; 項目概述 在現代企業級應用中&#xff0c;數據可視化已成為提升用戶體驗的關鍵要素。Odoo 18 作為領先的企業資源規劃系統&#xff0c;為開發者提供了強大的視圖定制能力。本教程將帶您深入了解如何在list&#xff08;列表&#xff09;視圖和Kanban&#xff08;…

LabVIEW儀表檢測

依托LabVIEW 圖形化開發平臺&#xff0c;集成 NI、Keysight、Fluke 等硬件&#xff0c;構建自動化儀表檢測工裝系統。方案覆蓋從二維碼識別、程序燒寫、多維度校準到數據管理的全流程自動化檢測&#xff0c;解決傳統人工檢測中效率低下&#xff08;單卡檢測效率提升 62.5%&…

Java八股文——消息隊列「場景篇」

什么是消息隊列&#xff1f; 面試官您好&#xff0c;消息隊列&#xff08;Message Queue, MQ&#xff09;&#xff0c;從本質上講&#xff0c;是一個實現了“先進先出”&#xff08;FIFO&#xff09;隊列數據結構的、專門用于在不同系統或服務之間進行可靠異步通信的中間件。 …

CTE vs 子查詢:深入拆解PostgreSQL復雜SQL的隱藏性能差異

1 SQL優化的關鍵抉擇 在PostgreSQL數據庫性能優化領域&#xff0c;CTE&#xff08;公共表表達式&#xff09; 和子查詢的選擇往往決定了復雜SQL查詢的執行效率。許多開發者習慣性地認為兩者功能等價&#xff0c;但實際執行路徑卻存在顯著差異。本文將深入剖析兩者的底層機制&a…

【fargo】x264的intra refresh 1:編碼

【fargo】x264的intra refresh 2:識別NAL類型、 NAL slice header 解析器大神的理論分析: H264Encoder 編碼輸出一幀 D:\XTRANS\thunderbolt\ayame\zhb-bifrost\player-only\echo\codec\x264\echo_h264_encoder.cppbool H264Encoder::encode

npm下載離線依賴包

項目中需要用到mermaid以來&#xff0c;使用npm安裝&#xff1a; npm install mermaid 但是客戶現場是離線環境&#xff0c;無法直接使用npm install mermaid安裝&#xff0c;所以需要考慮下載離線依賴包&#xff0c;命令為&#xff1a; npm pack mermaid 下載后&#xff1…

【教程】不同架構(armv7l等)下載Miniconda安裝包

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 目錄 armv7l架構 aarch、arm、x86架構 armv7l架構 wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-armv7l.sh bash Miniconda3-…

【C++特殊工具與技術】嵌套類

在 C 中&#xff0c;類的嵌套&#xff08;Nested Class&#xff09;是一種強大的封裝手段。通過將一個類定義在另一個類&#xff08;稱為外圍類&#xff0c;Enclosing Class&#xff09;的內部&#xff0c;我們可以將關聯緊密的功能邏輯集中管理&#xff0c;同時限制嵌套類的作…

Python安裝cartopy報錯,解決cartopy環境配置問題

1、嘗試過各種辦法&#xff0c;說依賴包的問題&#xff0c;下載了沒用&#xff0c;說版本問題沒用。conda安裝成功了&#xff0c;運行仍然報錯。采用了一個笨辦法解決&#xff08;高效便捷&#xff09;。 用 conda-forge 重建環境&#xff0c;因為依賴混亂&#xff0c;重新創建…

python實戰項目77:足球運動員數據分析

python實戰項目77:足球運動員數據分析 一、數據集介紹二、加載數據集三、查看數據3.1 查看數據大小3.2 查看數據前幾行3.3 查看數據基本信息四、數據預處理4.1 查看并處理缺失值4.2 查看并處理重復值五、運動員身高和體重分布5.1 查看身高和體重列數據情況5.2 數據類型轉換5.3…

安卓官方版fat-aar:使用Fused Library將多個Android庫發布為一個庫

安卓官方版fat-aar:使用Fused Library將多個Android庫發布為一個庫 - Wesley’s Blog 在開發 sdk 時&#xff0c;內部一般會劃分好幾個模塊。但當需要打包成一個模塊發布時&#xff0c;往往需要依賴第三方插件。比如著名的 fat-aar&#xff1a;https://github.com/kezong/fat-…

VR看房:重塑房地產行業生態的技術革命

在科技浪潮的席卷下&#xff0c;虛擬現實&#xff08;VR&#xff09;技術正深刻重塑房地產行業格局。VR看房這一創新模式的興起&#xff0c;打破了傳統看房的諸多限制&#xff0c;從根本上變革了購房者的看房體驗&#xff0c;也為房地產企業的運營帶來全新契機與挑戰。 一、VR…

vscode+react+ESLint解決不引入組件,vscode不會報錯的問題

解決不引入組件&#xff0c;vscode不會報錯的問題 routes.jsx 全部代碼如下 export const routes [{path:"/",element:<Home/>} ]在項目根目錄下新建個 eslint.config.js 加入下面的代碼&#xff0c;要保證node_modules里安裝了 eslint-plugin-react&a…

Android制作AAR包并混淆后加載調用

為將部分算法代碼封裝為AAR包供其他項目調用&#xff0c;我基于零經驗的情況搭建了一個Demo進行功能驗證&#xff0c;在此記錄流程以備后續參考。 1、新建一個Android Project&#xff0c;選擇No Activity 2、創建 MyClasses 類用于封裝相關代碼 3、創建taar包并在其中定義MyAl…

常見應用層協議介紹

在計算機網絡中,應用層協議是 OSI 模型中的最高層,負責為用戶提供直接的網絡服務。 一、基于TCP的協議 應用層協議端口號基于的傳輸層協議關鍵特性HTTP80TCP無狀態、明文傳輸HTTPS443TCP (TLS加密)HTTP+SSL/TLS加密FTP20(數據)/21(控制)TCP雙通道傳輸、支持認證SSH22TCP加密替…

分享兩個可以一鍵生成sql server數據庫 html格式巡檢報告的腳本

方法一&#xff1a;使用sqlcmd C:\>sqlcmd -S LAPTOP-25D4U18P -i C:\sqlserver_check_html.sql -o C:\check\report.html -h-1 -f 65001sqlserver_check_html.sql代碼如下&#xff1a; SET NOCOUNT ON; -- 修復錯誤的關鍵設置 SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS…

AI Gateway 介紹

AI 網關和傳統的 API 網關 API 網關發展 在最開始的時候&#xff0c;互聯網通過電話線連接上網&#xff0c;通過“調制解調器&#xff08;Modem&#xff09;”將計算機信號和電話線信號“調制”與“調解”以實現上網功能。當今時代大多使用寬帶上網&#xff0c;撥號上網已被逐…

15.3 LLaMA 3+LangChain實戰:智能點餐Agent多輪對話設計落地,訂單準確率提升90%!

LLaMA 3LangChain實戰&#xff1a;智能點餐Agent多輪對話設計落地&#xff0c;訂單準確率提升90%&#xff01; 關鍵詞&#xff1a;多輪對話設計、場景化提示工程、LLaMA 3 微調、LangChain Agent、飯店點餐場景建模 飯店點餐場景的 Agent 方案設計 通過分層架構實現復雜場景對…

EXPLAIN優化 SQL示例

以下通過 6 個真實案例展示如何使用 EXPLAIN 優化 SQL&#xff0c;每個案例包含問題 SQL、EXPLAIN 分析、優化方案和優化后效果對比&#xff1a; 案例 1&#xff1a;全表掃描優化 (typeALL) 問題 SQL&#xff08;用戶訂單查詢&#xff09;&#xff1a; SELECT * FROM orders …