zoukankan      html  css  js  c++  java
  • Windows内存管理(3)检查内存可用性,结构化异常处理 和 ASSERT

    1.      检查内存可用性

    在驱动程序开发中,对内存的操作要格外小心。如果某段内存是只读的,而驱动程序试图去写操作,会导致系统的崩溃。

    DDK提供了两个函数,帮助程序员在不知道某段内存是否可读写的情况下,试探这段内存的可读写性。

    VOID 
      ProbeForRead(
        IN CONST VOID  *
    Address,
        IN SIZE_T  
    Length,
        IN ULONG  
    Alignment
        );

    VOID 
      ProbeForWrite(
        IN CONST VOID  *
    Address,
        IN SIZE_T  
    Length,
        IN ULONG  
    Alignment
        );

    这两个函数不是返回该段内存是否可读写,而是当不可读写的时候,引发一个异常。

     

     

    2.      结构化异常处理

     

    (1)   try-except

    __try 

    {

            // guarded code

    }

    __except ( expression )

    {

            // exception handler code

    }

     

    其中expression 有三种可能,如下:

    EXCEPTION_EXECUTE_HANDLER (1) :进入到__except进行错误处理,处理完后不再回到__try块中,转而继续执行。

    EXCEPTION_CONTINUE_SEARCH (0) :不使用__except块中的异常处理,转而向上一层回卷。如果已经是最外层,则向操作系统请求异常处理函数。

    EXCEPTION_CONTINUE_EXECUTION (–1) :重复先前错误的指令,这个在驱动程序中很少用到。

     

    回卷:程序执行到某个地方出现异常错误时,系统会寻找出错点是否处于 一个try{}块中,并进入try块所对应的异常处理代码。如果当前try块没有提供异常处理,则会向更外一层的try块,寻找异常处理代码。直到最外层try块也没有提供异常处理代码,则交由操作系统处理。

     

    (2)   try-finally

     

    __try {

       // guarded code

    }

    __finally {

       // termination code

    }

    利用try-finally块,强迫函数在退出前执行一段代码。在try块中,无论运行什么代码(即使是return语句或者触发异常),在程序退出前都会运行finally块中的代码。这样的目的是让程序在退出前运行一些资源回收的工作,而资源回收代码的最佳位置就是放在这个块中。

    例如:

    原本的代码:

    NTSTATUS test()

    {

        NT_STATUS status = STATUS_SUCCESS;

        status = Foo1(…);

        if(!NT_SUCCESS(status))

        {

           //回收资源;

           //return status

    }

     

    status = Foo2(…);

        if(!NT_SUCCESS(status))

        {

           //回收资源;

           //return status

    }

     

        status = FooN(…);

        if(!NT_SUCCESS(status))

        {

           //回收资源;

           //return status

    }

    }

     

    利用try-finally后的代码:

    NTSTATUS test()

    {

        NT_STATUS status = STATUS_SUCCESS;

        __try

    {

    status = Foo1(…);

            if(!NT_SUCCESS(status))

            {

               //return status

    }

     

    status = Foo2(…);

        if(!NT_SUCCESS(status))

        {

           //return status

    }

     

        status = FooN(…);

        if(!NT_SUCCESS(status))

        {

           //return status

    }

    }

    __finally

    {

        if(!NT_SUCCESS(status)

    {

            //统一回收资源

    }

    return status;

    }

    }

     

     

    3.      断言

    在驱动程序中,使用“断言”,一般是通过ASSERT宏。例如:

    NTSTATUS Foo(PCHAR* str)

    {

           ASSERT(str!=NULL);    //断言

           //str的操作。

    }

    这段代码认为输入绝不可能是空指针,因此在函数的开头做了一个断言。一旦断言失败,会引发 一个异常,并终止程序。

     

    例如:

    C语言的知识解释如下:

    assert( <expression> ); 

    expression结果为时,会在stderr中输出这条语句所在的文件名和行号,以及这条表达式。这只在调试版本中起作用,在Release版本中不会产生任何代码。 
    通常当我们使用assert时,都在强烈说明一个含义:在这里必然如此。它通常用于一个函数的先验条件和后验条件的检查。比如我写一个C风格复制字符串的函数,并且认为调用者不应该传入NULL指针: 

    char   *   clone_string(const   char   *   source) 
    { 
        char   *   result; 
        assert(source   !=   NULL); 
        result   =   (char   *)malloc(strlen(source)   +   1); 
        if   (result   !=   NULL) 
        { 
            strcpy(result,   source); 
            assert(strcmp(result,   source)   ==   0); 
        } 
        return   result; 
    } 
    注意到我对source是否为NULL是用assert检查的,但对result是不是为NULL是用if语句判断的,这是因为在调用代码正确的情况下source必然不为NULL,如果断言失败,说明调用代码中有错误,需要修改;但result作为malloc的返回值则不一定,在malloc代码无误的情况下仍然可能返回NULL——当内存块不足时。最后又用assertstrcpy的结果进行检查,因为只要代码正确,无论什么情况strcpy应该正常完成复制,它没有malloc那种异常情况存在。

     

  • 相关阅读:
    排序
    自动生成存储过程的工具
    感悟javascript
    VS.net 2008 beta2 新功能
    北极光合伙人邓锋:要别人跳火坑,你自己先跳
    [转]驱动开发中应该注意的事项
    [转]CryptoAPI的应用(一)概述以及初始化模块
    [转]强制重启N种法
    Different ways of handling IRPs
    [转]分层驱动模型中IRP的传递与完成
  • 原文地址:https://www.cnblogs.com/forlina/p/2116194.html
Copyright © 2011-2022 走看看