系統級C語言程式設計

來源:互聯網
上載者:User

摘要:本文主要介紹C語言中中斷服務程式的編寫、安裝和使用。由於硬中斷服務程式的編寫涉及到硬體連接埠讀寫操作,使得使用者直接和硬體打交道,在程式設計過程中要用到的資料(如硬體連接埠地址等)比較多,這就使程式員和電腦的硬體裝置間缺少一種“緩衝”的作用,況且,用組合語言來直接對硬體編程要方便得多。本文僅對非強制中斷程式的編寫作個介紹。
關鍵詞:非強制中斷、中斷向量、中斷向量表、TSR記憶體駐留、DOS重入、插斷要求、段地址、位移量、寄存器、BIOS、DOS、setvect ( )、getvect ( )、keep ( )、disable ( )、enable ( )、geninterrupt ( )、int86 ( )、interrupt
    對於一般的C語言愛好者而言,就如何在C中使用中斷常式這一問題應該已經非常熟悉,例如,我們可以通過int86 ( )函數調用13H號中斷直接對磁碟物理扇區進行操作,也可以通過INT86 ( )函數調用33H號中斷在螢幕上顯示滑鼠游標等。其實,13H號也好,33H號也好,它們只不過就是一些函數,這些函數的參數通過CPU的寄存器傳遞。中斷號也只不過是間接地指向函數體的起始記憶體單元,說它是間接的,也就是說,函數的起始段地址和位移量是由中斷號通過一種方法算得的(具體如何操作,下面會作解釋)。如此一來,程式員不必要用太多的時間去寫操作硬體的程式了,只要在自己的程式中設定好參數,再調用BIOS或DOS提供的中斷服務程式就可以了,大大減小了程式開發難度,縮短了程式開發週期。那麼中斷既然是函數,就可以由使用者任意的調用、由使用者任意地編寫。
    電腦記憶體的前1024個位元組(位移量00000H到003FFH)儲存著256個中斷向量,每個中斷向量佔4個位元組,前兩個位元組儲存著中斷服務程式的入口地址位移量,後兩個位元組儲存著中斷程式的入口段地址,使用時,只要將它們分別調入寄存器IP及CS中,就可以轉入中斷服務程式實現中斷調用。每當中斷髮生時,CPU將中斷號乘以4,在中斷向量表中得到該中斷向量地址,進而獲得IP及CS值,從而轉到中斷服務程式的入口地址,調用中斷。這就是中斷服務程式通過中斷號調用的基本過程。在電腦啟動的時候,BIOS將基本的中斷填入中斷向量表,當DOS得到系統控制權後,它又要將一些中斷向量填入表中,還要修改一部分BIOS的中斷向量。有一部分中斷向量是系統為使用者保留的,如60H到67H號中斷,使用者可以將自己的中斷服務程式寫入這些中斷向量中。不僅如此,使用者還可以自己更改和完善系統已有的中斷向量。
    在C語言中,提供了一種新的函數類型interrupt,專門用來定義中斷服務程式,比如我們可以寫如下的中斷服務程式:
/*例1:中斷服務程式*/
void interrupt int60()
{
 puts("This is an example");
}
該中斷的功能就是顯示一個字串,為什麼不用printf ( )函數呢?這就牽涉到DOS的重入問題,後面將作一些介紹。
    一個簡單的中斷服務程式寫好了,如何把它的函數入口地址填寫到中斷向量表中,以便在產生中斷的時候能轉入中斷服務程式去執行呢?這裡要用到setvect ( )和getvect ( )函數。setvect ( )有兩個參數:中斷號和函數的入口地址,其功能是將指定的函數安裝到指定的中斷向量中,getvect ( )函數有一個參數:中斷號,傳回值是該中斷的入口地址。在安裝中斷以前,最好用disable ( )函數關閉中斷,以防止在安裝過程中又產生新的中斷而導致程式運行混亂,待安裝完成後,再用enable ( )函數開放中斷,使程式正常運行。現在我們可以把上面的例子再豐富一下:
/*例2:中斷服務程式的編寫、安裝和使用*/

#include <dos.h>

#include <stdio.h>

#ifdef __cplusplus

 #define __ARGU ...

#else

 #define __ARGU

#endif

void interrupt int60 (__ARGU)  /*中斷服務函數*/

{

 puts("This is an example");

}

