Linux 0.11 內核解析:中斷相關(1)asm.s文件中斷處理分析

0 源代碼

有兩個版本的,一個是帶中文注釋,Intel格式的;一個是不帶注釋是AT&T格式的。

Linux 0.11 中文注釋版
Linux 0.11 源碼,基于《Linux內核完全注釋》趙炯

1 asm.s 文件

我們先假設該文件處理的中斷是無特權過渡的情況(具體是不是暫時不知道)。

我們看一下,當中斷發生且被檢測到之后,硬件做了什么。
在這里插入圖片描述
我們可以看到,中斷被檢測到之后,一些必要的信息被壓棧了。
在這里插入圖片描述

在80386手冊中提到,中斷發生之后,一些必要的,為了中斷程序結束之后返回原程序繼續執行的信息,會被壓棧,就像最開始那個圖看到的內樣,這里分成了4種情況,我們只討論無特權轉換的兩種情況,分別輸有出錯碼的異常和無處錯碼的異常。

這個壓棧過程是硬件完成的,回顧一下我們之前的知識
在這里插入圖片描述

在中斷發生之后,80386執行了

  1. 保存斷點(將EFLAGS,CS,EIP依次壓棧)
  2. 識別中斷源

至于怎么通過中斷號,找到對應的中斷服務程序,我們后面說。

2 無處錯碼中斷

源代碼在kernel/asm.s文件中

_divide_error:  # int0 除法錯中斷,中斷錯誤碼是0pushl $_do_divide_error  # 用于打印除法錯中斷的錯誤信息的函數,在traps.c實現# 中斷處理程序的地址入棧
no_error_code:# 保護現場xchgl %eax,(%esp) # eax的值入棧,eax保存中斷處理程序地址pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %ds 		# 占4字節而非2字節push %espush %fspushl $0		# "error code" 中斷有無錯誤碼,是有相關規定的lea 44(%esp),%edxpushl %edx# 設置內核數據段選擇符,設置好數據尋址的基址movl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax	# 執行中斷處理程序,C語言函數do_divide_error# 恢復現場addl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret # 返回中斷處理之前的程序,繼續執行后續指令_debug:pushl $_do_int3		# _do_debugjmp no_error_code

我們通過畫堆棧內存圖的方式分析一下這個過程。

2.1 檢測中斷后,硬件完成的事情

硬件完成的事情其實就是壓棧,壓棧之后的內存圖是:
在這里插入圖片描述

2.2 進入中斷服務程序

在識別到中斷源之后,通過某種方式,CPU獲取的中斷服務程序的CS:EIP,進入了終端服務程序,我們這里以debug為例子。

注意,所有的無出錯號的中斷,默認當成出錯號是0,會在保護現場的時候入棧,這個其實是配合實際出錯號是0的除法錯中斷,先不管它。

pushl $_do_int3		# _do_debug 中斷服務程序主體,中斷處理程序
jmp no_error_code

執行完這兩條指令,內存分布是:
在這里插入圖片描述
然后就跳轉到了no_error_code

它主要分成三大部分

  1. 保護現場
  2. 執行中斷處理程序
  3. 恢復現場和返回原程序

下面是保護現場的部分

# 保護現場xchgl %eax,(%esp) # eax的值入棧,eax保存中斷處理程序地址pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %ds 		# 占4字節而非2字節push %espush %fspushl $0		# "error code" 中斷有無錯誤碼,是有相關規定的lea 44(%esp),%edxpushl %edx

然后是執行中斷處理程序的部分,會有數據段選擇符是設定,先暫時不管,這個其實就是類似于8086設置ds的值。

	# 設置內核數據段選擇符,設置好數據尋址的基址movl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax	# 執行中斷處理程序,C語言函數do_debug

緊接著是恢復現場和返回原程序

# 恢復現場addl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret # 返回中斷處理之前的程序,繼續執行后續指令

這里需要補充

iret的作用

中斷門或陷阱門中斷服務例程的返回,按照之前入棧的相反順序將EIP、CS先后出棧;再將EFLAGS標志寄存器出棧,恢復現場(IRET指令)。

簡而言之,就是把最開始保存斷點的部分依次出棧,從而回到源程序。

我們看一下這個過程的內存分布
在這里插入圖片描述

其中

  • esp0對應push $0
  • esp1對應pushl %edx
  • esp2對應addl $8,%esp
  • esp3對應popl %eax

還有一個比較值得關心的

call指令執行前后,寄存器的變化

這一點,使用C語言分析一下函數調用,看看前后變化,在學堂在線《Linux內核分析》中科大孟寧老師MOOC講解很清晰。


后面的內些的無處錯碼的函數,與上面的例子完全一樣

_debug:pushl $_do_int3		# _do_debugjmp no_error_code_nmi:pushl $_do_nmijmp no_error_code_int3:pushl $_do_int3jmp no_error_code_overflow:pushl $_do_overflowjmp no_error_code_bounds:pushl $_do_boundsjmp no_error_code_invalid_op:pushl $_do_invalid_opjmp no_error_code_coprocessor_segment_overrun:pushl $_do_coprocessor_segment_overrunjmp no_error_code_reserved:pushl $_do_reservedjmp no_error_code_irq13:pushl %eaxxorb %al,%aloutb %al,$0xF0movb $0x20,%aloutb %al,$0x20jmp 1f
1:	jmp 1f
1:	outb %al,$0xA0popl %eaxjmp _coprocessor_error

