编程题
第一题
问题描述
实现一个linux应用程序A,显示当前目录下的文件名。(用C或Rust编程)
代码实现
这里我选择使用c语言实现本题
输出结果
第二题
问题描述
实现一个linux应用程序B,能打印出调用栈链信息。(用C或Rust编程)
代码实现
这里我选择使用c语言实现本题
运用"gcc -g question1-2.c -o question1-2"指令对于该c代码进行编译
输出结果
采用"gdb question1-2"进行gdb调试
输入“run”;
可以看到打印出的代用栈链信息如下:
问答题
第一题
问题描述
应用程序在执行过程中,会占用哪些计算机资源?
题目解答
- CPU:应用程序需要中央处理器(CPU)来执行其指令,这是应用程序运行所需的最基本资源。
- 内存:应用程序需要内存来存储其代码、数据和运行时状态。应用程序使用内存来处理数据和执行操作,因此需要足够的内存才能正常运行。
- 硬盘空间:应用程序需要在硬盘上存储其代码、数据和其他文件,例如配置文件、日志文件等。
- 网络带宽:如果应用程序需要与其他计算机或服务器进行通信,那么它需要网络带宽来传输数据。
- 图形处理器(GPU):某些应用程序需要使用GPU来处理图像、视频或其他图形相关的任务。
- I/O设备:应用程序需要使用输入/输出设备,如鼠标、键盘、摄像头等,以便与用户进行交互或者读取数据。
综上所述,应用程序执行过程中需要占用多种计算机资源,其中最基本的包括CPU、内存和硬盘空间。
第二题
问题描述
请用相关工具软件分析并给出应用程序A的代码段/数据段/堆/栈的地址空间范围。
题目解答
使用 readelf 工具查看地址空间:
数据段(.data)和代码段(.text)的起止地址可以从输出信息中看出。
数据段(.data):
代码段(.text):
使用Linux系统的pmap命令查看一个正在运行的进程的地址空间映射。
第三题
问题描述
请简要说明应用程序与操作系统的异同之处
题目解答
不同点:
- 作用范围不同:操作系统是计算机系统中的核心软件,负责管理硬件、协调各种应用程序的运行,为应用程序提供API,以及提供基本的服务和资源管理;而应用程序是用户需要的具体功能软件,运行在操作系统之上,用于完成各种特定的任务。
- 执行方式不同:操作系统作为底层软件,与硬件直接交互,而应用程序则是由操作系统加载并执行的。操作系统通常运行在系统内核态,具有更高的权限,而应用程序则运行在用户态,权限相对较低。
- 功能目标不同:操作系统的主要目标是提供稳定、高效、安全的计算环境,为各种应用程序提供基础服务和资源管理;而应用程序则是为了满足用户的具体需求,提供特定的功能和服务。
相同点:
- 都是由代码写成的程序;
- 都需要占用系统资源;
- 都需要硬件的支持;
第四题
问题描述
请基于QEMU模拟RISC—V的执行过程和QEMU源代码,说明RISC-V硬件加电后的几条指令在哪里?完成了哪些功能?
题目解答
在 QEMU 源码中可以找到“上电”的时候刚执行的几条指令,如下:
实现的功能是:
- 读取当前的 Hart ID CSR mhartid 写入寄存器 a0;
- (我们还没有用到:将 FDT (Flatten device tree) 在物理内存中的地址写入 a1);
- 跳转到 start_addr ,在我们实验中是 RustSBI 的地址;
第五题
问题描述
RISC-V中的SBI的含义和功能是什么?
题目解答
SBI是RISC-V标准的一个重要组成部分,全称为"Supervisor Binary Interface"。它定义了一个用于操作系统与处理器之间进行通信的标准接口,包含一系列的SBI函数,操作系统可以通过这些函数与处理器进行交互,从而实现一些特权级别的操作。
SBI可以提供以下功能
- 进入和退出Supervisor模式:通过SBI函数,操作系统可以将处理器从User模式切换到Supervisor模式,并在完成一些特权操作之后再切换回User模式。
- 中断控制:SBI可以提供一些函数用于开启、关闭、处理、查询中断。
- 内存管理:SBI提供了函数,可以完成虚拟地址和物理地址的转换,以及一些与页面和内存管理相关的操作。
- 外设访问:SBI可以提供一些函数,让操作系统可以访问和管理处理器外部的一些设备,如串口、网卡、时钟等。
SBI在操作系统开发中扮演了一个非常重要的角色,它为操作系统提供了一种可靠、可移植、高效的访问底层硬件资源的方式,同时也为处理器的特权级别切换、中断处理等提供了一套规范的接口,使得操作系统的开发变得更加方便和可靠。
第六题
问题描述
为了让应用程序能在计算机上执行,操作系统与编译器之间需要达成哪些协议?
题目解答
- 二进制文件格式协议:操作系统和编译器需要约定好可执行文件的二进制格式,使得操作系统能够正确地加载和解析应用程序的二进制代码。通常使用的可执行文件格式包括ELF、COFF、Mach-O等。
- 调用约定协议:操作系统和编译器需要约定好函数的参数传递和返回值的方式,以及栈的使用规则等。不同的操作系统和体系结构可能会有不同的调用约定,例如x86-64平台的调用约定为System V ABI。
- 系统调用协议:操作系统需要提供一组系统调用接口,使得应用程序可以通过操作系统访问系统资源,如文件、网络、内存等。编译器需要支持这些系统调用,并且将应用程序中的系统调用转化为相应的汇编指令。
- 内存管理协议:操作系统和编译器需要约定好虚拟内存的管理方式,包括地址空间的分配、内存映射、内存保护等。
- 运行时环境协议:操作系统和编译器需要约定好运行时库的接口和行为,例如动态链接器的规范,以及C语言标准库的实现等。
这些协议的约定使得操作系统和编译器能够正确的协同工作,使得应用程序能够顺利地在计算机上执行。同时,这些协议的设计也为计算机系统的可移植性和扩展性提供了基础。
第七题
问题描述
请简要说明从QEMU模拟的RISC-V计算机加电开始运行到执行应用程序的第一条指令这个阶段的执行过程。
题目解答
- 加电启动:QEMU模拟器会加载RISC-V计算机的BIOS固件,通过解析固件中的启动代码,初始化计算机的各种硬件设备,包括CPU、内存、磁盘等。
- 执行引导程序:BIOS固件会在计算机内存中加载引导程序,该程序负责加载操作系统内核到内存中,并将控制权转交给内核。
- 加载内核:引导程序会从磁盘中读取操作系统内核的镜像文件,并将其加载到内存中的指定位置。
- 初始化内核:内核启动后会对硬件设备进行初始化,并分配进程的虚拟地址空间和栈空间,为应用程序的运行做好准备。
- 加载应用程序:操作系统会根据应用程序的信息,在指定的虚拟地址空间中加载应用程序的代码和数据等,并为其分配栈空间。
- 执行应用程序:CPU根据操作系统加载的应用程序信息,从指定的虚拟地址空间中读取应用程序的第一条指令,开始执行应用程序。
在这个过程中,QEMU模拟器提供了一个环境,使得我们可以通过操作与硬件之间的接口来管理硬件资源和执行应用程序。这个过程的每个步骤都是必要的,它们构成了操作系统启动和应用程序执行的完整流程。
第八题
问题描述
为何应用程序员编写应用时不需要建立栈空间和指定地址空间?
题目解答
因为操作系统会为每个进程分配独立的虚拟地址空间和栈空间,使得不同的进程之间互不干扰。
在现代操作系统中,进程的虚拟地址空间通常由操作系统负责管理,应用程序员只需要将其编写为独立的可执行文件,并由操作系统加载到内存中运行即可。操作系统会为每个进程分配一段独立的虚拟地址空间,包括代码段、数据段、堆空间和栈空间等,使得进程之间互不干扰。
操作系统也会为每个进程分配一段独立的栈空间,用于存储函数调用和局部变量等。当进程执行函数调用时,操作系统会自动将函数参数和返回地址等信息压入该进程的栈中,并为函数分配一段独立的栈空间。当函数执行完毕后,操作系统会自动将栈空间中的数据弹出,并跳回到原函数调用点。
第九题
问题描述
现代的很多编译器生成的代码,默认情况下不再严格保存/恢复栈帧指针。在这个情况下,我们只要编译器提供足够的信息,也可以完成对调用栈的恢复。根据给出这些信息,调试器可以如何复原出最顶层的几个调用栈信息?假设调试器可以理解编译器生成的汇编代码
题目解答
- 首先,我们当前的pc在flip函数的开头,这是我们正在运行的函数。返回给调用者处的地址在ra寄存器里,是0x10742.因为我们还没有开始操作栈指针,所以调用处的sp与我们相同,都是0x40007f1310。
- 0x10742在flap函数内。根据flap函数的开头可知,这个函数的栈帧大小是16个字节,所以调用者处的栈指针应该是sp + 16 = 0x40007f1320。调用flap的调用者返回地址保存在栈上8(sp),可以读出来是0x10750,还在flap函数内。
- 依次类推,只要能理解已知地址对应的函数代码,就可以完成恢复操作。
实践作业
题目要求
题目原理
要想实现这个题目要求,我们就先要搞明白我们应该在哪里作出修改;
在移除标准库依赖这一小节中,我们先移除了println!宏
基于 SBI 服务完成输出和关机这一小节中,讲述了如何实现我们自己的print!宏和println!宏
所以我们要实现所要求的彩色输出,需要修改的就是我们自己实现的宏println!;
题目解答
对于println!宏的修改如下:
这里我选择使用亮黄色来对于println的内容上色,以下是再次make run后的输出结果: