Linux0.11內核系列—2.系統調用機制分析

来源:http://www.cnblogs.com/joey-hua/archive/2016/06/09/5570691.html
-Advertisement-
Play Games

【版權所有,轉載請註明出處。出處:http://www.cnblogs.com/joey-hua/p/5570691.html 】 Linux內核從啟動到初始化也看了好些個源碼文件了,這次看到kernel文件夾下的system_call.s,這個文件主要就是系統調用的過程。但說到系統調用,不只是這一 ...


【版權所有,轉載請註明出處。出處:http://www.cnblogs.com/joey-hua/p/5570691.html 】

 

Linux內核從啟動到初始化也看了好些個源碼文件了,這次看到kernel文件夾下的system_call.s,這個文件主要就是系統調用的過程。但說到系統調用,不只是這一個文件這麼簡單,裡面牽扯到的內容太多,這裡就做個筆記記錄一下從建立中斷到最終調用系統調用的完整機制。

假設就從write這個函數作為系統調用來解釋。

系統調用的本質就是用戶進程需要訪問內核級別的代碼,但用戶進程的許可權是最低的,內核代碼是許可權最高的,不允許直接訪問,需要通過中斷門作為媒介來實現許可權的跳轉。簡單講就是用戶進程調用一個中斷,這個中斷再去訪問內核代碼。這裡就來學習一下Linux內核具體是怎麼做的。

1.建立中斷描述符表IDT

因為要用到中斷,所以首先要建立中斷描述符表IDT,作用如下圖:

在head.s文件中,建立好了IDT,比如要使用int 0x80,就從_idt開始找到偏移為0x80的地方執行代碼。

	.align 3						# 按8 位元組方式對齊記憶體地址邊界。
_idt:	.fill 256,8,0			# idt is uninitialized# 256 項,每項8 位元組,填0。

idt_descr:					#下麵兩行是lidt 指令的6 位元組操作數:長度,基址。
	.word 256*8-1			# idt contains 256 entries
	.long _idt

	lidt idt_descr							# 載入中斷描述符表寄存器值。

2.建立0x80號中斷

所有的系統調用都是通過0x80號中斷來實現的,所以接下來就是建立第0x80號中斷,在sched.c中:

// 設置系統調用中斷門。
  set_system_gate (0x80, &system_call);

這裡通過set_system_gate這個巨集定義就把0x80中斷和函數system_call關聯上了,這裡先不管system_call,先看set_system_gate,在system.h中:

//// 設置系統調用門函數。
// 參數:n - 中斷號;addr - 中斷程式偏移地址。
// &idt[n]對應中斷號在中斷描述符表中的偏移值;中斷描述符的類型是15,特權級是3。
#define set_system_gate(n,addr) _set_gate(&idt[n],15,3,addr)


//// 設置門描述符巨集函數。
// 參數:gate_addr -描述符地址;type -描述符中類型域值;dpl -描述符特權層值;addr -偏移地址。
// %0 - (由dpl,type 組合成的類型標誌字);%1 - (描述符低4 位元組地址);
// %2 - (描述符高4 位元組地址);%3 - edx(程式偏移地址addr);%4 - eax(高字中含有段選擇符)。
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ( "movw %%dx,%%ax\n\t" \	// 將偏移地址低字與選擇符組合成描述符低4 位元組(eax)。
  "movw %0,%%dx\n\t" \		// 將類型標誌字與偏移高字組合成描述符高4 位元組(edx)。
  "movl %%eax,%1\n\t" \		// 分別設置門描述符的低4 位元組和高4 位元組。
"movl %%edx,%2":
:"i" ((short) (0x8000 + (dpl << 13) + (type << 8))),
  "o" (*((char *) (gate_addr))),
  "o" (*(4 + (char *) (gate_addr))), "d" ((char *) (addr)), "a" (0x00080000))

這裡參考中斷門結構圖可知,這裡設置特權級是3,用戶進程也是3,就可以直接訪問此中斷,偏移地址對應的上面的system_call,也就是說如果調用中斷int 0x80,那麼就會去訪問system_call函數。註意這裡的n就是0x80,也就是idt數組的[0x80],idt在head.h中聲明,編譯後會變成符號_idt,在head.s中定義的,就此關聯上。

3.聲明系統調用函數

以write系統函數為例,在write.c中聲明此函數:

_syscall3 (int, write, int, fd, const char *, buf, off_t, count)

_syscall3又是一個巨集定義,在unistd.h中:

// 有3 個參數的系統調用巨集函數。type name(atype a, btype b, ctype c)
// %0 - eax(__res),%1 - eax(__NR_name),%2 - ebx(a),%3 - ecx(b),%4 - edx(c)。
#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) \
: "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)), "d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}

所以翻譯過來就是在write.c中可以寫成:

