目录

处理页面错误

断点异常

系统调用

用户模式启动

页面错误与内存保护


处理页面错误

页错误异常,中断向量 14 ( T_PGFLT),是一个特别重要的异常,我们将在本实验和下一个实验中大量练习。当处理器发生页面错误时,它将导致错误的线性(即虚拟)地址存储在特殊的处理器控制寄存器CR2 中。在trap.c 中, 我们提供了一个特殊函数的开头 page_fault_handler(),用于处理页面错误异常。

exercise5:trap_dispatch的修改,使页面错误异常分配到page_default_handler。值需要注意一个问题,就是通过trap_no确定异常在IDT中的index,因为trap_no在TRAPHANDER中push了num。

//注意这里是return,否则printframe信息会错误。

switch (tf->tf_trapno){case T_PGFLT:page_fault_handler(tf);return;
...
}

断点异常

断点异常,中断向量 3 ( T_BRKPT),通常用于允许调试器通过用特殊的 1 字节int3软件中断指令临时替换相关程序指令来在程序代码中插入断点。在 JOS 中,我们将稍微滥用此异常,将其转换为任何用户环境都可以用来调用 JOS 内核监视器的原始伪系统调用。如果我们将 JOS 内核监视器视为原始调试器,这种用法实际上有些合适。例如,panic()在lib/panic.c 中的用户模式实现,在int3显示其恐慌消息后执行。

exercise6:与上一个实现方法相同。

case T_BRKPT:
monitor(tf);
return;

Question3:取决于初始化断点条目时dpl的设置。

SETGATE(idt[T_BRKPT],0,GD_KT,trap_brkpt,3);//cpl<=dpl,int $3,用户态,dpl=3

若DPL设置为0,则产生保护异常,因为INT$3属于系统级别的指令,当前为用户态,若dpl设为3,则无法正确进行,从而产生一般保护异常。当dpl设置为3时,用户态可以访问,引发断点异常。

Question4:重点是区分当前的权限是否能够正确执行指令。也就是要保证DPL>=CPL,防止出现用户态使用内核态指令。

系统调用

用户进程通过调用系统调用来要求内核为它们做事。当用户进程调用系统调用时,处理器进入内核模式,处理器和内核协同保存用户进程的状态,内核执行适当的代码以进行系统调用,然后恢复用户进程。用户进程如何引起内核的注意以及它如何指定要执行的调用的确切细节因系统而异。

在 JOS 内核中,将使用int 导致处理器中断的指令。特别是将int $0x30 用作系统调用中断。T_SYSCALL您定义为常量 48 (0x30)。您必须设置中断描述符以允许用户进程引起该中断。注意中断0x30不能由硬件产生,所以不存在允许用户代码产生的歧义。

应用程序将在寄存器中传递系统调用号和系统调用参数。这样,内核就不需要在用户环境的堆栈或指令流中四处游荡。系统调用号会去%eax,和参数(其中最多5个)将去%edx, %ecx%ebx%edi,和%esi。内核将返回值传回%eax. 调用系统调用的汇编代码已为您编写, syscall()在lib/syscall.c 中。

exercise7:

        trap_dispatch():

case T_SYSCALL:result=syscall(tf->tf_regs.reg_eax,tf->tf_regs.reg_edx,tf->tf_regs.reg_ecx,tf->tf_regs.reg_ebx,tf->tf_regs.reg_edi,tf->tf_regs.reg_esi);//在寄存器中存储了系统调用号与参数
tf->tf_regs.reg_eax=result;//结果保存到eax

kern/syscall():

int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{switch (syscallno) {//不同的系统调用号返回不同的函数case (SYS_cputs):sys_cputs((const char*)a1,a2);return 0;case (SYS_cgetc):return sys_cgetc();case (SYS_getenvid):return sys_getenvid();case (SYS_env_destroy):return sys_env_destroy(a1);default:return -E_INVAL;}
}

        lib/syscall():

        这里的函数是用户进程实际调用的函数,user下的文件调用lib/syscall这里面的函数引发系统中断,然后lib中的syscall通过T-SYSCALL引发系统中断,进入内核,然后调用kern/syscall中的具体处理函数,如用户程序hello中的cprintf,就是系统中断进入内核后调用了内核中的cprintf完成的。

static inline int32_t
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
// 通用系统调用:在AX中传递系统调用号, DX、CX、BX、DI、SI 中最多五个参数。 用 T_SYSCALL 中断内核。
// “volatile”告诉汇编器不要优化这条指令只是因为我们不使用
返回值。
// 最后一个子句告诉汇编器这可以可能改变条件代码和任意内存位置。 int32_t ret;asm volatile("int %1\n": "=a" (ret)//=表示输出操作数,用%1来访问,eax保存: "i" (T_SYSCALL),//立即整数操作数"a" (num),//eax"d" (a1),//edx"c" (a2),"b" (a3),"D" (a4),"S" (a5): "cc", "memory");if(check && ret > 0)panic("syscall %d returned %d (> 0)", num, ret);return ret;
}

GCC内联汇编基础 - 简书

用户模式启动

