Sunny Chen的专栏

欢迎进入Sunny Chen的blog!

陈晴阳ID:acqy
51216次访问,排名2051好友0人,关注者1
陈晴阳,2003年毕业于中南大学铁道学院计算机专业。MCP/MCAD,国家认证高级程序员、系统分析员,中国系统分析员协会顾问团顾问。软件设计与构架、信息系统建设的爱好者。具有三年Microsoft Axapta/Dynamics AX的开发经验。
acqy的文章
原创 25 篇
翻译 0 篇
转载 0 篇
评论 23 篇
acqy的公告

发送邮件


最近评论
czdvcc:wow power leveling
xzy110:中国软件的悲哀!
acqy:void interrupt newint(__ARGU); 这是定义一个中断函数。interrupt是中断函数修饰符,专门用于定义中断函数的。在该修饰符所定义的函数中最好不要使用类似printf这样的函数,因为它会破坏中断运行的环境(例如这类函数中使用了其它未保护中断),编译的时候自然不会出错,但是有可能造成运行的不稳定。具体的内容需要参考Turbo C的帮助文档和《DOS中断大全》一书<……
Editing:请教楼主:
void interrupt 类型函数的参数到底干什么用的?
这类函数的出口参数是怎么设置的?
麻烦楼主解答一下,谢谢!
TIMEREACH:int MyIntArray[100] 的意思就是
int[100] MyIntArray
这样是不是就好理解一点了呢?
文章分类
收藏
    相册
    湘潭大学
    友情链接
    mailbomb的专栏
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 系统级C语言程序设计收藏

    新一篇: 今天对我的blog内容进行了整理 | 旧一篇: 磁盘快速格式化的理论和实践

    摘要:本文主要介绍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

    发表于 @ 2001年01月22日 14:30:00|评论(loading...)|编辑

    新一篇: 今天对我的blog内容进行了整理 | 旧一篇: 磁盘快速格式化的理论和实践

    评论

    # 发表于2004-10-06 11:07:00  IP: 210.82.175.*
    有提供下载的么
    #Sunny Chen 发表于2004-10-07 21:17:00  IP: 220.170.4.*
    具体可以发邮件给我:qychen2003@hotmail.com
    Sunny Chen
    #笑千秋 发表于2004-10-10 15:44:00  IP: 61.178.79.*
    不错!
    #笑千秋 发表于2004-10-10 15:47:00  IP: 61.178.79.*
    可否编一个在系统启动前(没有进入系统)显示警告信息的程序
    信箱:topcool99@163.com
    #DOS鬼 发表于2004-10-27 14:06:00  IP: 221.0.103.*
    你用的什么编译器?
    #Sunny Chen 发表于2004-10-28 14:26:00  IP: 61.187.64.*
    Turbo C 2.0
    #路过者 发表于2005-01-29 11:24:00  IP: 218.104.167.*
    为什么不写完整呢?"有一定经验的读者很容易得到该程序的执行结果"
    ....这是什么意思。
    我想看这篇文章的大多是个初学者,也就是根本就没经验。
    #field 发表于2005-03-03 08:43:00  IP: 219.234.84.*
    我也想下载
    #蓝天 发表于2005-07-18 08:28:00  IP: 61.186.252.*
    还是不错,这种底层的东西还是可以,不过用汇编介绍最好,能更清楚的了解底层,用C就像带着枷锁跳舞一样
    #Editing 发表于2007-07-11 12:09:01  IP: 222.205.89.*
    请教楼主:
    void interrupt 类型函数的参数到底干什么用的?
    这类函数的出口参数是怎么设置的?
    麻烦楼主解答一下,谢谢!
    #acqy 发表于2007-07-11 17:19:27  IP: 218.242.135.*
    void interrupt newint(__ARGU); 这是定义一个中断函数。interrupt是中断函数修饰符,专门用于定义中断函数的。在该修饰符所定义的函数中最好不要使用类似printf这样的函数,因为它会破坏中断运行的环境(例如这类函数中使用了其它未保护中断),编译的时候自然不会出错,但是有可能造成运行的不稳定。具体的内容需要参考Turbo C的帮助文档和《DOS中断大全》一书
    这类函数主要用于对中断的处理,一般没有返回值,至于其参数,如果你不是使用的C++,直接使用void就ok了。

    很久没有碰这些技术了,如有回答不实之处,请谅解!
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © acqy