Linux0.11系統調用:預備知識

系統調用

預備知識

目標:了解系統調用的流程,在Linux 0.11上添加兩個系統調用,并編寫兩個簡單的應用程序測試它們。

對應章節:同濟大學趙炯博士的《Linux內核0.11完全注釋(修正版V3.0)》的第5.5節

下面就針對這一節做一些筆記

系統調用的概念

系統調用(通常稱為 syscalls)是 Linux 內核與上層應用程序進行交互通信的唯一接口。這些接口為了應用級的代碼能夠在不同的操作系統上都可以運行,就需要有標準去限制系統調用接口的標準。不同操作系統的系統調用接口都要按照這個標準來。

POSIX(Portable Operating System Interface for Computing Systems)由IEEE開發,是一個標準族: 1003.1, 2003…于保證編制的應用程序可以在源代碼一級上在多種操作系統上移植和運行。

image-20250429162117425

那用戶態程序想要訪問內核資源怎么辦呢?

  • 從對中斷機制的說明可知,用戶程序通過直接或間接(通過庫函數)調用中斷 int 0x80
  • 并在 eax寄存器中指定系統調用功能號,即可使用內核資源,包括系統硬件資源。
  • 但是,這個太麻煩了還需要記住系統調用的功能號,還需設置中斷,標準接口定義的 C 函數庫中的函數間接地使用內核的系統調用,C 函數庫已經幫封裝好了
  • 系統調用的實現:(C 函數庫已經給封裝好了)
    • (1) 用戶程序中寫上一段包含int指令的代碼
    • (2) OS寫中斷處理代碼,獲取想調程序的編號(系統調用編號)
    • (3) OS根據編號轉去執行相應的代碼

image-20250429163350968

上面提到的系統調用的功能號系統調用編號是什么?

  • 指的是同一個東西:在 Linux 內核中,每個系統調用都具有唯一的一個系統調用功能號。

  • 這些號定義在文件include/unistd.h 中第 60 行開始處。例如,write 系統調用的功能號是 4,定義為符號__NR_write。

    // 以下是內核實現的系統調用符號常數,用于作為系統調用函數表中的索引值。( include/linux/sys.h )
    #define __NR_setup 0		/* used only by init, to get system going */
    /* __NR_setup 僅用于初始化,以啟動系統 */
    #define __NR_exit 1
    #define __NR_fork 2
    #define __NR_read 3
    #define __NR_write 4 
    
  • 這些系統調用號對應于 include/linux/sys.h 中定義的系統調用處理程序指針數組表 sys_call_table[]中項的索引值。因此 write()系統調用的處理程序指針就位于該數組的項 4 處。

    extern int sys_setup ();	// 系統啟動初始化設置函數。 (kernel/blk_drv/hd.c,71)
    extern int sys_exit ();		// 程序退出。 (kernel/exit.c, 137)
    extern int sys_fork ();		// 創建進程。 (kernel/system_call.s, 208)
    extern int sys_read ();		// 讀文件。 (fs/read_write.c, 55)
    extern int sys_write ();	// 寫文件。 (fs/read_write.c, 83)
    // 系統調用函數指針表。用于系統調用中斷處理程序(int 0x80),作為跳轉表。
    fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,sys_write, sys_open,省略了
    };
    

這里有很多的系統調用處理函數,他們有幾個相同點:

  • 系統調用執行的結果返回值。通常負值表示錯誤,而 0 則表示成功。

    在出錯的情況下,錯誤的類型碼被存放在全局變量 errno 中。通過調用庫函數 perror(),我們可以打印出該錯誤碼對應的出錯字符串信息。

系統調用處理過程

image-20250429172248493

現在以read系統調用為例,來說明這個過程

首先,在include/unistd.h文件里面有個read函數的聲明

// 對應各系統調用的函數原型定義。int read(int fildes, char * buf, off_t count);

但是,操作系統是負責寫系統調用的,是要提供系統調用sys_read(),在文件fs/read_write.c中。接下來給出從read()->sys_read()的具體流程。

前面給出了read()的聲明,下面得找到定義,Linux0.11的源碼好像沒有找到,但是在lib文件夾有一些其他的類似的函數定義例如write()。代碼如下,這里用到了_syscall3宏,把這個宏展開之后,得到write()的定義。