int write(int fd,const char* buf,off_t count) \
{ \
long __res; \
__asm__ volatile ( "int $0x80" \
: "=a" (__res) \
: "" (__NR_write), "b" ((long)(fd)), "c" ((long)(buf)), "d" ((long)(count))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}

是不是一下子就清晰明朗了,也就是說,如果一個用戶進程要使用write函數,就會去調用int 0x80中斷,然後把三個參數fd、buf、count分別存入ebx、ecx、edx寄存器,還有個最關鍵的是_NR_write,會把這個值存入eax寄存器,具體做什麼用等會再說,這個是在unistd.h中定義的:

#define __NR_write 4

好,現在各種初始化和聲明都完成了,萬事俱備只欠東風!

4.系統調用過程

用戶進程調用函數write,就會調用int 0x80中斷,上面第2點已經說了,如果調用中斷int 0x80會去訪問system_call函數,sched.c:

extern int system_call (void);	// 系統調用中斷處理程式(kernel/system_call.s,80)。

是在system_call中定義,註意編譯後頭部會加上_,以下代碼只截取了前半部分:

_system_call:
	cmpl $nr_system_calls-1,%eax 	# 調用號如果超出範圍的話就在eax 中置-1 並退出。
	ja bad_sys_call
	push %ds 											# 保存原段寄存器值。
	push %es
	push %fs
	pushl %edx 						# ebx,ecx,edx 中放著系統調用相應的C 語言函數的調用參數。
	pushl %ecx 						# push %ebx,%ecx,%edx as parameters
	pushl %ebx 						# to the system call
	movl $0x10,%edx 			# set up ds,es to kernel space
	mov %dx,%ds 				# ds,es 指向內核數據段(全局描述符表中數據段描述符)。
	mov %dx,%es
	movl $0x17,%edx 			# fs points to local data space
	mov %dx,%fs 					# fs 指向局部數據段(局部描述符表中數據段描述符)。
# 下麵這句操作數的含義是:調用地址 = _sys_call_table + %eax * 4。參見列表後的說明。
# 對應的C 程式中的sys_call_table 在include/linux/sys.h 中,其中定義了一個包括72 個
# 系統調用C 處理函數的地址數組表。
call _sys_call_table(,%eax,4)
pushl %eax 										# 把系統調用號入棧。(這個解釋錯誤,是函數返回值入棧)
movl _current,%eax 						# 取當前任務(進程)數據結構地址??eax。

註意從pushl %edx開始的三句代碼,是前面第3點提到的三個參數依次從右向左入棧。重點是call _sys_call_table(,%eax,4)這句代碼,翻譯過來就是call [eax*4 + _sys_call_table],根據第3點,eax存的是_NR_write的值也就是4,因為_sys_call_table是sys.h中的一個int (*)()類型的數組,裡面存的是所有的系統調用函數地址,所以再翻譯一下就是訪問sys_call_table[4]也就是sys_write函數:

// 系統調用函數指針表。用於系統調用中斷處理程式(int 0x80),作為跳轉表。
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
  sys_write, ...}

sys_write在fs下的read_write.c:

int
sys_write (unsigned int fd, char *buf, int count)
{
  struct file *file;
  struct m_inode *inode;
...
}

好了,到這裡為止才明白千迴百轉最終調用的就是這個sys_write函數。至此分析結束!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、如何通過代碼設置Button title的字體大小 設置Button.titleLabel.font = [UIFont systemFontOfSize:<#(CGFloat)#>] ; 2、獲取當前時間 3、判斷字元串是否為空字元的方法 4、tableView優化 5、百度地圖自定義大頭針圖 ...
  • 添加按鈕 ...
  • 使用Genymotion調試出現錯誤INSTALL_FAILED_CPU_ABI_INCOMPATIBLE解決辦法 http://www.cnblogs.com/xiaobo-Linux/ 下載地址:http://files.cnblogs.com/files/xiaobo-Linux/genymo ...
  • 一、NSAttributeString簡介 NSAttributedString叫做富文本,是一種帶有屬性的字元串,通過它可以輕鬆的在一個字元串中表現出多種字體、字型大小、字體大小等各不相同的風格,還可以對段落進行格式化。 二、字元屬性 1.NSString *const NSFontAttribute ...
  • 介紹 最近用淘寶客戶端的時候,編輯地址的時候有個地區選擇的功能。看上面的效果覺得挺酷,滾動的時候,是最後一個從下麵飛上來挨著前一個。就自己鼓搗一個出來玩玩。 說了效果可能不太直觀,下麵上兩張圖看看效果 淘寶地區選擇效果 再來一張自己的效果 gif的效果可能不太好,大家自己用Android手機打開淘寶 ...
  • 前言 斷斷續續的已經學習 一年多了, 從 到現在的 , 一直在語法之間徘徊, 學一段時間, 工作一忙, 再撿起來隔段時間又忘了.思來想去, 趁著這兩個月加班不是特別多, 就決定用 仿寫一個完整項目. 花田小憩:是一個植物美學生活平臺, 以自然生活為主導, 提倡植物學生活方法, 倡導美學標準的生活態度 ...
  • 一、簡介 NSSet到底什麼類型,其實它和NSArray功能性質一樣,用於存儲對象,屬於集合; NSSet , NSMutableSet類聲明編程介面對象,無序的集合,在記憶體中存儲方式是不連續的,不像NSArray(是有序的集合)類聲明編程介面對象是有序集合,在記憶體中存儲位置是連續的; NSSet和 ...
  • BadgeValueView 效果 源碼 https://github.com/YouXianMing/UI-Component-Collection 中的 BadgeValueView ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...