前言:歡迎各位光臨本博客,這裏小編帶你直接手撕,文章並不複雜,願諸君耐其心性,忘卻雜塵,道有所長!!!!

Linux:理解用户態與內核態_代碼示例


IF’Maxue:個人主頁

個人專欄:

《C語言》《C++深度學習》

《Linux》

《數據結構》

《數學建模》


永久的享受。不破不立!就是⛺️生活是默默的堅持,毅力



文章目錄

  • 內存中的程序與操作系統
  • 用户態與內核態:權限邊界
    • 權限識別與切換
    • 段描述符表控權
  • 捕捉信號:sigaction用法
    • sigaction函數結構
    • 函數返回值
    • sigaction的優勢
    • 代碼示例與運行結果
  • sa_mask信號集:屏蔽規則
    • 核心機制
    • 規則更新説明
    • 實際框架表現與擴展屏蔽
    • 擴展屏蔽代碼示例




內存中的程序與操作系統

軟件,自然也得佔一塊內存空間。就是程序一運行,它的代碼和要用的數據就會“住進”物理內存;操作系統本身也

每個用户程序(進程)都有自己專屬的“地址映射表”(用户頁表)——就像每家有自己的門牌號對照表,所以這類表會有很多份;但操作系統的“地址映射表”(內核頁表)全系統只需要一份,所有進程共用。

為啥這麼設計?很簡單:操作系統的代碼和材料是固定的,一份映射表就夠讓所有進程找到它,不管進程怎麼切換,都能精準定位到操作系統。

那操作系統的內存裏都裝了啥?看下面這張圖就清楚了:

Linux:理解用户態與內核態_用户態_02


用户態與內核態:權限邊界

用户進程和操作系統(內核)共用0-4GB的“虛擬內存地盤”。這時候你可能會問:用户進程會不會偷偷跑到內核的地盤,亂訪問內核的代碼和數據?

答案是:絕對不會!因為操作系統對誰都不放心,它用“用户態”和“內核態”劃了條嚴格的權限紅線:

  • 用户態:進程以普通用户身份運行,只能在自己的0-3GB虛擬內存裏折騰;不用麻煩操作系統時,直接用自己的虛擬地址就能執行自己的資源;
  • 內核態:進程以內核身份運行(比如要調用系統功能時),能訪問整個0-4GB虛擬內存,負責管理電腦的全局資源(比如CPU、內存)。

權限識別與切換

CPU裏有個cs寄存器,它的前兩個比特位是“權限身份證”(CPL),能判斷當前進程的權限:

  • 數值0:內核態;
  • 數值3:用户態。
  • Linux:理解用户態與內核態_代碼示例_03


如果用户態進程敢碰3-4GB的內核地盤,CPU一檢測到越權,就會直接終止這個進程,絕不留情。

那用户態怎麼切到內核態?得用專門的“指令鑰匙”:

  • int 0x80:比較老的系統調用方式;
  • syscall:更高效的系統調用方式。
    這兩個指令的作用很容易:把cs寄存器前兩位改成0(切到內核態),再跳轉到內核的內存地址——相當於“進入內核的辦公區”。
  • Linux:理解用户態與內核態_內核態_04


段描述符表控權

除了用户態、內核態的大劃分,還有“段描述符表”(分全局、局部兩種)來細化權限管理。表裏面的RPL和DPL是兩個“權限檢查官”:

  • DPL:某塊內存區域(段)本身的權限級別,規定了訪問它至少應該什麼權限;
  • RPL:請求訪問該區域的進程的權限級別。內核會對比這兩個級別,只有滿足要求,才允許進程訪問。

捕捉信號:sigaction用法

在Linux裏,除了signal函數能處理信號,sigaction是更靈活的“信號處理器”——既能查看當前信號的處理方式,也能修改它的處理動作。

sigaction函數結構

這張圖把函數的“長相”和參數分工講得很清楚:

Linux:理解用户態與內核態_代碼示例_05

  • signum:要操作的信號編號(比如SIGINT,就是按Ctrl+C觸發的信號);
  • act:指向sigaction結構體的指針,裏面裝着新的信號處理配置(比如用哪個函數處理信號);
  • oldact:用來保存原來的信號處理配置,不用保存的話填NULL就行。

函數返回值

  • 成功:返回0;
  • 失敗:返回-1,還會設置errno變量,告訴你哪裏出錯了。
  • Linux:理解用户態與內核態_代碼示例_06


sigaction的優勢

它比signal函數功能更全——不僅能處理普通信號,還能處理實時信號,應對複雜的信號場景更給力。

Linux:理解用户態與內核態_內核態_07

代碼示例與運行結果

下面是實際使用代碼,註釋寫得很細,跟着看就能懂:

Linux:理解用户態與內核態_用户態_08


比如代碼裏定義了sig_handler函數處理信號,再用sigaction把SIGINT信號和這個函數綁定;運行後按Ctrl+C,就能看到信號被成功捕捉,輸出對應的提示——運行效果如下:

Linux:理解用户態與內核態_用户態_09


sa_mask信號集:屏蔽規則

sigaction結構體裏有個sa_mask成員,它是“信號屏蔽清單”——負責控制信號處理期間,哪些信號不能進來打擾。

核心機制

當某個信號的處理函數開始工作時,內核會自動把這個信號加到“屏蔽表”(block表)裏,把對應的位從0改成1——防止同一個信號反覆來搗亂;等處理函數執行完返回,再把這個信號從屏蔽表裏去掉,位從1改回0。

還有個“未決表”(pending),狀態和屏蔽表正好相反:信號沒處理時位是1,處理完就變成0。

Linux:理解用户態與內核態_代碼示例_10


説白了:操作系統不允許同一個信號同時被多次處理,得一個一個來。

規則更新説明

關於屏蔽表和未決表的狀態變化,看這張圖就能理清:

Linux:理解用户態與內核態_用户態_11

實際框架表現與擴展屏蔽

在Ubuntu 20.04、CentOS 7這些常見體系裏,當前正在處理的信號會被自動屏蔽。如果想在處理某個信號時,順便屏蔽其他信號,把那些信號加到sa_mask裏就行。

Linux:理解用户態與內核態_用户態_12


Linux:理解用户態與內核態_內核態_13


Linux:理解用户態與內核態_代碼示例_14

擴展屏蔽代碼示例

下面的代碼演示了怎麼給sa_mask加額外的屏蔽信號——比如處理SIGINT時,順便屏蔽SIGQUIT(按Ctrl+\觸發的信號),這樣在處理SIGINT期間按Ctrl+\,信號會被屏蔽,等SIGINT處理完才會生效:

Linux:理解用户態與內核態_代碼示例_15