write.s文件
#include <set_seg.h>
#define __LIBRARY__
// Linux 標準頭文件。定義了各種符號常數和類型,并申明了各種函數。
// 如定義了__LIBRARY__,則還包括系統調用號和內嵌匯編_syscall0()等。
#include <unistd.h> 寫文件系統調用函數。
// 該宏結構對應于函數:int write(int fd, const char * buf, off_t count)
// 參數:fd - 文件描述符;buf - 寫緩沖區指針;count - 寫字節數。
// 返回:成功時返回寫入的字節數(0 表示寫入0 字節);出錯時將返回-1,并且設置了出錯號。
_syscall3(int,write,int,fd,const char *,buf,off_t,count)展開之后:
int write(int fd, const char *buf, off_t count)
{long __res;__asm__ volatile ("int $0x80": "=a" (__res): "0" (__NR_write), "b" ((long)(fd)), "c" ((long)(buf)), "d" ((long)(count)));if (__res >= 0)return (int) __res;errno = -__res;return -1;
}

image-20250429202424816

接下來介紹宏_syscalln():這個宏完成了c標準庫函數的定義,也就是對相關的系統調用sys_函數名的封裝。

其中 n 代表攜帶的參數個數,可以分別 0 至 3。最多可以直接傳遞 3 個參數。其中寄存器 eax 中存放著系統調用號,而攜帶的參數可依次存放在寄存器 ebx、ecx 和 edx 中。所以用戶程序能夠向內核最多直接傳遞3個參數,當然也可以不帶參數。下面給出_syscall3()的代碼

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \: "=a" (__res) \: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \return (type) __res; \
errno=-__res; \
return -1; \
}

依然以read為例來說明宏_syscall3()的作用

GCC 內聯匯編(AT&T格式)學習資源:https://zhuanlan.zhihu.com/p/606376595

#define --LIBRARY-- 
#include <unistd.h>
-syscall3(int, read, int, fd, char *, buf, int, n) 展開之后int read(int fd, char *buf, int n)
{// 存儲系統調用返回值long __res;// 內聯匯編觸發系統調用,volatile:編譯器不優化__asm__ volatile ("int $0x80"          // 觸發系統調用的中斷指令: "=a" (__res)       // 輸出,系統調用返回值存入 __res "=a" 表示使用寄存器 eax 來傳遞返回值,"=" 表示只寫操作: "0" (__NR_read),   // 輸入,"0" 表示使用和輸出操作數列表中編號為 0 的操作數相同的寄存器(即 eax)"b" ((long)(fd)),  // ebx 傳遞文件描述符"c" ((long)(buf)), // ecx 傳遞緩沖區指針"d" ((long)(n))    // edx 傳遞要讀取的字節數);// 系統調用成功,返回讀取字節數if (__res >= 0)return (int)__res;// 系統調用失敗,設置錯誤碼errno = -__res;return -1;
}