用户程序从lib/entry.S开始运行 。经过一些设置后,此代码在lib/libmain.c 中调用。修改libmain()以初始化全局指针 thisenv以指向数组struct Env中的此环境 envs[]。在make run-hello的情况下,之前在打印“ hello, world ”后,它会尝试thisenv->env_id.

这就是它较早出错的原因。在增加thisenv指向正确的env后,访问id不会再出错,但是由于现在内核目前只支持一个环境,因此在完成后就退出环境。

entry.s首先设置了用户模式的数据段,然后在代码段设置了新进程运行的入口。先查看了当前的用户栈顶是否在USTACKTOP处,如果参数不存在,那么手动添加虚拟参数后再执行libmain的内容。argc指的是参数的个数,argv是参数地址的数组, argv[0]保存程序的名字。然后切换当前的用户环境执行用户程序umain()。

void
libmain(int argc, char **argv)
{thisenv=&envs[ENVX(sys_getenvid())];// save the name of the program so that panic() can use itif (argc > 0)binaryname = argv[0];// call user main routineumain(argc, argv);// exit gracefullyexit();
}

页面错误与内存保护

内存保护是操作系统的一项重要功能,可确保一个程序中的错误不会损坏其他程序或损坏操作系统本身。

操作系统通常依靠硬件支持来实现内存保护。操作系统让硬件知道哪些虚拟地址有效,哪些无效。当程序试图访问无效地址或没有权限访问的地址时,处理器会在导致错误的指令处停止程序,然后将有关尝试操作的信息捕获到内核中。如果故障是可修复的,内核可以修复它并让程序继续运行。如果故障不可修复,则程序无法继续,因为它永远不会越过导致故障的指令。

作为可修复故障的示例,请考虑自动扩展堆栈(UXSTACKTOP)。在许多系统中,内核最初分配单个堆栈页面,然后如果程序在访问堆栈下方的页面时出错,内核将自动分配这些页面并让程序继续运行。通过这样做,内核只分配程序所需的堆栈内存,但程序可以在拥有任意大堆栈的假象下工作。

系统调用为内存保护提出了一个有趣的问题。大多数系统调用接口让用户程序将指针传递给内核。这些指针指向要读取或写入的用户缓冲区。然后内核在执行系统调用时取消引用这些指针。这有两个问题:

         内核中的页面错误可能比用户程序中的页面错误严重得多。如果内核在操作自己的数据结构时出现页面错误,那就是内核错误,并且错误处理程序应该使内核(以及整个系统)恐慌。但是当内核解引用用户程序给它的指针时,它需要一种方法来记住这些解引用导致的任何页面错误实际上代表用户程序。

         内核通常比用户程序具有更多的内存权限。用户程序可能会传递一个指向系统调用的指针,该指针指向内核可以读取或写入但程序不能读取的内存。内核必须小心不要被欺骗取消引用这样的指针,因为这可能会泄露私有信息或破坏内核的完整性。

因此,内核在处理用户程序提供的指针时必须非常小心。通过系统调用函数传入指针给内核时,应当检查其地址空间是否是用户空间以及页表是否允许内存操作。

   exercise9&&10:

        kern/trap.c,改变page_fault_handler(),如果页面错误发生在内核中,则产生恐慌。

   if((tf->tf_cs&3)==0)//cpl,当前特权级别为0,则处于内核中panic("page fault happens in kernel mode!adress:%d",fault_va);

kern/syscall.c,完成user_mem_check(),检查系统调用的参数,是否访问了不可访问的内存。

注释里说不需要页面对齐,只需要将页面上限多检查两个,但是实现的时候有问题,最后还是改成了对齐的方式。还有一个要注意的是向下取整之后,如果第一个页面访问就出现问题,那么要注意第一个页面的地址是返回原始va还是对齐后的地址。

int
user_mem_check(struct Env *env, const void *va, size_t len, int perm)
{// LAB 3: Your code here.size_t  start=(size_t)ROUNDDOWN(va,PGSIZE);size_t end=(size_t)ROUNDUP((uint32_t)va+len,PGSIZE);for(start;start<end;start+=PGSIZE){pte_t* pte=pgdir_walk(env->env_pgdir,(const void*)start,0);if(pte&&start<(size_t)ULIM&&((*pte&perm)==perm)&&(*pte&PTE_P)){//perm|PTE_Pcontinue;}else{user_mem_check_addr=(uintptr_t) (start<(size_t) va?(size_t)va:start);//start<va,the first is va rather than startreturn -E_FAULT;}}return 0;
}

最后是kdebug.c中user_mem_check的使用,没什么说的,调用就好了。

if(user_mem_check(curenv,(const void*)usd,sizeof(struct UserStabData),PTE_U)!=0)return -1;stabs = usd->stabs;
stab_end = usd->stab_end;
stabstr = usd->stabstr;
stabstr_end = usd->stabstr_end;// Make sure the STABS and string table memory is valid.
// LAB 3: Your code here.
if(user_mem_check(curenv,(const void*)stabs,sizeof(struct Stab),PTE_U)!=0)return -1;
if(user_mem_check(curenv,(const void*)stabstr,(size_t)(stabstr_end-stabstr),PTE_U)!=0)return -1;

