• assert()和panic()


    assert()和panic()
    先来看assert()。你或许早就开始使用这个函数,但之前你使用的都是现成的assert,只要包含一个头文件,就可以方便地使用。如今什么都得自力更生了,不过不用怕,写一个assert函数并非难事,见下面的代码:
    12 #define ASSERT
    13 #ifdef ASSERT
    14 void assertion_failure(char *exp, char *file, char *base_file, int line);
    15 #define assert(exp) if (exp) ; \
    16         else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)
    17 #else
    18 #define assert(exp)
    19 #endif
    注意其中的__FILE__、__BASE_FILE__和__LINE__这三个宏,它们的意义如下[1]:
    __FILE__: 将被展开成当前输入的文件。在这里,它告诉我们哪个文件中产生了异常。
    __BASE_FILE__: 可被认为是传递给编译器的那个文件名。比如你在m.c中包含了n.h,而n.h中的某一个assert函数失败了,则__FILE__为n.h,__BASE_FILE__为m.c。
    __LINE__: 将被展开成当前的行号。
    明白了这几个宏的意义,剩下的assertion_failure()这个函数就显得容易了,它的作用就是将错误发生的位置打印出来:
    42 PUBLIC void assertion_failure(char *exp, char *file, char *base_file, int line)
    43 {
    44         printl(”%c␣␣assert(%s)␣failed:␣file:␣%s,␣base_file:␣%s,␣ln%d”,
    45                 MAG_CH_ASSERT,
    46                 exp, file, base_file, line);
    47
    48         /**
    49          * If assertion fails in a TASK, the system will halt before
    50          * printl() returns. If it happens in a USER PROC, printl() will
    51          * return like a common routine and arrive here.
    52          * @see sys_printx()
    53          *
    54          * We use a forever loop to prevent the proc from going on:
    55          */
    56         spin(”assertion_failure()”);
    57
    58         /* should never arrive here */
    59         __asm__ __volatile__(”ud2”);
    60 }
    注意这里使用了一点点小伎俩,那就是使用了一个改进后的打印函数,叫做printl(),它其实就是一个定义成printf的宏,不过这里的printf跟上一章中的稍有不同,它将调用一个叫做printx的系统调用,并最终调用函数sys_printx(),它位于tty.c中:
    181 PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc* p_proc)
    182 {
    183         const char * p;
    184         char ch;
    185
    186         char reenter_err[] = ”?␣k_reenter␣is␣incorrect␣for␣unknown␣reason”;
    187         reenter_err[0] = MAG_CH_PANIC;
    188
    189         /**
    190          * @note Code in both Ring 0 and Ring 1~3 may invoke printx().
    191          * If this happens in Ring 0, no linear-physical address mapping
    192          * is needed.
    193          *
    194          * @attention The value of ‘k_reenter’ is tricky here. When
    195          * -# printx() is called in Ring 0
    196          * - k_reenter > 0. When code in Ring 0 calls printx(),
    197          * an ‘interrupt re-enter’ will occur (printx() generates
    198          * a software interrupt). Thus ‘k_reenter’ will be increased
    199          * by ‘kernel.asm::save’ and be greater than 0.
    200          * -# printx() is called in Ring 1~3
    201          * - k_reenter == 0.
    202         */
    203         if (k_reenter == 0) /* printx() called in Ring<1~3> */
    204                 p = va2la(proc2pid(p_proc), s);
    205         else if (k_reenter > 0) /* printx() called in Ring<0> */
    206                 p = s;
    207         else /* this should NOT happen */
    208                 p = reenter_err;
    209
    210         /**
    211         * @note if assertion fails in any TASK, the system will be halted;
    212         * if it fails in a USER PROC, it’ll return like any normal syscall
    213         * does.
    214         */
    215         if ((*p == MAG_CH_PANIC) ||
    216             (*p == MAG_CH_ASSERT && p_proc_ready < &proc_table[NR_TASKS])) {
    217                 disable_int();
    218                 char * v = (char*)V_MEM_BASE;
    219                 const char * q = p + 1; /* +1: skip the magic char */
    220       
    221                 while (v < (char*)(V_MEM_BASE + V_MEM_SIZE)) {
    222                         *v++ = *q++;
    223                         *v++ = RED_CHAR;
    224                         if (!*q) {
    225                                 while (((int)v - V_MEM_BASE) % (SCR_WIDTH * 16)) {
    226                                         /* *v++ = ’ ’; */
    227                                         v++;
    228                                         *v++ = GRAY_CHAR;
    229                                 }
    230                                 q = p + 1;
    231                         }
    232                 }
    233
    234                 __asm__ __volatile__(”hlt”);
    235         }
    236
    237         while ((ch = *p++) != 0) {
    238                 if (ch == MAG_CH_PANIC || ch == MAG_CH_ASSERT)
    239                         continue; /* skip the magic char */
    240
    241                 out_char(tty_table[p_proc->nr_tty].p_console, ch);
    242         }
    243
    244         return 0;
    245 }
    容易看到,sys_printx()将首先判断首字符是否为预先设定的“Magic Char”,如果是的话,则做响应的特殊处理。我们的assertion_failure()就使用了MAG_CH_ASSERT作为“Magic Char”。当sys_printx()发现传入字符串的第一个字符是MAG_CH_ASSERT时,会同时判断调用系统调用的进程是系统进程(TASK)还是用户进程(USER PROC),如果是系统进程,则停止整个系统的运转,并将要打印的字符串打印在显存的各处;如果是用户进程,则打印之后像一个普通的printx调用一样返回,届时该用户进程会因为assertion_failure()中对函数spin()的调用而进入死循环。换言之,系统进程的assert失败会导致系统停转,用户进程的失败仅仅使自己停转。
  • 相关阅读:
    linux 常用快捷键
    命令行远程链接
    mybatis参数错误 Parameter '×××' not found. Available parameters are [0, 1, param1, param2]
    mybatis延迟加载
    mybatis跨XML引用
    eclipse修改项目名称
    wait, WIFEXITED, WEXITSTATUS
    进程通信中如何进行值得传递?
    fork新建进程
    Ubuntu安装genymotion模拟器步骤
  • 原文地址:https://www.cnblogs.com/broadview/p/1490561.html
Copyright © 2020-2023  润新知