接下來,將展開后的代碼分成三部分

  • 首先,傳入給eax寄存器一個系統調用號__NR_read,這個前文已經說明,__NR_read=3sys_read()這個系統調用函數在sys_call_table[]中項的索引值,有了這個才能定位到系統調用函數。輸出的結果也使用寄存器 eax 來傳遞返回值

  • int $0x80:進入系統調用中斷程序在kernel/system_call.s中。下面是中斷代碼:

    `kernel/system_call.s`中
    # ====================== 系統調用處理流程 ====================== #
    # 入口:用戶態執行int 0x80后CPU跳轉至此
    .align 2
    system_call:cmpl $nr_system_calls-1,%eax   # 1. 校驗系統調用號范圍ja bad_sys_call                # 超過最大值跳錯誤處理# ==== 保存用戶態上下文 ==== #push %ds                       # 2. 保存用戶數據段push %es                       # 保存用戶附加段push %fs                       # 保存特殊用途段pushl %edx                     # 系統調用參數3pushl %ecx                     # 參數2pushl %ebx                     # 參數1# ==== 設置內核環境 ==== #movl $0x10,%edx                # 3. 內核數據段選擇子(GDT[2], RPL=0)mov %dx,%ds                    # 設置ds/es為內核數據段mov %dx,%esmovl $0x17,%edx                # 用戶數據段選擇子(LDT[2], RPL=3)mov %dx,%fs                    # fs保持用戶數據訪問能力# ==== 執行系統調用 ==== #call *sys_call_table(,%eax,4)  # 4. 查系統調用表執行對應函數# eax*4因為函數指針占4字節# ==== 調度檢查 ==== #pushl %eax                     # 保存系統調用返回值movl current,%eax              # 獲取當前進程task_struct指針cmpl $0,state(%eax)           # 5. 檢查進程狀態(0=就緒態)jne reschedule                 # 非就緒態跳調度cmpl $0,counter(%eax)         # 檢查時間片剩余je reschedule                  # 時間片耗盡跳調度# ==================== 系統調用返回路徑 ==================== #
    ret_from_sys_call:movl current,%eax             cmpl task,%eax                # 6. 是否是初始任務0?je 3f                         # 是則跳過信號處理cmpw $0x0f,CS(%esp)           # 檢查原CS是否是用戶態代碼段jne 3fcmpw $0x17,OLDSS(%esp)        # 檢查原SS是否是用戶態堆棧段jne 3f# ==== 信號處理 ==== #movl signal(%eax),%ebx        # 7. 獲取待處理信號位圖movl blocked(%eax),%ecx       # 獲取阻塞信號掩碼notl %ecx                     # 反轉掩碼(允許通過的信號)andl %ebx,%ecx                # 計算有效信號bsfl %ecx,%ecx                # 掃描最低有效位je 3f                         # 無信號則跳過btrl %ecx,%ebx                # 清除已處理信號位movl %ebx,signal(%eax)        # 更新信號位圖incl %ecx                     # 信號編號轉為1-basedpushl %ecx                    # 參數壓棧call do_signal                # 8. 調用信號處理函數popl %eax# ==== 恢復上下文 ==== #
    3:  popl %eax                     # 恢復系統調用返回值popl %ebx                     # 9. 逆向彈出寄存器popl %ecxpopl %edxpop %fspop %espop %dsiret                          # 10. 中斷返回用戶態# ====================== 錯誤處理路徑 ====================== #
    .align 2
    bad_sys_call:movl $-1,%eax                 # 返回-1表示錯誤iret                          # 直接返回用戶態# ====================== 進程調度路徑 ====================== #
    .align 2
    reschedule:pushl $ret_from_sys_call      # 將返回地址壓棧jmp schedule                  # 跳轉到調度函數

    這里的int 0x80是系統調用中斷是在哪設置呢?為什么int 0x80就跳到_system_call執行呢?

    • 通常,異常中斷處理過程(int0 --int 31)都在 traps.c 的初始化函數中進行了重新設置(kernl/traps.c,181)
    • 而系統調用中斷 int128 則在調度程序初始化函數中進行了重新設置(kernel/sched.c,385)。
    // 調度程序的初始化子程序。
    void sched_init (void)
    {// 設置時鐘中斷處理程序句柄(設置時鐘中斷門)。set_intr_gate (0x20, &timer_interrupt);// 修改中斷控制器屏蔽碼,允許時鐘中斷。outb (inb_p (0x21) & ~0x01, 0x21);// 設置系統調用中斷門。set_system_gate (0x80, &system_call);
    }
    
  • 上面的中斷系統調用函數中call [_sys_call_table + eax * 4] ,直接跳轉到sys_read()函數位置完成了系統調用。

下面給出整個read()->sys_read()的簡易流程圖

┌─────────────┐            ┌───────────────┐
│  用戶態      │            │   內核態       │
├─────────────┤            ├───────────────┤
│ 1. read()   │            │               │
└──────┬──────┘            │               │▼                   │               │
┌──────┴───────┐           │               │
│ 2. int 0x80  ├───中斷───? │3. system_call│
│ (NR=3 in EAX)│           │  - 檢查NR有效性 │
└──────────────┘           │  - 保存寄存器   │▼               │┌─────┴─────┐         ││4.查系統調用表			|        │sys_call_table       │       │[NR=3] → sys_read    │└─────┬─────┘         ││               │▼               │┌─────┴─────┐         ││5.sys_read()         │└─────┬─────┘         ││               │▼               │┌─────┴─────┐         ││ 返回用戶態  │         ││ (iret指令) │         │└───────────┘         │

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

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

相關文章

如何防止 ES 被 Linux OOM Killer 殺掉