這些都一樣,其中比較特別的是,Linux0.11實現了幾個用戶自定義中斷_irq13就是int45

2.2.1 其他細節的說明

這里保存中斷號0,以及保存最開始的堆棧地址esp的值,似乎沒啥用,以后再說吧。

這里比較奇怪的是,除法零中斷,它的錯誤碼是0,使用的是無處錯碼的處理程序,如果錯誤碼在之前就被壓入棧的話,iret指令執行之前,指向的應該是error code 0,而不是eip,除非這個除法零中斷的處理比較特殊,不將出錯碼壓棧,與其他無處錯碼的中斷等同對待,才比較合理。以后再看看這個細節,暫時先放放。

3 有出錯碼中斷

結構與無處錯碼中斷基本一致,這里簡要說明一下區別。

首先,硬件處理有區別,壓棧之后,會把出錯碼壓棧。

在這里插入圖片描述
然后,在保存現場的處理上,有一些不同。

_double_fault:pushl $_do_double_fault
error_code:xchgl %eax,4(%esp)		# error code <-> %eaxxchgl %ebx,(%esp)		# &function <-> %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fspushl %eax			# error codelea 44(%esp),%eax		# offsetpushl %eaxmovl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fscall *%ebxaddl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret

看最開始的指令
在這里插入圖片描述
上圖是將中斷處理函數壓棧之后。

然后執行

xchgl %eax,4(%esp)		# error code <-> %eax
xchgl %ebx,(%esp)		# &function <-> %ebx

分別將

  1. eax與棧內的error code交換,eax入棧
  2. ebx保存中斷處理函數地址,ebx入棧

然后,在壓入錯誤碼的時候,壓入的是pushl %eax # error code,這個時候error code就是動態的了,取決于之前硬件壓入的錯誤碼是多少,所以,之前的除法零是靜態的0,極大概率可能不壓入錯誤碼,當成固定機制處理,也可能與兼容性有關。

然后就是調用中斷處理函數的時候,執行call *%ebx,因為這次是ebx存放函數地址。

其他的就沒有差別了,需要注意的是,error code最開始就被替換下來了,在后面才壓棧。

錯誤碼壓棧的作用

好吧,看起來錯誤碼壓棧之后,好像根本沒有使用就略過了啊,肯定不會的,我們看看手冊。
在這里插入圖片描述

某種類型的異常也導致錯誤碼壓棧。異常處理器也能夠使用錯誤碼幫助檢測異常。

這一點說明了

  1. 錯誤碼的功能:幫助檢查異常
  2. 某些有錯誤碼的異常會壓棧,不是所有的都會把錯誤碼壓棧,比如除法零錯誤,很可能就不壓棧

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

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

相關文章

【精華文】C語言結構體特殊情況分析:結構體指針 / 基本數據類型指針,指向其他結構體

參考鏈接&#xff1a;Structure pointer pointing to different structure instance 注&#xff1a;可以查看此篇的問題和唯一的回復&#xff0c;那是相對正確的&#xff0c;不要看comment&#xff0c;有很多錯誤。 我是拒絕分析這種問題的&#xff0c;因為似乎沒有人會這么亂用…

enum in c language

今天說說C語言中的枚舉。 參考&#xff1a;Enumeration (or enum) in C 1 定義 定義一個枚舉類型很容易&#xff1a; enum aa { a1, a2, a3 };這里 enum是關鍵字aa是枚舉變量&#xff0c;也就是我們自定義類型a1,a2,a3是枚舉成員 然后怎么使用呢&#xff1f; 首先&#…

信號量SIGCHLD的使用,如何讓父進程得知子進程執行結束,如何讓父進程區分多個子進程的結束

本教程基于 Ubuntu 20.10 gcc 10.2.0. 示例程序如果不能正常編譯和執行&#xff0c;說明您系統和工具版本與我的不匹配&#xff0c;請自行查閱資料。 0 概述 先給出該信號的描述&#xff1a; SignalValueDescriptionSIGCHLD17Child status has changed (POSIX). Signal sent …

使用gdb調試多進程程序、同時調試父進程和子進程

參考: [1] GDB debugging multi-process programs [2] Debugging programs with multiple processes 根據這兩篇參考鏈接&#xff0c;完全可以實現使用gdb同時調試父進程和子進程。 接下來說明一下可能遇到的坑 gdb8.1版本有bug&#xff0c;設置完set detach-fork-on off&…

Linux安裝Ncurses庫

參考&#xff1a;How To Install Ncurses Library In Linux 針對Ubuntu說明一下&#xff1a; wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.2.tar.gz&#xff0c;至于最新版本&#xff0c;自己看官網&#xff0c;修改一下版本號即可。tar xzf ncurses-6.2.tar.gzcd nc…

