【OS Pintos】Project1 项目要求说明 | 进程中止信息 | 参数传递 | 用户内存访问 | 有关项目实现的建议

简介: 【OS Pintos】Project1 项目要求说明 | 进程中止信息 | 参数传递 | 用户内存访问 | 有关项目实现的建议

💭 写在前面

本篇博客将对 Pintos 的 Project1 项目的实现要求进行说明。建议阅读上篇铺垫内容并阅读 Pintos 手册,了解 Pintos 项目的 "游戏规则"。



0x00 进程终止信息(Process Termination Messages

当用户程序被终止(terminated)时,内核会打印终止信息,输出形式如下所示:

Process Name: exit(exit status)\n

(具体参考 Pintos 手册 3.3.2)

❓ 思考:用户程序是如何终止的呢?

当 ELF 用户程序运行时,lib/user/entry.c 中的_start() 首先被调用:

void _start(int argc, char* argv)
{
    exit (main (argc, argv));
}
  • 在执行程序后,exit() 系统调用被调用。
  • Pintos 只提供了 exit() 系统调用 API 接口,但 exit() 系统调用还没有实现。

我们如何得到一个进程的名称?

struct thread
{
    /* 拥有者为 thread.c  */   
    tid_t tid;
    enum thread_status status;
    char name[16];

用户程序又是如何终止的?lib/user/syscall.c exit() 的函数调用流程:

  • -> lib/user/syscall.c 中的 syscall1 (SYS_EXIT, status)
  • -> userprog/syscall.c 中的 syscall_handler()
  • -> threads/thread.c 中的 thread_exit()
  • -> userprog/process.c 中的 process_exit()

(具体请参考25-29页

0x01 参数传递(Argument Passing)

用户程序可以有多个参数:

根据 80x86 的调用惯例,解析参数并将其分配到内存中(参考 Pintos 手册 3.5)

假设参数的长度小于 4KB

  • 测试程序使用少于 128 字节的参数。

 

/bin/ls -l foo bar 将被解析为 /bin/ls-lfoobar

你可以在以下函数之后开始实现参数传递:

userprog/process.c : static bool setup_stack(void **esp)

请参考上一节的 "代码级流程" :

我们刚才说了, /bin/ls -l foo bar 将被解析为 /bin/ls-lfoobar,将参数推到栈顶:

hex_dump() 的结果,这个函数对调试非常有用(在 src/lib/stdio.c 中):

userprog/process.c 中,有 setup_stack() ,它分配了一个最小的栈页(4KB)。

由于给定的代码只分配了栈页,我们需要在 setup_stack() 之后补上栈。

(参照 Pintos 手册中的 "3.5 80x86 调用约定" 来构成栈)

0x02 系统调用:基本实现(System Calls

你需要实现下列系统调用(每个系统调用的要求在 Pintos 手册 3.3.4 中有描述):

实现:halt, exit, exec, wait, read(stdin), write(stdout)

📌 注意:Pintos exec 不同于 UNIX exec()

两个新的系统调用(fibonacci, max_of_four_int),读和写是这个项目中的特殊情况。

与文件系统相关的系统调用不需要在本项目中实现:

选择实现:create, remove, open, filesize, read, write, seek, tell, close

但是,读和写至少应该做到能执行标准流的输入输出!

halt 函数

halt()    // 通过调用 shutdown_power_off() 终止Pintos。
  • 该函数调用 shutdown_power_off() 函数,终止 Pintos。

exit 函数

exit()    // 终止当前用户程序,并将状态返回给内核
  • 终止当前用户程序,并将状态返回给内核。

exec 函数

exec()
  • 创建子进程
  • 参考 userprog/process.c 中的 process_execute()

wait 函数

wait()
  • wait() 系统调用要做的是礼貌地等待子进程,直到它完成其工作。
  • 需要检查子线程的 ID 是否有效
  • 当子线程寄了,你需要从子线程获取退出状态
  • 为了防止程序在 wait() 返回之前就终止进程,你可以使用自旋锁与忙等待技术,或使用在threads/thread.cthread_yield() 函数。

💡 神奇海螺:什么也不做。"忙等待技术" 就是代码什么也不做,只是不断地检查状况。

write 和 read 函数

write() 
read()

虽然没有完全实现,但至少可以从 STDIN 读,写到 STDOUT。

文件描述符:STDIN,STDOUT

  • STDIN = 0,STDOUT = 1

使用下面的函数来实现 read(0)

  • pintos/src/devices/input.c路径下的 uint8_t input_getc(void) 函数

使用下面的函数来实现 write(1)

  • pintos/src/lib/kernel/console.c 路径下的 void putbuf(...)  函数

代码级流程

  • 当 ELF 可执行程序(用户程序)完成后,exit() 系统调用 被调用。
  • exit() 系统调用后,返回到 process_wait() 。(exit 调用完成后系统进入进程等待状态)

源代码(Source Code)

lib/user/syscall.h lib/user/syscall.c

  • 系统调用的 API 接口已经在 Pintos 代码中给好了
  • 这意味着我们不需要为系统调用的 API 添加任何东西

userprog/syscall.h

  • 这里只有一个 syscall_init() 的样板 ,在 Pintos 启动时注册系统调用中断
  • 我们可以在这个文件中编写系统调用的原型

userprog/syscall.c

  • 必须钦定 syscall_handler() 大手子去处理系统调用
  • 如果已经做了参数传递,可以从 intr_frame* f 中获得系统调用号
  • intr_frame* f 的 esp 成员指向系统调用号。(可以参考 lib/syscall-nr.h 来检查每个系统调用号)
  • 然后你可以用 switch 语句对系统调用进行分类(分清这些系统调用到底是做什么的会写在这里)

0x03 系统调用:额外实现(Additional System Calls)

试着在 pintos 中写几个新的系统调用接口:

  • 斐波那契数列的第 项:
int fibonacci(int n)   // 返回斐波那契数列的第n项
  • 返回四个整数的最大值:
int max_of_four_int(int a, int b, int c, int d)  // 返回 a b c d 中的最大值

      * 命名请使用 fibonacci max_of_four_int ,不要使用其他名称!

 

  • 编写用户级程序,使用新的系统调用

pintos/src/examples 中制作 additional.c  (执行文件的名称必须是 "additional")

通过使用新的系统调用编写简单的例子,用法:./additional [num 1] [num 2] [num 3] [num 4]

功能:使用 [num 1] 作为参数打印 fibonacci 系统调用的结果;

使用 [num 1, 2, 3, 4] 作为参数打印 max_of_four_int 系统调用的结果。

你可以运行以下命令以检查你的程序是否正常工作:

pintos/src/userprog$ pintos --filesys-size=2 -p ../examples/additional -a additional -- -f -q run 'additional 10 20 62 40'

* additional 需要在 Pintos 里运行

📌 注意:为了编译新增加的用户程序 additional ,你需要修改 src/examples 中的 Makefile,参照 Makefile中其他用户程序的编写方式去修改。

源代码(Source Code)

lib/user/syscall.h

编写2个新的系统调用API的原型

lib/user/syscall.c

max_of_four_int() 定义新的 syscall4() 函数(lib/user/syscall.c)

定义 fibonacci() max_of_four_int() 的系统调用 API

lib/syscall-nr.h

为 2 个新的系统调用添加系统调用号

userprog/syscall.h

编写 2 个新的系统调用的原型

userprog/syscall.c

定义 fibonacci()max_of_four_int() 系统调用

这些系统调用到底是做什么的,会写在这里。

0x04 用户内存访问(Accessing User Memory)

用户程序可以传递一个无效的指针。

  • 空指针,如 tests/userprog/open-null.c 中的 open (NULL)  
  • 未映射的虚拟内存
  • 指向内核地址空间的指针

无效的指针(Invalid pointers)必须在不损害内核或其他运行进程的前提下被拒绝。

  • 它可以通过 2 种方式实现
  • 验证用户提供的指针的有效性validity of a user-provided pointer),然后取消对它的引用。
  • 只检查一个用户指针是否指向 PHYS_BASE 下面,然后取消对它的引用。如果该指针无效,将导致缺页异常(page fault)。你可以通过修改 userprog/exception.c 中的 page_fault() 代码来处理它。

* 参考 Pintos 手册 3.1.5

  • 可以利用 userprog/pagedir.c threads/vaddr.h 中的函数验证用户提供的指针的有效性
  • 利用 pagedir_get_page() 函数检查未映射的虚拟内存
  • 利用 is_user_vaddr() is_kernel_vaddr() 函数检查指向内核地址空间的指针
  • 使用这些函数来验证给定指针的有效性

0x05 实现的先后顺序(建议)

在你实现核心功能之前,你无法看到结果。

强烈建议先阅读/理解上面说的内容和 Pintos 手册,设计好结构,然后开始编写。

Step1:参数传递:实现后,可用 hex_dump() 函数检查结果。

请参考代码级流程

可用 src/userprog/process.c load() 检查 load() 的参数

如果你想在执行 process_wait() 之前检查 dump values,可暂时把 process_wait() 改成无限循环,以阻止进程,便于测试检查。    ( 等后面完成 process_wait() 的实现后再说)

Step2:用户内存访问保护用户内存访问不受系统调用的影响。

参考 src/threads/vaddr.h

建议实现检查给定地址有效性的函数。

Step3:系统调用处理程序实现 syscall_handler() 函数来处理系统调用。

src/userprog/syscall.c syscall_handler() 函数
检查
syscall.c syscall_handler() 的参数 struct intr_frame

struct intr_frame src/threads/interrupt.h 中)

Step4:系统调用实现先实现完 exec()exit()write()read(),再实现其它的。

将需要进行同步化 (可以选择使用忙等待)
syscall_handler 以非正常方式终止时,退出状态为 -1 。

Step5:额外实现最后再实现 fibonacci()max_of_four_int()

修改以下内容:

src/lib/syscall-nr.h
src/lib/syscall.h
src/lib/syscall.c

* 可参考 src/tests/userprog 路径下的源代码,Refer to Pintos manual 3.2

🚩 评分(Evaluation)

  • 在这个项目中,76 个测试中有 21 个将计入评分。
  • 总分是 100 分,包括 80 分的测试案例和20分的文档。
  • 每一个额外的系统调用的实现都会有额外的2.5分。(实现中的 fibonacci() max_of_four_int() 有5分)※ 这将在开发部分(80%)计算,所以总分将是 4 分(5*80%)。
  • 将使用 Pinots 提供的评分脚本(make grade 或 make check in src/userprog)。
  • 在评分之后,请参考 src/userprog/build 中的 '成绩' 和 '结果'文件( '成绩' 文件只有在你使用 make grade 时才会被创建)。
  • 测试用例被分为功能测试和稳健性测试,请参考以下内容,根据测试类型检查每个测试案例的要点:
  • pintos/src/tests/userprog/Rubric.functional.
  • pintos/src/tests/userprog/Rubric.robustnesss
  • 功能性和稳健性分别占总分的50% (... SeeMore)

* Refer to Pintos manual 1.2.1

如果你看到 src/tests/userprog/Grading,功能测试集占总分的 35%,健壮性测试集占总分的25%.

📃 评分方式如下:

(剩余的 20%是留给项目报告的,占总分的20%)

 

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2022.9.24
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau, Operating Systems: Three Easy Pieces

A. Silberschatz, P. Galvin, and G. Gagne,

Operating System Concepts, 9th Edition, John Wiley & Sons, Inc., 2014, ISBN 978-1-118-09375-7.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

相关文章
|
8天前
|
算法 调度 UED
深入理解操作系统内存管理:原理与实践
【4月更文挑战第23天】 在现代计算机系统中,操作系统的内存管理是保证系统高效、稳定运行的关键组成部分。本文旨在深入探讨操作系统中内存管理的理论基础、关键技术以及实际操作过程,通过对内存分配策略、虚拟内存技术、分页与分段机制等核心概念的详细解析,为读者提供一个清晰、全面的内存管理视角。此外,文章还将通过案例分析,展示内存管理在解决实际问题中的应用,以期加深读者对操作系统内存管理复杂性的认识和理解。
|
24天前
|
算法 程序员
深入理解操作系统内存管理:分页系统的优势与挑战
【4月更文挑战第7天】 在现代操作系统中,内存管理是一项至关重要的任务,它确保了计算机能够高效、安全地运行各种程序。分页系统作为内存管理的一种技术,通过将物理内存分割成固定大小的单元——页面,为每个运行的程序提供了一种独立且连续的内存地址空间。该技术不仅简化了内存分配,还允许更高效的内存使用和保护。本文探讨了分页系统的核心原理,优势以及面临的挑战,旨在为读者揭示其在操作系统设计中的重要性。
|
4天前
|
缓存 算法 调度
深入理解操作系统的内存管理机制
【4月更文挑战第27天】 在现代计算机系统中,操作系统扮演着至关重要的角色,尤其在资源管理和调度方面。内存管理是操作系统的核心功能之一,它负责分配、跟踪和回收应用程序使用的物理内存。本文将探讨操作系统如何通过不同的内存管理技术来优化内存使用效率,包括分页、分段以及虚拟内存等概念。通过对这些技术的深入分析,读者将获得对操作系统内部工作原理的更深刻理解,并了解它们如何影响应用程序性能和系统稳定性。
|
1天前
|
机器学习/深度学习 自动驾驶 安全
深入理解操作系统内存管理:策略与实现基于深度学习的图像识别技术在自动驾驶系统中的应用
【4月更文挑战第30天】 在现代计算机系统中,操作系统的内存管理是确保系统高效、稳定运行的关键组成部分。本文将深入探讨操作系统中内存管理的多种策略及其实现机制,包括但不限于分页、分段和段页式结合等技术。我们将剖析内存分配的原理,讨论虚拟内存技术的实现以及它如何提供更大的地址空间并允许内存的交换。同时,我们还会涉及内存保护机制,它们是如何防止程序访问未授权的内存区域。最后,文中将对现代操作系统如Linux和Windows中的内存管理实践进行比较分析,以期给读者提供全面而深入的理解和参考。 【4月更文挑战第30天】 随着人工智能技术的飞速发展,深度学习已经
|
1天前
|
安全 Linux Windows
深入理解操作系统内存管理:分页与分段的融合
【4月更文挑战第30天】 在现代操作系统中,内存管理是确保多任务环境稳定运行的关键。本文将深入探讨分页和分段两种内存管理技术,并分析它们如何相互融合以提供更为高效、安全的内存使用策略。通过对比这两种技术的优缺点,我们将探索现代操作系统中它们的综合应用,以及这种融合对操作系统设计和性能的影响。
|
1天前
|
算法 安全 Android开发
深入理解操作系统的内存管理机制构建高效Android应用:Kotlin的协程优势
【4月更文挑战第30天】 在现代计算机系统中,操作系统的内存管理是确保系统高效、稳定运行的关键。本文将探讨操作系统内存管理的核心技术,包括内存分配、虚拟内存、分页和分段等概念,以及它们是如何协同工作以提高内存利用率和系统性能的。通过对这些技术的详细分析,我们可以更好地理解操作系统背后的原理,并评估不同内存管理策略对系统行为的影响。 【4月更文挑战第30天】 在移动开发领域,尤其是针对Android平台,性能优化和流畅的用户体验始终是开发者追求的核心目标。随着Kotlin语言的普及,协程作为其在异步编程领域的杀手锏特性,已经逐渐成为提高应用性能和简化代码结构的重要工具。本文将深入探讨Kotli
|
4天前
|
存储 算法 安全
深入理解操作系统的内存管理机制
【4月更文挑战第27天】 本文将探讨操作系统中一个至关重要的组成部分——内存管理。我们将深入分析内存管理的基本原理,包括分页、分段和虚拟内存的概念,以及它们如何共同作用以支持现代多任务操作系统。文章还将讨论内存管理的关键性能指标,如页面置换算法的效率对系统响应时间的影响,以及内存碎片问题的解决方案。通过对这些高级概念的剖析,读者将获得操作系统内存管理机制深层次的认识。
|
5天前
|
算法
深入理解操作系统的内存管理
【4月更文挑战第26天】 在现代计算机系统中,操作系统的内存管理是确保系统高效、稳定运行的关键组成部分。本文将深入探讨操作系统内存管理的核心技术,包括虚拟内存、物理内存分配策略、分页和分段机制以及内存交换技术。通过分析这些技术的工作原理及其优缺点,读者将获得对操作系统如何优化内存使用和管理的深刻理解。
|
6天前
|
算法
探索现代操作系统的虚拟内存管理
【4月更文挑战第25天】 操作系统的心脏——虚拟内存管理,是确保多任务并发执行和系统稳定性的关键。本文将深入剖析虚拟内存的核心机制,包括分页、分段、请求调页以及交换技术。我们将探讨虚拟内存如何允许操作系统使用有限的物理内存来模拟更大的地址空间,以及这一过程对性能的影响。此外,文章还将介绍一些高级话题,比如内存分配策略、页面置换算法以及虚拟内存的优化方法。
|
7天前
|
存储 算法
深入理解操作系统的内存管理机制
【4月更文挑战第24天】 在现代计算机系统中,操作系统扮演着资源管理者的角色,其中内存管理是其核心职责之一。本文将探讨操作系统如何通过内存管理提升系统性能和稳定性,包括物理内存与虚拟内存的概念、分页机制、内存分配策略以及内存交换技术。我们将透过理论与实践的结合,分析内存管理的关键技术及其对系统运行效率的影响。
10 0