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失败会导致系统停转,用户进程的失败仅仅使自己停转。
assert()和panic()相关推荐
- Rust编程语言的核心部件
Rust是一门强调安全.并发.高效的系统编程语言.无GC实现内存安全机制.无数据竞争的并发机制.无运行时开销的抽象机制,是Rust独特的优越特性.它声称解决了传统C语言和C++语言几十年来饱受责难的内 ...
- (转)PowerHA完全手册(一,二,三)
PowerHA完全手册(一) 原文:http://www.talkwithtrend.com/Article/39889-----PowerHA完全手册(一) http://www.talkwitht ...
- 进程间通信(IPC)+进程加锁解锁
[0]README 0.1) source code and text description are from orange's implemention of a os: 0.2) for com ...
- InfoQ中文站特供稿件:Rust编程语言的核心部件
本文为InfoQ中文站特供稿件,首发地址为: http://www.infoq.com/cn/articles/rust-core-components .如需转载,请与InfoQ中文站联系.原文发表 ...
- linux oops产生原理,kernel panic , Oops 等cpu异常的分析与定位
一.kernel panic 二.mips异常机制 三.linuxkernel 对mips异常的处理 四.kernel panic 实例分析 Kernel panic 内核代码,相比用户层代码更难以 ...
- 操作系统真象还原实验记录之实验十二:实现ASSERT
操作系统真象还原实验记录之实验十二:实现ASSERT,通过makefile完成编译 对应书P367 第8.2节 1.相关基础知识 见书 2.实验代码 完成了开关中断函数.实现assert断言函数用于调 ...
- Linux内核oops panic简析
源码基于:Linux 5.4 0. 前言 内核异常的级别大致分为三个:BUG.oops.panic. BUG 是指那些不符合内核的正常设计,但内核能够检测出来并且对系统运行不会产生影响的问题,比如在原 ...
- Solidity 中 revert(), assert() 和 require()
函数assert和require可以用于检查条件,如果条件不满足则抛出异常. assert(): 函数只能用于测试内部错误,检查不变量,正常的函数代码永远不会产生Panic, 甚至是基于一个无效的外部 ...
- Go 知识点(14) — Go 多协程(单个协程触发panic会导致其它所有协程挂掉,每个协程只能捕获到自己的 panic 不能捕获其它协程)
在多协程并发环境下,我们常常会碰到以下两个问题.假设我们现在有 2 个协程,我们叫它们协程 A 和 B . [问题1]如果协程 A 发生了 panic ,协程 B 是否会因为协程 A 的 panic ...
- Python3 try-except、raise和assert解析
20220221 案例 def product(x):result = 1print(x)count = 0for i in x:try:result *= float(i)except Except ...
最新文章
- Oracle 验证A表的2个字段组合不在B表2个字段组合里的数据
- c语言 链表 删除节点,C语言实现单链表节点的删除(不带头结点)
- Promise之异步调用
- su切换到oracle后怎么退出,linux下启动oralce和关闭oracle以及数据库实例化
- snort简介以及在Ubuntu下的安装
- 二、CSS基础(1)
- git常用命令常用场景
- 在苹果Mac上的“磁盘工具”中如何修复储存设备?
- 互联网产品需求管理思考——统一需求管理
- vivado2019.2安装+license添加教程
- 总结一个技术总监的教训和经验
- STM32F103驱动THM3060读取二代身份证
- 免费顺丰快递单号查询API接口demo【快递鸟API接口】
- 第三章 代码的坏味道
- 为啥将phpstudy打开,却访问不了rips
- utf-8的中文是一个汉字占三个字节长度吗?
- 操作系统——吸烟者问题
- Houdini 求中点,点连成线
- java变量的定义有哪些规则_Java变量详解
- LTSPICE仿真那些事