gdb 10.2的安裝

參考 [1] GDB-10.2 [2] README for GDB release 個人系統 Ubuntu20.10。 注意gdb10.2需要c11語法&#xff0c;需要安裝g 下載安裝包wget https://ftp.gnu.org/gnu/gdb/gdb-10.2.tar.xz解壓縮tar -xvzf gdb-10.2.tar.xz進入解壓之后的目錄mkdir buildcd build配置&#xff0c;…

gdb tui的使用

[1] GDB Text User Interface [2] GDB Text User Interface 簡單來說&#xff0c;進入gdb之后&#xff0c;使用ctrl x 2就足夠了。其他細節請參考上述鏈接&#xff0c;選一個就可以。

C語言中信號函數(signal)的使用

先來簡單談談C語言中的信號&#xff08;signal&#xff09; 首先&#xff0c;signal是C語言庫中的函數&#xff0c;它實際上是軟中斷&#xff0c;也就是軟件發出的終端&#xff0c;本質來說&#xff0c;類似于int n。 對于接收到該軟中斷信號的進程&#xff0c;就會停下手頭的…

UNIX哲學

參考&#xff1a; 對比Linux與Windows 使用Linux想要做某些事情的時候&#xff0c;就拆開想&#xff0c;想想我需要哪些功能&#xff0c;需要哪些工具&#xff0c;依次怎么執行&#xff0c;然后用管道建立連接&#xff0c;讓數據依次流過不同的工具&#xff0c;從而得到最終結果…

fork創建多個子進程

references: [1] how to create two processes from a single Parent [2] fork() in C [3] linux中fork同時創建多個子進程的方法 fork的本質&#xff0c;就是復制&#xff0c;把當前進程復制一份&#xff0c;然后兩個進程并發地執行fork后面的語句&#xff0c;區別就是&#x…

wait系統調用

reference:Wait System Call in C 只強調幾點&#xff0c;剩下的直接看參考鏈接內容就好了&#xff0c;不是偷懶&#xff0c;而是里面內容寫的很好了&#xff0c;沒必要再寫一遍了&#xff0c;這種東西就是單純的系統調用而已&#xff0c;理解了功能&#xff0c;就完事了&#…

Linux進程間通信:共享內存與管道

references: [1] IPC through shared memory [2] Inter Process Communication (IPC) [3] https://www.geeksforgeeks.org/pipe-system-call/ [4] watch command in Linux with Examples 參考鏈接1和2是介紹了共享內存IPC的簡單原理和相關系統調用的使用參考鏈接3是介紹了管道通…

find command基本使用

find命令通常用于根據文件名查找文件&#xff0c;這是最基本用法。 find [path] -name/-iname [filename] path寫要查找的路徑&#xff0c;自動遞歸查找filename寫文件名&#xff0c;可以使用通配符*還有其他什么的表達式 具體細節請man find查閱文檔。

正則表達式特別需要注意的點:“空“字符的匹配

在正則表達式中&#xff0c;[...]代表1個字符&#xff0c;不管里面有多少字符&#xff0c;最終這個東西的結果都是1個字符。 對于表達式[^a]表達的匹配除了a之外的字符&#xff0c;并且是1個字符。 需要注意的是&#xff0c;有些特殊字符是不會被匹配的。 我們看一個示例&am…

vim多列操作--插入/刪除

插入 How to insert text at beginning of a multi-line selection in vi/VimVim Commands 刪除 ctrl v使用上下左右鍵選中一片區域按d刪除

vim進行行內某部分的復制剪切粘貼

ctrl v使用方向鍵選中你要復制的部分 按d&#xff08;剪切&#xff09;或者按y&#xff08;復制&#xff09;再移動到你的目標位置&#xff0c;按p粘貼&#xff08;在正常模式下才行&#xff0c;如果不是&#xff0c;先按esc&#xff09; 這個過程與你操作word文檔的復制粘貼…

函數調用堆棧

基于孟寧老師的Linux內核分析 1 int g(int x){ 2 int y x 3;3 return y;4 }5 6 int f(int x){7 int z x 10;8 return g(z);9 }10 11 int main(){12 int a f(8) 1;13 return 0;14…

gdb調試的幾點提示(1)

GDB debugger Examining Memory Continuing and Stepping How to translate a virtual memory address to a physical address? s和n是C語言的下一步 si和ni是匯編語言下一步 gdb能夠查看的都是虛擬地址&#xff0c;不能查看物理地址&#xff0c;應用程序都不能查看物理地址…

C語言讀取文件

C語言一次性讀取文件 C - File I/O C library function - fread() 需要注意的點 fgets函數&#xff0c;一次只能讀取一行&#xff0c;并且在結尾自動添加\0fread函數&#xff0c;可以讀取很多內容&#xff0c;但是不會添加\0需要手動完成&#xff0c;具體看[參考1]

Vivado提高綜合和實現的速度

讓計算機的資源盡可能給vivado&#xff0c;綜合、實現的時候修改一個參數 jobs改為你的計算機的最大值&#xff0c;我的計算機是12核的。 速度會快很多&#xff01;