操作系统——ASSERT(十六)

操作系统——ASSERT(十六)

2020-10-01 20:49:38 hawk


概述

  这篇我们,我们接着前面实现的中断处理的基础上,增加assert机制,并且加深对于中断的理解


 断言

  assert,断言,是程序员断定程序运行在此处时,某数据的值一定为多少多少。

  在这个系统中,实际上会实现两种断言——一种是为系统内核使用的ASSERT;另一种是为用户进程使用的assert。

  这里我们首先实现专门为内核使用的ASSERT。我们首先简单的分析一下——当内核运行中出现问题时,一般就没有必要继续运行下去了,因为出现的问题往往会是比较严重的问题。另一方面,当内核输出保存信息时,屏幕上的输出最后也不应该被其他进程干扰,这样才方便我们进行排除错误。因此我们往往会在关中断的情况下,进行打印报错信息。

  因此,实际上我们实现的ASSERT,首先需要进行自由的开、关中断的函数,如下所示

#define EFLAGS_IF    (0x00000200)            //eflags寄存器的IF位为1

/*
    b:1字节        w:2字节        l:4字节        q:8字节
    g表示任意地址
*/
#define GET_EFLAGS(EFLAG_VAR) asm volatile(" \
    pushfl;\
    popl %0":"=g"((EFLAG_VAR)));



/*
    获取当前中断状态变量
*/
enum intr_status intr_get_status() {

    uint32_t eflags = 0;
    GET_EFLAGS(eflags);

    return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF;
}


/*
    开中断
    返回开中断前eflags的状态
*/
enum intr_status intr_enable() {

    if(INTR_ON == intr_get_status()) { return INTR_ON;}
    else {
        
        asm volatile("\
            sti");            //sti, Set IF,将IF位置为1
        return INTR_OFF;
    }
}        
    


/*
    关中断
    返回开中断前eflags的状态
*/
enum intr_status intr_disable() {

    if(INTR_OFF == intr_get_status()) { return INTR_OFF;}
    else {
        
        asm volatile("\
            cli":::"memory");            //cli, Clear IF,将IF位置为0 不知道为什么吧cobber/modify位设置,感觉并没有修改
        return INTR_ON;
    }
}


/*
    将中断设置为给定的status
    返回修改前的中断状态
*/
enum intr_status intr_set_status(enum intr_status status) {
    return status & INTR_ON ? intr_enable() : intr_disable();
}

 

  根据对应的代码可以看出来,这里获取状态就是通过读eflags寄存器。而对于关、开中断来说,使用sti(set IF)、cli(clear IF)进行开中断、关中断。

  下面,我们实现ASSERT函数。其思路也很简单——首先简单的关闭(屏蔽)中断,避免被其他进程干扰。下面则是调用打印程序对应输出即可,其源代码如下所示

    #define PANIC(...) panic_spin(__FILE__, __LINE__, __func__, __VA_ARGS__)


    #ifdef NDEBUG
        #define ASSERT(CONDITION) ((void*)0)
    #else
        #define ASSERT(CONDITION) \
            if((CONDITION)) {} \
            else { \
        /* 符号#让编译器将参数转换为字符串字符串字面量 */ \
                PANIC(#CONDITION); \
            }

    #endif    /*NDEBUG*/



void panic_spin(char* filename, int line, const char* func, const char* condition) {

    //    关闭中断,防止被干扰
    intr_disable();


    put_str("\n\n\n!!!!!!! error !!!!!!\n");
    put_str("filename:");    put_str(filename);    put_char('\n');

    put_str("line:0x");    put_uHex(line);        put_char('\n');
    
    put_str("function:");    put_str(func);        put_char('\n');

    put_str("condition:");    put_str(condition);    put_char('\n');

    while(1) {;}
}

 

  这里只需要说明两点

  1.  ...和__VA_ARGS__。也就是...相当于可变参数,而__VA_ARGS__代表的就是所有与"..."相对应的参数。因此,这里换一个更加形象的表明

PANIC(...)  panic_spin(__FILE__, __LINE__, __func__, ...)

  2.  __FILE__、__LINE__、__func__预编译的宏。对于__FILE__,其表明当前行语句所在的文件名称。__LINE__,其表明当前行语句所在的文件中的行。__func__,其表明当前行语句所在的父函数的名称。

  实际上,我们如果想要输出ASSERT要求的文件、行以及函数等信息,一般一定会使用到__FILE__、__LINE__和__func__这些宏。而实际上为了我们使用ASSERT的时候方便一些——不需要每一次调用ASSERT,都需要在添加__FILE__、__LINE__和__func__等宏,从而我们首先用ASSERT作为宏包装一下前面实现的panic_spin,从而最后完成ASSERT要求的功能。

 

  下面我们在kernel中尝试调用ASSERT,如下所示

#include "print.h"
#include "init.h"
#include "debug.h"

int main(void) {

    /*
        初始化所有的模块
    */    
    init_all();

    ASSERT(1 == 2);

    while(1);

    return 0;
}

 

  这里给出仓库链接。类似于前面的步骤,这里使用make进行构建,然后再虚拟机上进行运行,结果如图所示

 

posted @ 2020-10-01 23:51  hawkJW  阅读(447)  评论(0编辑  收藏  举报