X86下的8259A子系統

X86下的8259A子系統

聲明

本文會用盡量樸素非術語的語言整體介紹x86架構下中斷處理的大概過程,希望能夠幫助讀者對PC的工作模式有一個簡單的形象化的認識。
爲了幫助理解,示意圖經過簡化後不能保證完全正確。

背景

在描述8259A的工作方式之前,我們先明確幾個基礎的前提

  • cpu的工作方式是“取址->執行”,每執行一條指令,IP(Instruction Pointer)指令寄存器會自動移動到下一條指令在這裏插入圖片描述
    在這裏插入圖片描述
  • 有的時候cpu執行的指令可能是一個io指令,比如讀寫磁盤,這個工作要磁盤來完成,比如這裏的一個out指令,向磁盤控制器發出一個信號
    在這裏插入圖片描述
  • 在沒有其它部件協助的情況下,我們的程序爲了獲得磁盤讀取/寫入數據的結果,必須寫一個while循環去不斷詢問磁盤是否執行成功,這明顯是非常浪費cpu的寶貴時間的
    在這裏插入圖片描述
  • 於是我們想,能不能有一種機制使得cpu不需要去不斷詢問磁盤的狀態,當磁盤執行完成之後主動通知cpu,這時候cpu再做接下來的處理,在此之前,cpu可以繼續做其他事情
    在這裏插入圖片描述
  • 於是人們設計了一個叫PIC(Programmable Interrupt Controller)可編程中斷控制器 的東西來解決cpu輪詢的問題,在磁盤處理完任務後通過一個電信號通知PIC芯片,PIC芯片再告知cpu磁盤讀寫成功,這時cpu會放下手中處理的事情,來處理磁盤讀寫完成的收尾工作,再處理完收尾工作之後又回到剛纔手頭上的工作上去。這樣的工程就是我們常說的中斷機制。
    在這裏插入圖片描述
  • 這時讀者可能會問,磁盤爲什麼不能直接告訴cpu我已經處理完了,而是要通過一個PIC芯片間接的告訴cpu這件事情呢,原因是和cpu協同工作的硬件不止磁盤一個,還有鍵盤,鼠標,各種協處理設備,這每個設備都與PIC的引腳相連,每個設備通知PIC自己有要通知cpu的消息的這個動作稱爲中斷請求
  • 一般的X86架構所在的主板上的PIC是由兩塊能接收8位信號的8259A芯片及聯而成的,所以一共能接受16種設備中斷,而cpu是如何區分中斷請求是由哪個設備發出的呢

8259A

協同工作

在這裏插入圖片描述
在一般運行的狀態下cpu不斷執行正常代碼,每執行一條指令,IP(Instruction Pointer)指令寄存器會自動移動到下一條指令,外部設備與PIC的IR0-IR5引腳相連
在這裏插入圖片描述
CPU的INTR引腳提供給外部電路一個功能,當外部電路給INTR一個信號的時候,cpu會在執行完當前IP指向的指令之後,從數據總線上取一箇中斷號,這個中斷號是PIC在向CPU發送中斷信號的時候放在數據總線上的,通過這個中斷號,cpu可以區分不同的設備請求,從而進入不同的中斷處理程序
在這裏插入圖片描述
PIC除了能告訴cpu是哪個設備發出的中斷之外,還能夠判斷中斷來臨的優先級,比如在一箇中斷請求正在被cpu執行的時候,PIC會記錄這個請求的是誰,當又有一箇中斷髮生的時候,PIC會比較這兩個請求誰的優先級更高,如果新來的請求優先級更高,則PIC向cpu發出一箇中斷信號,cpu從原來的中斷處理程序中被中斷出來,執行新的中斷處理程序,當執行完新的中斷處理程序之後,再返回原來的中斷處理程序
另外PIC還有能力屏蔽一些外部中斷請求
而cpu也可個通過設置自己的EFLAGS寄存器中的一個標誌位來屏蔽INTR引腳上由PIC傳來的信號,在老版本的linux代碼中我們可以看到cli sti這樣的指令來臨時關閉和打開中斷功能來實現臨界區的功能

Intel保留中斷號

在這裏插入圖片描述
轉自《linux內核源碼剖析》

  • 如上圖可以看到,Intel的cpu在遇到各種故障的時候會給自己發出各種中斷號,cpu收到這些中斷號所產生的行爲和從PIC收到中斷號的行爲是一致的,都是要找相應的中斷處理程序來收尾

  • 比如當一個用戶的程序除以了0,cpu就會給自己發出一箇中斷信號,並且中斷號爲0,這是如果我們給0註冊了一箇中斷處理程序,我們就能捕獲這個信息並且在屏幕上顯示一些文本告訴用戶錯誤

  • 所以cpu正常的工作場景應該是這樣的

    • cpu不斷的取址執行

    • 當有硬件中斷的時候處理相應的收尾工作,比如接收磁盤發來的數據,接收網卡發來的數據

    • 當有自己程序發生錯誤的時候進入錯誤處理的中斷處理程序,以比較優雅的方式通知用戶,並關閉用戶進程

    • 中斷處理程序在內存中應該這樣排列

      • 0-31 留給cpu自己會產生的中斷
      • 32開始往後可以選16箇中斷號給硬件

豬隊友IBM

  • 8259A是可編程芯片,就是說我們可個在芯片初始化的時候規定它的工作行爲,這裏面最重要的是規定引腳接收到硬件傳來的信號後要放在數據總線上的中斷號
  • IBM在自己出場的BIOS中對8259A做了一次初始化,把許多硬件中斷號設置成了從0x08開始,這就與intel的保留的功能相沖突了
  • 如果我們不重新初始化8259A會發生什麼情況呢,那就是cpu收到了一箇中斷號,可能是由硬件產生的,也可能是由cpu產生的。這時候cpu就沒法做收尾工作了

Linux中的處理

以下代碼摘自linux0.12源碼中的setup.s文件,這段代碼在初始化兩塊8259A,

mov	al,#0x20		! start of hardware int's (0x20)
mov	al,#0x28		! start of hardware int's 2 (0x28)

我們能看到這兩句話在重置硬件中斷的中斷號

! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.

mov	al,#0x11		! initialization sequence
out	#0x20,al		! send it to 8259A-1
.word	0x00eb,0x00eb		! jmp $+2, jmp $+2
out	#0xA0,al		! and to 8259A-2
.word	0x00eb,0x00eb
mov	al,#0x20		! start of hardware int's (0x20)
out	#0x21,al
.word	0x00eb,0x00eb
mov	al,#0x28		! start of hardware int's 2 (0x28)
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0x04		! 8259-1 is master
out	#0x21,al
.word	0x00eb,0x00eb
mov	al,#0x02		! 8259-2 is slave
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0x01		! 8086 mode for both
out	#0x21,al
.word	0x00eb,0x00eb
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0xFF		! mask off all interrupts for now
out	#0x21,al
.word	0x00eb,0x00eb
out	#0xA1,al

! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
發佈了6 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章