void install (void interrupt (*fadd)(__ARGU),int num) /*安裝中斷*/
{
 disable(); /*關閉中斷*/
 setvect(num, fadd); /*設定中斷*/
 enable(); /*開放中斷*/
}
void main()
{
install (int60,0x60);/*將int60函數安裝到0x60中斷*/
geninterrupt (0x60); /*人為產生0x60號中斷*/
}
有一定經驗的讀者很容易得到該程式的執行結果:在螢幕上顯示“This is an example!”。
    編寫、安裝中斷服務程式的方法就介紹這些。下面再淺談一下記憶體駐留程式(TSR)的編寫和使用。在C語言中,可以用keep ( )函數將程式駐留記憶體。這個函數有兩個參數:status和size。size為駐留記憶體長度,可以用size=_SS+_SP/16-_psp得到,當然這也是一種估算的方法,並不是精確值。函數執行完以後,出口狀態資訊儲存在status中。比如,對於上面的例子,將“geninterrupt (0x60);”改寫成“keep(0,_SS+_SP/16-_psp);”後再執行程式,這一段程式就被駐留,此後在其它的任何軟體或程式設計中,只要用到了60H號中斷,就會在螢幕上顯示“This is an example!”的字樣。要恢複系統對60H號中斷的定義,只能重新啟動電腦。
    像上面的例子其實還很不完善,它沒有考慮DOS系統內容的狀態、沒有考慮程式是否已經駐留記憶體、沒有考慮退出記憶體駐留等問題。對於第二個問題還是很容易解決的:執行程式一開始就讀取某一函數中斷入口地址(如63H號中斷)判斷是否為空白(NULL),如果為空白就先將該地址置為非空再駐留記憶體,若為非空則表示已經駐留並退出程式。這一步判斷非常重要,否則將會因為重複駐留佔用過多記憶體空間而最後造成系統崩潰。至於其它兩個問題,在此不多作說明,有興趣的讀者可以參考一些有關書籍。
    不僅如此,我們還可以通過在DOS下使用熱鍵(Hotkey)來調用記憶體駐留程式。比如將《希望漢字系統》內建的《希望詞典》駐留記憶體後,在任意時刻按下Ctrl+F11鍵,就能啟用程式,出現詞典介面。微機的鍵盤中有一個微處理晶片,用來掃描和檢測每個按鍵的按下和釋放狀態。大多數按鍵都有一個掃描碼,告知CPU當前的狀態,但一些特殊的鍵如PrintScreen、Ctrl+Break等不會產生掃描碼,而直接產生中斷。正因為如此,我們可以將Ctrl+Break產生的中斷號指向我們自己寫好的程式入口地址,那麼當按下Ctrl+Break後,系統就會調用我們自己的程式去執行,這實際上也就是修改了Ctrl+Break的中斷向量。至於其它按鍵啟用程式則可以利用9H號鍵盤中斷捕獲的掃描碼來實現,在此不多作說明。例如,執行下面的程式後,退回DOS系統,在任意的時候按下Ctrl+Break後,螢幕的底色就會變成紅色。
/*例3:中斷服務程式編寫、安裝和使用,記憶體駐留*/
#include <dos.h>
#include <conio.h>
#ifdef __cplusplus
 #define __ARGU ...
#else
 #define __ARGU
#endif
void interrupt newint(__ARGU); /*函式宣告*/
void install (void interrupt (*fadd)(__ARGU), int num);
int main()
{
 install (newint,0x1b); /*Ctrl+Break中斷號:1BH*/
 keep(0,_SS+(_SP/16)-_psp); /*駐留程式*/
 return 0;
}
void interrupt newint(__ARGU)
{
 textbackground(4); /*設定螢幕底色為紅色*/
 clrscr(); /*清除螢幕*/
}
void install (void interrupt (*fadd)(__ARGU), int num)
{
 disable();
 setvect(num,fadd); /*設定中斷*/
 enable();
}
    由於13H號中斷是BIOS提供的磁碟中斷服務程式,對於DOS下的應用程式,它們的存檔、讀盤功能都是通過調用這一中斷來實現的。有許多DOS下的病毒就喜歡修改13H號中斷來破壞系統,例如,修改13H號中斷服務程式,將其改成:
