多余的读写端口什么时候会对程序造成影响_程序员需要了解的硬核知识之控制硬件...
應(yīng)用和硬件的關(guān)系
我們作為程序員一般很少直接操控硬件,我們一般通過(guò) C、Java 等高級(jí)語(yǔ)言編寫(xiě)的程序起到間接控制硬件的作用。所以大家很少直接接觸到硬件的指令,硬件的控制是由 Windows 操作系統(tǒng) 全權(quán)負(fù)責(zé)的。
你一定猜到我要說(shuō)什么了,沒(méi)錯(cuò),我會(huì)說(shuō)但是,任何事情沒(méi)有絕對(duì)性,環(huán)境的不同會(huì)造成結(jié)果的偏差。雖然程序員沒(méi)法直接控制硬件,并且 Windows 屏蔽了控制硬件的細(xì)節(jié),但是 Windows 卻為你開(kāi)放了 系統(tǒng)調(diào)用功能來(lái)實(shí)現(xiàn)對(duì)硬件的控制。在 Windows 中,系統(tǒng)調(diào)用稱為 API,API 就是應(yīng)用調(diào)用的函數(shù),這些函數(shù)的實(shí)體被存放在 DLL 文件中。
下面我們來(lái)看一個(gè)通過(guò)系統(tǒng)調(diào)用來(lái)間接控制硬件的實(shí)例
假如要在窗口中顯示字符串,就可以使用 Windows API 中的 TextOut 函數(shù)。TextOut 函數(shù)的語(yǔ)法(C 語(yǔ)言)如下
BOOL TextOut{ HDC hdc, // 設(shè)備描述表的句柄 int nXStart, // 顯示字符串的 x 坐標(biāo) int nYStart, // 顯示字符串的 y 坐標(biāo) LPCTSTR lpString, // 指向字符串的指針 int cbString // 字符串的文字?jǐn)?shù) }
那么,在處理 TextOut 函數(shù)的內(nèi)容時(shí),Windows 做了些什么呢?從結(jié)果來(lái)看,Windows 直接控制了作為硬件的顯示器。但 Windows 本身也是軟件,由此可見(jiàn),Windows 應(yīng)該向 CPU 傳遞了某種指令,從而通過(guò)軟件控制了硬件。
Windows 提供的 TextOut 函數(shù) API 可以向窗口和打印機(jī)輸出字符。C 語(yǔ)言提供的 printf 函數(shù),是用來(lái)在命令提示符中顯示字符串的函數(shù)。使用 printf 函數(shù)是無(wú)法向打印機(jī)輸出字符的。支持硬件輸入輸出的 IN 指令和 OUT 指令
Windows 控制硬件借助的是輸入和輸出指令。其中具有代表性的兩個(gè)輸入輸出指令就是 IN 和 OUT指令。這些指令也是匯編語(yǔ)言的助記符。
可以通過(guò) IN 和 OUT 指令來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的讀入和輸出,如下圖所示
也就是說(shuō),IN 指令通過(guò)指定的端口號(hào)輸入數(shù)據(jù),OUT 指令則是把 CPU 寄存器中存儲(chǔ)的數(shù)據(jù)輸出到指定端口號(hào)的端口。
那么這個(gè)端口號(hào) 和 端口是什么呢?你感覺(jué)它像不像港口一樣?通過(guò)標(biāo)注哪個(gè)港口然后進(jìn)行貨物的運(yùn)送和運(yùn)出?
下面我們來(lái)看一下官方是如何定義端口號(hào)和端口的
還記得計(jì)算機(jī)組成原理中計(jì)算機(jī)的五大組成部分嗎,再來(lái)回顧一下:運(yùn)算器、控制器、存儲(chǔ)器、輸入設(shè)備和輸出設(shè)備。我們今天不談前三個(gè),就說(shuō)說(shuō)后面兩個(gè)輸入設(shè)備和輸出設(shè)備,這兩個(gè)與我們本節(jié)主題息息相關(guān)。
那么問(wèn)題來(lái)了,IO設(shè)備如何實(shí)現(xiàn)輸入和輸出的呢?計(jì)算機(jī)主機(jī)中,附帶了用來(lái)連接顯示器以及鍵盤(pán)等外圍設(shè)備的連接器。 而連接器的內(nèi)部,都連接有用來(lái)交換計(jì)算機(jī)主機(jī)同外圍設(shè)備之間電流特性的 IC。如果 IC 你不明白是什么的話,可以參考作者的文章 程序員需要了解的硬核知識(shí)之內(nèi)存 進(jìn)行了解。這些 IC 統(tǒng)稱為 IO 控制器。
IO 是 Input/Output 的縮寫(xiě)。顯示器、鍵盤(pán)等外圍設(shè)備都有各自專用的 I/O 控制器。I/O 控制器中有用于臨時(shí)保存輸入輸出數(shù)據(jù)的內(nèi)存。這個(gè)內(nèi)存就是 端口(port)。端口你就可以把它理解為我們上述說(shuō)的 港口。IO 控制器內(nèi)部的內(nèi)存,也被稱為寄存器,不要慌,這個(gè)寄存器和內(nèi)存中的寄存器不一樣。CPU 內(nèi)存的寄存器是用于進(jìn)行數(shù)據(jù)運(yùn)算處理的,而IO中的寄存器是用于臨時(shí)存儲(chǔ)數(shù)據(jù)的。
在 I/O 設(shè)備內(nèi)部的 IC 中,有多個(gè)端口。由于計(jì)算機(jī)中連接著很多外圍設(shè)備,因此也就有很多 I/O 控制器。當(dāng)然也會(huì)有多個(gè)端口,一個(gè) I/O 控制器可以控制多個(gè)設(shè)備,不僅僅只能控制一個(gè)。各端口之間通過(guò) 端口號(hào) 進(jìn)行區(qū)分。
端口號(hào)也被稱為 I/O地址 。IN 指令和 OUT 指令在端口號(hào)指定的端口和 CPU 之間進(jìn)行數(shù)據(jù)的輸入和輸出。這跟通過(guò)內(nèi)存的地址來(lái)對(duì)內(nèi)存進(jìn)行讀寫(xiě)是一樣的道理。
測(cè)試輸入和輸出程序
首先讓我們利用 IN 指令和 OUT 指令,來(lái)進(jìn)行一個(gè)直接控制硬件的實(shí)驗(yàn)。假如試驗(yàn)的目的是讓一個(gè)計(jì)算機(jī)內(nèi)置的喇叭(蜂鳴器)發(fā)出聲音。蜂鳴器封裝在計(jì)算機(jī)內(nèi)部,但它也是外圍設(shè)備的一種。
用匯編語(yǔ)言比較繁瑣,這次我們用 C 語(yǔ)言來(lái)實(shí)現(xiàn)。在大部分 C 語(yǔ)言的處理(編譯器的種類)中,只要使用 _asm{ 和 }括起來(lái),就可以在其中記述助記符。也就是說(shuō),采用這種方式就能夠使用 C 語(yǔ)言和匯編語(yǔ)言混合的源代碼。
在 AT 兼容機(jī)中,蜂鳴器的默認(rèn)端口號(hào)是 61H ,末尾的 H 表示的是十六進(jìn)制數(shù)的意思。用 IN 指令通過(guò)該端口號(hào)輸入數(shù)據(jù),并將數(shù)據(jù)的低2位設(shè)定為 ON,然后再通過(guò)該端口號(hào)用 OUT 指令輸出數(shù)據(jù),這時(shí)蜂鳴器就會(huì)發(fā)出聲音。同樣的方法,將數(shù)據(jù)的低2位設(shè)定為 OFF 并輸出后,蜂鳴器就停止工作。
位設(shè)定為 ON 指的是將該位設(shè)定為1,位設(shè)定為 OFF 指的是將該位設(shè)定為0 。把位設(shè)定為 ON,只需要把想要設(shè)定為 ON 的位設(shè)定為1,其他位設(shè)定為0后進(jìn)行 OR 運(yùn)算即可。由于這里需要把低2位置為1,因此就是和 03H 進(jìn)行 OR 運(yùn)算。03H 用8為二進(jìn)制來(lái)表示的話是 00000011。由于即便高6位存在著具體意義。和0進(jìn)行OR運(yùn)算后也不會(huì)發(fā)生變化,因而就和 03H 進(jìn)行 OR 運(yùn)算。把位設(shè)定為 OFF,只需要把想要置 OFF 的位設(shè)定為0,其他位設(shè)定為1后進(jìn)行 AND 運(yùn)算即可。由于這里需要把低2位設(shè)定為0,因此就要和 FCH 進(jìn)行 AND 運(yùn)算。在源代碼中,FCH 是用 0FCH 來(lái)記述的。在前面加 0 是匯編語(yǔ)言的規(guī)定,表示的是以 A - F 這些字符開(kāi)頭的十六進(jìn)制數(shù)是數(shù)值的意思。0FCH 用8位二進(jìn)制數(shù)來(lái)表示的話是 11111100。由于即便高6位存在著具體意義,和1進(jìn)行 AND 運(yùn)算后也不會(huì)產(chǎn)生變化,因而就是同 0FCH 進(jìn)行 OR 運(yùn)算。
void main(){ // 計(jì)數(shù)器 int i; // 蜂鳴器發(fā)聲 _asm{ IN EAX, 61H OR EAX, 03H OUT 61H, EAX } // 等待一段時(shí)間 for(i = 0;i < 1000000;i++); // 蜂鳴器停止發(fā)生 _asm{ IN EAX, 61H AND EAX, 0FCH OUT 61H, EAX } }
我們對(duì)上面的代碼進(jìn)行說(shuō)明,main 是 C 語(yǔ)言程序起始位置的函數(shù)。在該函數(shù)中,有兩個(gè)用 _asm{} 圍起來(lái)的部分,它們中間有一個(gè)使用 for 循環(huán)的空循環(huán)
首先是蜂鳴器發(fā)聲的部分,通過(guò) IN EAX,61H(助記符不區(qū)分大小寫(xiě))指令,把端口 61H 的數(shù)據(jù)存儲(chǔ)到 CPU 的 EAX 寄存器中。接下來(lái),通過(guò) OR EAX,03H 指令,把 EAX 寄存器的低2位設(shè)定成 ON。最后,通過(guò) OUT 61H,EAX 指令,把 EAX 寄存器的內(nèi)容輸出到61端口。使蜂鳴器開(kāi)始發(fā)音。雖然 EAX 寄存器的長(zhǎng)度是 32 位,不過(guò)由于蜂鳴器端口是8位,所以只需對(duì)下8位進(jìn)行OR運(yùn)算和AND運(yùn)算就可以正常工作了。
其次是一個(gè)重復(fù)100次的空循環(huán),主要是為了在蜂鳴器開(kāi)始發(fā)音和停止發(fā)音之間稍微加上一些時(shí)間間隔。因?yàn)楝F(xiàn)在計(jì)算機(jī)器的運(yùn)行速度非常快,哪怕是 100 萬(wàn)次循環(huán),也幾乎是瞬時(shí)間完成的。
然后是用來(lái)控制器蜂鳴器停止發(fā)聲的部分。首先,通過(guò) IN EAX,61H 指令,把端口 61H 的數(shù)據(jù)存儲(chǔ)到 CPU 的 EAX 寄存器中。接下來(lái),通過(guò) AND EAX,0FCH 指令,把 EAX 寄存器的低2位設(shè)定為 OFF。最后,通過(guò) OUT 61H,EAX 指令,把寄存器的 EAX 內(nèi)容輸出到61號(hào)端口,使蜂鳴器停止發(fā)音。外圍設(shè)備的中斷請(qǐng)求IRQ(Interrupt Request) 代表的就是中斷請(qǐng)求。IRQ 用來(lái)暫停當(dāng)前正在運(yùn)行的程序,并跳轉(zhuǎn)到其他程序運(yùn)行的必要機(jī)制。該機(jī)制被稱為 處理中斷。中斷處理在硬件控制中擔(dān)當(dāng)著重要的角色。因?yàn)槿绻麤](méi)有中斷處理,就有可能無(wú)法順暢進(jìn)行處理的情況。
從中斷處理開(kāi)始到請(qǐng)求中斷的程序(中斷處理程序)運(yùn)行結(jié)束之前,被中斷的程序(主程序)的處理是停止的。這種情況就類似于在處理文檔的過(guò)程中有電話打進(jìn)來(lái),電話就相當(dāng)于是中斷處理。假如沒(méi)有中斷處理的發(fā)生,就必須等到文檔處理完成后才能夠接聽(tīng)電話。由此可見(jiàn),中斷處理有著巨大的價(jià)值,就像是接聽(tīng)完電話后會(huì)返回原來(lái)的文檔作業(yè)一樣,中斷程序處理完成后,也會(huì)返回到主程序中繼續(xù)。
實(shí)施中斷請(qǐng)求的是連接外圍設(shè)備的 I/O 控制器,負(fù)責(zé)實(shí)施中斷處理的是 CPU,外圍設(shè)備的中斷請(qǐng)求會(huì)使用不同于 I/O 端口的其他編號(hào),該編號(hào)稱為中斷編號(hào)。在控制面板中查看軟盤(pán)驅(qū)動(dòng)器的屬性時(shí),IRQ處現(xiàn)實(shí)的數(shù)值是 06,表示的就是用06號(hào)來(lái)識(shí)別軟盤(pán)驅(qū)動(dòng)器發(fā)出的請(qǐng)求。還有就是操作系統(tǒng)以及 BIOS 則會(huì)提供響應(yīng)中斷編號(hào)的中斷處理程序。
BIOS(Basic Input Output System): 位于計(jì)算機(jī)主板或者擴(kuò)張卡上內(nèi)置的 ROM 中,里面記錄了用來(lái)控制外圍設(shè)備的程序和數(shù)據(jù)。
假如有多個(gè)外圍設(shè)備進(jìn)行中斷請(qǐng)求的話, CPU 需要做出選擇進(jìn)行處理,為此,我們可以在 I/O 控制器和 CPU 中間加入名為中斷控制器的 IC 來(lái)進(jìn)行緩沖。中斷控制器會(huì)把從多個(gè)外圍設(shè)備發(fā)出的中斷請(qǐng)求有序的傳遞給 CPU。中斷控制器的功能相當(dāng)于就是緩沖。下面是中斷控制器功能的示意圖
CPU 在接受到中斷請(qǐng)求后,會(huì)把當(dāng)前正在運(yùn)行的任務(wù)中斷,并切換到中斷處理程序。中斷處理程序的第一步處理,就是把 CPU 所有寄存器的數(shù)值保存到內(nèi)存的棧中。在中斷處理程序中完成外圍設(shè)備的輸入和輸出后,把棧中保存的數(shù)值還原到 CPU 寄存器中,然后再繼續(xù)進(jìn)行對(duì)主程序的處理。
假如 CPU 寄存器數(shù)值還沒(méi)有還原的話,就會(huì)影響到主程序的運(yùn)行,甚至還有可能會(huì)使程序意外停止或發(fā)生運(yùn)行時(shí)異常。這是因?yàn)橹鞒绦蛟谶\(yùn)行過(guò)程中,會(huì)用到 CPU 寄存器進(jìn)行處理,這時(shí)候如果突然插入其他程序的運(yùn)行結(jié)果,此時(shí) CPU 必然會(huì)受到影響。所以,在處理完中斷請(qǐng)求后,各個(gè)寄存器的值必須要還原。只要寄存器的值保持不變,主程序就可以像沒(méi)有發(fā)生過(guò)任何事情一樣繼續(xù)處理。
用中斷來(lái)實(shí)現(xiàn)實(shí)時(shí)處理
中斷是指計(jì)算機(jī)運(yùn)行過(guò)程中,出現(xiàn)某些意外情況需主機(jī)干預(yù)時(shí),機(jī)器能自動(dòng)停止正在運(yùn)行的程序并轉(zhuǎn)入處理新情況的程序,處理完畢后又返回原被暫停的程序繼續(xù)運(yùn)行。
在程序的運(yùn)行過(guò)程中,幾乎無(wú)時(shí)無(wú)刻都會(huì)發(fā)生中斷,其原因就是為了實(shí)時(shí)處理外部輸入的數(shù)據(jù),雖然程序也可以在不會(huì)中斷的基礎(chǔ)上處理外部數(shù)據(jù),但是那種情況下,主程序就會(huì)頻繁的檢查外圍設(shè)備是否會(huì)有數(shù)據(jù)輸入。由于外圍設(shè)備會(huì)有很多個(gè),因此有必要按照順序來(lái)調(diào)查。按照順序檢查多個(gè)外圍設(shè)備的狀態(tài)稱為 輪詢。對(duì)于計(jì)算機(jī)來(lái)說(shuō),這種采用輪詢的方式不是很合理,如果你正在檢查是否有鼠標(biāo)輸入,這時(shí)候發(fā)生了鍵盤(pán)輸入該如何處理呢?結(jié)果必定會(huì)導(dǎo)致文字的實(shí)時(shí)處理效率。所以即時(shí)的中斷能夠提高程序的運(yùn)行效率。
上面只是中斷的一種好處,下面匯總一下利用中斷能夠帶來(lái)的正面影響
- 提高計(jì)算機(jī)系統(tǒng)效率。計(jì)算機(jī)系統(tǒng)中處理機(jī)的工作速度遠(yuǎn)高于外圍設(shè)備的工作速度。通過(guò)中斷可以協(xié)調(diào)它們之間的工作。當(dāng)外圍設(shè)備需要與處理機(jī)交換信息時(shí),由外圍設(shè)備向處理機(jī)發(fā)出中斷請(qǐng)求,處理機(jī)及時(shí)響應(yīng)并作相應(yīng)處理。不交換信息時(shí),處理機(jī)和外圍設(shè)備處于各自獨(dú)立的并行工作狀態(tài)。
- 維持系統(tǒng)可靠正常工作。現(xiàn)代計(jì)算機(jī)中,程序員不能直接干預(yù)和操縱機(jī)器,必須通過(guò)中斷系統(tǒng)向操作系統(tǒng)發(fā)出請(qǐng)求,由操作系統(tǒng)來(lái)實(shí)現(xiàn)人為干預(yù)。主存儲(chǔ)器中往往有多道程序和各自的存儲(chǔ)空間。在程序運(yùn)行過(guò)程中,如出現(xiàn)越界訪問(wèn),有可能引起程序混亂或相互破壞信息。為避免這類事件的發(fā)生,由存儲(chǔ)管理部件進(jìn)行監(jiān)測(cè),一旦發(fā)生越界訪問(wèn),向處理機(jī)發(fā)出中斷請(qǐng)求,處理機(jī)立即采取保護(hù)措施。
- 滿足實(shí)時(shí)處理要求。在實(shí)時(shí)系統(tǒng)中,各種監(jiān)測(cè)和控制裝置隨機(jī)地向處理機(jī)發(fā)出中斷請(qǐng)求,處理機(jī)隨時(shí)響應(yīng)并進(jìn)行處理。
- 提供故障現(xiàn)場(chǎng)處理手段。處理機(jī)中設(shè)有各種故障檢測(cè)和錯(cuò)誤診斷的部件,一旦發(fā)現(xiàn)故障或錯(cuò)誤,立即發(fā)出中斷請(qǐng)求,進(jìn)行故障現(xiàn)場(chǎng)記錄和隔離,為進(jìn)一步處理提供必要的依據(jù)。
利用 DMA 實(shí)現(xiàn)短時(shí)間內(nèi)大量數(shù)據(jù)傳輸
上面我們介紹了 I/O 處理和中斷的關(guān)系,下面我們來(lái)介紹一下另外一個(gè)機(jī)制,這個(gè)機(jī)制就是 DMA(Direct Memory Access)。DMA 是指在不通過(guò) CPU 的情況下,外圍設(shè)備直接和主存進(jìn)行數(shù)據(jù)傳輸。磁盤(pán)等硬件設(shè)備都用到了 DMA 機(jī)制,通過(guò) DMA,大量數(shù)據(jù)可以在短時(shí)間內(nèi)實(shí)現(xiàn)傳輸,之所以這么快,是因?yàn)?CPU 作為中介的時(shí)間被節(jié)省了,下面是 DMA 的傳輸過(guò)程
I/O 端口號(hào)、IRQ、DMA 通道可以說(shuō)是識(shí)別外圍設(shè)備的3點(diǎn)組合。不過(guò),IRQ、DMA 通道并不是所有外圍設(shè)備都具備的。計(jì)算機(jī)主機(jī)通過(guò)軟件控制硬件時(shí)所需要的信息的最低限,是外圍設(shè)備的 I/O 端口號(hào)。IRQ 只對(duì)需要中斷處理的外圍設(shè)備來(lái)說(shuō)是必須的,DMA 通道則只對(duì)需要 DMA 機(jī)制的外圍設(shè)備來(lái)說(shuō)是必須的。假如多個(gè)外圍設(shè)備都設(shè)定成相同的端口號(hào)、IRQ 和 DMA 通道的話,計(jì)算機(jī)就無(wú)法正常工作,會(huì)提示 設(shè)備沖突。文字和圖片的顯示機(jī)制
你知道文字和圖片是如何顯示出來(lái)的嗎?事實(shí)上,如果用一句話來(lái)簡(jiǎn)單的概括一下該機(jī)制,那就是顯示器中顯示的信息一直存儲(chǔ)在某內(nèi)存中。該內(nèi)存稱為VRAM(Video RAM)。在程序中,只要往 VRAM 中寫(xiě)入數(shù)據(jù),該數(shù)據(jù)就會(huì)在顯示器中顯示出來(lái)。實(shí)現(xiàn)該功能的程序,是由操作系統(tǒng)或者 BIOS 提供,并借助中斷來(lái)進(jìn)行處理。
在 MS-DOS 時(shí)代,對(duì)于大部分計(jì)算機(jī)來(lái)說(shuō),VRAM 都是主內(nèi)存的一部分。在現(xiàn)代計(jì)算機(jī)中,顯卡等專用硬件中一般都配置有與主內(nèi)存相獨(dú)立的 VRAM 和 GPU(Graphics Processing Unit),也叫做圖形處理器或者圖形芯片。這是因?yàn)?#xff0c;對(duì)經(jīng)常描繪圖形的 windows 來(lái)說(shuō),數(shù)百兆的 VRAM 都是必需的。
用軟件來(lái)控制硬件聽(tīng)起來(lái)好像很難,但實(shí)際上只是利用輸入輸出指令同外圍設(shè)備進(jìn)行輸入輸出而已。中斷處理是根據(jù)需要來(lái)使用的功能選項(xiàng)。DMA 則直接交給對(duì)應(yīng)的外圍設(shè)備即可。
雖然計(jì)算機(jī)領(lǐng)域新技術(shù)在不斷涌現(xiàn),但是計(jì)算機(jī)所能處理的事情,始終只是對(duì)輸入的數(shù)據(jù)進(jìn)行運(yùn)算,并把結(jié)果輸出,這一點(diǎn)是永遠(yuǎn)不會(huì)發(fā)生變化的。
最后,小編想說(shuō):我是一名python開(kāi)發(fā)工程師,整理了一套最新的python系統(tǒng)學(xué)習(xí)教程,想要這些資料的可以關(guān)注私信小編“01”即可,希望能對(duì)你有所幫助。
總結(jié)
以上是生活随笔為你收集整理的多余的读写端口什么时候会对程序造成影响_程序员需要了解的硬核知识之控制硬件...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 下载CIFAR-10、CIFAR-100
- 下一篇: matlab保存colormap失败