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失败会导致系统停转,用户进程的失败仅仅使自己停转。
原文地址:https://www.cnblogs.com/broadview/p/1490561.html