以上是LAB3partB,主要是针对页面错误的一些处理。

LAB3 PartB页面错误、断点异常和系统调用相关推荐

  1. java jsp公共异常页面_Java如何创建JSP错误页面以处理异常?

    在此示例中,您将学习如何在JSP页面中处理异常.JSP具有用于错误处理的内置机制,这是一个特殊页面,可用于处理Web应用程序中的每个错误.要将页面定义为错误页面,我们使用page指令,isErrorP ...

  2. springboot系列六、springboot配置错误页面及全局异常

    springboot系列六.springboot配置错误页面及全局异常 参考文章: (1)springboot系列六.springboot配置错误页面及全局异常 (2)https://www.cnbl ...

  3. 《Tsinghua os mooc》第1~4讲 启动、中断、异常和系统调用

    资源 OS2018Spring课程资料首页 uCore OS在线实验指导书 ucore实验基准源代码 MOOC OS习题集 OS课堂练习 Piazza问答平台 暂时无法注册 疑问 为什么用户态和内核态 ...

  4. pyqt5程序发生错误不中断_关于Windows页面错误的一些基础概念

    很少被开发者关注的页面错误 今天我们会说说关于虚拟内存处理中最为常见的一个问题:页面错误(Page Fault). 什么情况下会发生一个页面错误呢? 当应用程序请求的页面地址不在当前的内存驻留页面(M ...

  5. ASP.NET页面错误处理

    ASP.NET页面错误处理 ASP.NET应用可以再代码中利用异常捕获来处理错误(try.catch)但是应用出现的所有错误,都用异常捕获来处理是一种不良的编程习惯.try.catch使用简单,但是过 ...

  6. 未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项。参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))解决方法。...

    前一天晚上VS卡死,强制关闭后就没理他,然后晚上回去又经历了一次过热断电关机和一次蓝屏,当然我也不知道这些和他有没有关联,第二天早上打开程序出现了:"未能加载文件或程序集"Antl ...

  7. php核心技术与最佳实践 --- 错误与异常

    <?php /*php error*/ /** 异常和错误的概念不一样* 在PHP里,遇到任何自身错误都会触发一个错误,而不是抛出异常(对于一些情况,会同时抛出异常和错误)* 异常处理机制就是把 ...

  8. Python语法错误和异常

    语法错误和异常 语法错误和异常 异常和错误 处理异常 捕获指定异常 异常中的else 异常的finally 自定义异常类型 语法错误和异常 异常和错误 错误分为两种:语法错误与异常 语法错误:指拼写代 ...

  9. web常见页面错误盘点及分析

    转自:微点阅读  https://www.weidianyuedu.com 首先是412错误:打开当前调用的ajax方法,查看请求类型是post还是get,一般来说post的话改成get请求就可以解决 ...

最新文章

  1. c语言生成文件可以删,C语言-文件操作下
  2. element-ui表格列金额显示两位小数
  3. linux 常用命令技巧
  4. linux下编辑aacc.sh脚本命令,Shell命令实战详解
  5. si4438-IAR不能接收和发送的问题
  6. 如何判断微信内置浏览器(JS PHP)
  7. baidu patchrom项目开发详细教程(Being updated)
  8. 【链接保存】十分钟上手sklearn:特征提取,常用模型,交叉验证
  9. windows php sqlite,如何在Apache 2.4(Windows 7)上为PHP 5.6.14配置SQLite3?
  10. 如何能include外键对应的表?向博客园的兄弟请教!
  11. python读取json数据教程_Python教程之解析json数据
  12. django2.x/3.x 前端页面在debug模式中找不到动态文件static
  13. java定义整形输出_java程序命令行接受字符转换为整形并相加输出
  14. 计算机内存知识txt,计算机内存基础知识专题
  15. oracle dba开头的表,oracle中以dba_、user_、v$_、all_、session_、index_开头的常用表和视图...
  16. 线程池(ThreadPoolExecutor ) 的 创建、关闭、监控
  17. 医学遗传学词汇英语术语英文(Glossary) 5
  18. css实现LED液晶数码字体
  19. python函数注释:函数后面的箭头->
  20. Python怎么注册和调用大漠插件

热门文章

  1. opencv 学习1
  2. 安装和配置WINS 服务器
  3. 考研英语 - word-list-48
  4. Atitit 使用h5技术 html css js 制作桌面程序gui界面解决方案attilax总结
  5. 基于SCL语言的模拟量平均值滤波FB库功能介绍及创建FB库的具体方法
  6. 2023年全国最新道路运输从业人员精选真题及答案1
  7. 计算机毕业设计android的单词记忆英语考试系统app(源码+系统+mysql数据库+Lw文档)
  8. [境内法规]中国人民银行关于金融机构严格执行反洗钱规定防范洗钱风险的通知—银发2005
  9. 极限学习机(ELM)算法原理及C++代码实现
  10. 【单片机原理及其应用】第三章IO口的定义与使用