當 Linux 系統內存不足時&#xff0c;內核會找出一個進程 kill 掉它釋放內存&#xff0c;旨在保障整個系統不至于崩潰。如果 ES 按照最佳實踐去實施部署&#xff0c;會保留一半的內存&#xff0c;不至于發生此類事情。但事情總有例外&#xff0c;有的朋友可能 ES 和其他的程序部…

swagger2升級至openapi3的利器--swagger2openapi

背景&#xff1a; 因為項目需要升級JDK&#xff0c;涉及到swagger2升級至openapi3的情況。由于swagger 2和openapi 3的語法差距太大&#xff0c;需要對yaml進行升級。無奈單個yaml文件的內容太大&#xff0c;高至4萬多行&#xff0c;手動進行語法的轉換肯定是不可能了&#xff…

在yolo中Ultralytics是什么意思呢?超越分析的智能

在YOLO&#xff08;You Only Look Once&#xff09;目標檢測框架中&#xff0c;Ultralytics 是一家專注于計算機視覺和機器學習技術的公司&#xff0c;同時也是YOLO系列模型&#xff08;如YOLOv5、YOLOv8等&#xff09;的官方開發和維護團隊。以下是關鍵點解析&#xff1a; 1. …

【阿里云大模型高級工程師ACP習題集】2.7 通過微調增強模型能力 (上篇)(?????? 重點章節!!!)

習題集: 【單選題】在大模型微調中,與提示工程和RAG相比,微調的獨特優勢在于( ) A. 無需外部工具即可提升模型表現 B. 能讓模型學習特定領域知識,提升底層能力 C. 可以更高效地檢索知識 D. 能直接提升模型的知識邊界,無需訓練 【多選題】以下關于機器學習和傳統編程的說…

CuML + Cudf (RAPIDS) 加速python數據分析腳本

如果有人在用Nvidia RAPIDS加速pandas和sklearn等庫&#xff0c;請看我這個小示例&#xff0c;可以節省你大量時間。 1. 創建環境 請使用uv&#xff0c;而非conda/mamba。 # install uv if not yetcurl -LsSf https://astral.sh/uv/install.sh | shuv init data_gpucd data_g…

2-SAT之完美塔防

小N最近喜歡玩一款塔防游戲。 題目描述 這款游戲的棋盤是一個 nm 的網格&#xff0c;每個格子上會有以下類型物件&#xff1a; A 型炮臺&#xff1a;會向上下兩個方向同時發射激光&#xff0c;符號為 |;B 型炮臺&#xff1a;會向左右兩個方向同時發射激光&#xff0c;符號為…

【android bluetooth 案例分析 03】【PTS 測試 】【PBAP/PCE/SSM/BV-02-C】

1. 測試介紹 PBAP/PCE/SSM/BV-02-C [PCE Closes a PBAP Session] 1. Test Purpose Verify that the PCE can terminate a PBAP session. 2. Initial Condition IUT: The IUT is engaged in a PBAP session with the Lower Tester.Lower Tester: The Lower Tester is engag…

ArcGIS:開啟洪水災害普查、評估與制圖新征程

技術點目錄 一、洪水普查技術規范解讀二、ArcGIS介紹及數據管理三、空間數據的轉換與處理四、洪水淹沒專題地圖制作五、矢量數據的采集與處理六、柵格數據的下載與處理七、ArcGIS水文分析八、ArcGIS洪水分析九、ArcGIS淹沒分析了解更多 ———————————————————…

【系統參數合法性校驗】spring-boot-starter-validation

JSR303校驗 統一校驗的需求 前端請求后端接口傳輸參數&#xff0c;是在controller中校驗還是在Service中校驗&#xff1f; 答案是都需要校驗&#xff0c;只是分工不同。 Contoller中校驗請求參數的合法性&#xff0c;包括&#xff1a;必填項校驗&#xff0c;數據格式校驗&…

[零基礎]內網ubuntu映射到云服務器上,http訪問(frp內網穿透)

阿里云服務器&#xff0c;高校教師可以半價&#xff0c; frp下載地址&#xff1a;https://github.com/fatedier/frp/releases&#xff0c;選amd64&#xff0c; 云服務器開放端口 選擇網絡與安全–>安全組->管理規則 配置開放端口&#xff0c;7000為支持frp開放的端口&…

第十六屆藍橋杯 2025 C/C++組 破解信息