/*例4:病毒體程式虛擬碼*/
void interrupt new13(__ARGU)
{
 if (病毒發作條件成熟)
 { 修改入口參數指向病毒程式入口地址;
  執行病毒代碼;
 }
 調用原來的13H中斷;
}
只要當任一軟體(如EDIT.COM等)對磁碟有操作並且病毒發作條件成熟時,病毒就被啟用。當然,這樣做會導致可用記憶體空間減少,容易被使用者發現。一些“聰明”的病毒又會去修改其它的中斷向量,使得系統報告的記憶體大小和實際相符合。還有的病毒,當發現使用者通過一些程式(如DEBUG.COM等)去跟蹤它時,它會悄悄地溜掉,其基本原理仍然與修改中斷有關。硬碟的0面0柱1扇區(Side 0 Cylinder 0 Sector 1)儲存著重要的引導資訊,一旦破壞,電腦將無法識別硬碟。我們可以寫一個程式來防止任何軟體(包括病毒)對這一扇區執行“寫”操作,一定程度上實現了“防寫保護”的作用,它的基本原理就是修改13H號中斷向量並常駐記憶體,監視著軟體(包括病毒)對磁碟操作的每一個細節。讀者請注意:本程式沒有考慮記憶體駐留的退出,如果想恢複13H號中斷,請重新啟動電腦。
/*例5:主開機磁區保護,請用Turbo C 2.0編譯,MBSP.C*/
#include <dos.h>

#include <stdio.h>

#include <stdlib.h>

#define STSIZE 8192

#define PSP_ENV_PSP 0x2c
#define PARA(x) ((FP_OFF(x)+15)>>4)

typedef struct {
 unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags;
} INTERRUPT_PARAMETER;
void install (void interrupt (*faddress)(), int num);
void interrupt new13(INTERRUPT_PARAMETER p);
int main()
{
union REGS regs;
struct SREGS sregs;
unsigned mem;
unsigned far *pointer;
char far *stack;
printf("/n<<Master Boot Sector Protector>> version 1.0/n/n");
   if ((stack=malloc(STSIZE))==NULL)
  {
  printf ("Not enough Memory!/n");
  exit(1);
  }
 if (getvect(0x62)!=NULL)
  {
  printf("Already Installed!/n");
  exit(1);
  }
 install(getvect(0x13),0x62);
 install (new13,0x13);
 pointer=MK_FP(_psp,PSP_ENV_PSP);
 freemem(*pointer);
 segread(&sregs);
 mem=sregs.ds+PARA(stack)-_psp;
 setblock(_psp,mem);
 keep (0,mem);
 return 0;
}

void install (void interrupt (*faddress)(), int num)
{
 disable();
 setvect(num,faddress);
 enable();
}

void interrupt new13(INTERRUPT_PARAMETER p)
{
p.ax=_AX;
p.cx=_CX;
p.dx=_DX;
if(_AH==0x03&&_CH==0&&_CL==0x01&&_DH==0&&_DL==0x80) return;
enable();
geninterrupt (0x62);
disable();
_AX=p.ax;
_CX=p.cx;
_DX=p.dx;
return;
}
    說明:在使用本程式以前,請:①用殺毒軟體對電腦開機磁區、記憶體和所有檔案進行一次全面的掃描,確信電腦中沒有任何病毒;②有電腦組合語言基礎的讀者可以自己寫一個新的引導程式,先將本程式駐留記憶體,再調用原來的引導程式,以便在病毒還沒有取得系統控制權以前開啟防護功能。
    最後簡要說明一下DOS系統重入問題。DOS是單使用者單任務作業系統。如果程式在執行的過程中被打斷,就有可能因為破壞了原來的程式運行環境而造成運行不正常,這是災難性的。當中斷產生後,CPU立即中止當前的程式去執行中斷服務程式,如果在中斷服務程式中又有對DOS中斷的調用(如DOS的21H號中斷)時,這樣必定會重寫環境全域變數(例如PSP程式段首碼就會被改成正在執行的中斷程式的PSP),這樣原來的環境被破壞,原來的程式也就無法正確執行。當中斷調用完成並返回後,使用者得到的結果是出乎意料的。所以在編寫中斷服務程式時應該避免DOS系統功能調用,在C語言的中斷服務程式中不應該出現malloc ( )、printf ( )、sprintf ( )等函數。

參考文獻:
《C進階公用程式設計》王士元編著,清華大學出版社,1996.3

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.