目錄 題目&#xff1a; 題目描述&#xff1a; 題目鏈接&#xff1a; 思路&#xff1a; 思路詳解&#xff1a; 代碼&#xff1a; 代碼詳解&#xff1a; 題目&#xff1a; 題目描述&#xff1a; 題目鏈接&#xff1a; P12344 [藍橋杯 2025 省 B/Python B 第二場] 破解信息…

OpenAI Embedding 和密集檢索(如 BERT/DPR)進行語義相似度搜索有什么區別和聯系

OpenAI Embedding 和密集檢索&#xff08;如 BERT/DPR&#xff09;其實是“同一種思想的不同實現”&#xff0c;它們都屬于Dense Retrieval&#xff08;密集向量檢索&#xff09;&#xff0c;只不過使用的模型、部署方式和調用方式不同。 &#x1f9e0; 首先搞清楚&#xff1a;…

Linux電源管理(3)_關機和重啟的過程

原文&#xff1a;Linux電源管理&#xff08;3&#xff09;_Generic PM之重新啟動過程 1.前言 在使用計算機的過程中&#xff0c;關機和重啟是最先學會的兩個操作。同樣&#xff0c;這兩個操作在Linux中也存在&#xff0c;可以關機和重啟。這就是這里要描述的對象。在Linux Ke…

C# 繼承詳解

繼承是面向對象程序設計&#xff08;OOP&#xff09;中的核心概念之一&#xff0c;它極大地增強了代碼的重用性、擴展性和維護性。本篇文章將詳細講解C#中的繼承機制&#xff0c;包括基礎概念、語法特法、多重繼承&#xff08;通過接口實現&#xff09;、繼承的規則和實際應用示…

SQLAlchemy 2.x 異步查詢方法比較

SQLAlchemy 2.x 異步查詢中常用的 結果處理方法速查表&#xff0c;包含方法說明、使用場景、返回類型及典型用途。 SQLAlchemy 查詢結果處理方法速查表&#xff08;適用于 AsyncSession&#xff09; 方法 說明 返回類型 示例 SQL 示例輸出 scalars().all() 獲取單列所有…

極客天成參與”AI助力智慧城市構建”主題演講暨招商引智專題推介活動

4月7日下午&#xff0c;北京極客天成科技有限公司參加了天津市河東區數據局舉辦的“AI賦能智慧城市構建”主題演講暨招商引智專題推介活動。 活動中&#xff0c;華為&#xff08;天津&#xff09;有限公司數字政府解決方案總監姜華庚圍繞“政務大模型賦能智慧城市建設”&#x…

理解 EKS CloudWatch Pod CPU Utilization 指標:與 `kubectl top` 及節點 CPU 的關系

在使用 AWS EKS 時&#xff0c;CloudWatch Container Insights 提供了豐富的容器級別監控指標&#xff0c;幫助我們深入了解應用的運行狀態。如下截圖中的 ContainerInsights pod_cpu_utilization 指標就是一個非常重要的維度。本文將詳細解釋這個指標的含義&#xff0c;并將其…

使用pip3安裝軟件包報錯`externally-managed-environment`的幾種解決方式

1、pip3安裝軟件包報錯 報錯externally-managed-environment的原因&#xff1a; 從 Python 3.11 開始引入了 PEP 668 規范&#xff0c;該規范限制了在系統級 Python 環境中使用 pip 安裝第三方包&#xff0c;以避免與系統包管理器&#xff08;如 apt&#xff09;產生沖突。 如…

spring security用戶退出

Spring security默認實現了用戶退出的功能&#xff0c;用戶退出主要考慮退出后會話如何管理以及跳轉到哪個頁面。HttpSecurity類提供了logout()方法開啟退出登錄的支持&#xff0c;默認觸發用戶退出操作的URL為“/logout”&#xff0c;用戶退出時同時也會清除Session等默認用戶…

愛普生SG2520HHN晶振數據中心服務器的理想解決方案

在當今數字化時代&#xff0c;數據中心作為海量數據存儲、處理與傳輸的核心樞紐&#xff0c;其服務器的高效穩定運行至關重要。服務器作為其核心設備&#xff0c;對時鐘信號的精度和穩定性提出了嚴苛要求——微小的時序誤差可能導致數據傳輸失敗或系統宕機。愛普生 SG2520HHN 差…