您现在的位置是:主页 > news > 集团企业网站设计方案/磁力在线搜索引擎
集团企业网站设计方案/磁力在线搜索引擎
admin2025/6/16 7:47:36【news】
简介集团企业网站设计方案,磁力在线搜索引擎,深圳专业优定软件网站建设,做h网站风险💭 写在前面 本篇博客将开始介绍 Pintos 的基本知识,为 Pintos 的 Project1 用户程序(User Program)做必要的只是铺垫,讲解 Pintos 运行原理、虚拟内存、页函数以及系统调用的实现。 📜 文章目录 0x00 背…
💭 写在前面
本篇博客将开始介绍 Pintos 的基本知识,为 Pintos 的 Project1 用户程序(User Program)做必要的只是铺垫,讲解 Pintos 运行原理、虚拟内存、页函数以及系统调用的实现。
📜 文章目录
0x00 背景介绍
0x01 用户程序是如何工作的(How User Program Works)
0x02 前置知识:Pintos 运行原理
0x03 代码实现
0x04 虚拟内存(Virtual Memory)
0x05 页函数(Functions for page)
0x06 系统调用(System Calls)
0x00 背景介绍
Pintos 是一个简单的操作系统,它可以启动,允许应用程序,关机。
你可以试着在 Pintos 上运行应用程序 "echo" :
① 首先在 src/examples 和 src/userprog 中 Makefile
- cd 至 src/examples 目录下,输入 make:
- cd 至 src/userprog 下,输入 make:
② 在 /src/userprog 路径下输入运行它
- 输入如下指令,利用 echo 打印 x (注意 -a echo 后面是两个 -)
~/pintos/src/userprog $
pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'
熟悉的 "Powering off..." 这个 echo 程序看上去已经运行成功了,但是似乎没有出现结果。
0x01 用户程序是如何工作的(How User Program Works)
仔细观察前面指令的细节:
~/pintos/src/userprog $
pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'
- --filesys-size=2: 生成大小为 2mb 的模拟 Pintos 磁盘(disk)。
- -p ../examples/echo -a echo: 将 .../examples/echo 复制到模拟磁盘,并将名称从 .../examples/echo 改为 echo 。
- -- :在 echo 和 -f 之间:将 pintos 的选项和内核参数分离开。
- -f:Pintos 对模拟磁盘进行格式化操作。
- -q:在用户执行完 echo 后 Pintos 将终止。
- run 'echo x' :Pintos 将以 x 作为参数,执行 echo。
❓ 再次思考:我们为什么看不看到 echo 命令的结果?
这是因为在目前的 Pintos 中,系统调用(system call)、系统调用处理程序(system call handler)、参数传递(argument passing)和 用户栈(user stack)都没有实现!
(这些都是要你动手操作的,加油)
可以说,目前的 Pintos 其实就是个空壳子,许多操作系统应具有的功能都没有,这些都是斯坦福专家们精心为你准备的练习,让你自己手糊一个操作系统玩玩。说得轻松,但是难度真的不容小觑!
0x02 前置知识:Pintos 运行原理
如果想明白的更透彻,我们这里可以了解一下 Pintos 的运行原理。
Pintos 可以加载和运行常规的 ELF(Executable & Linkable Format)可执行文件。
因此要运行一个用户程序,我们必须把用户程序复制到模拟磁盘上(可以参考刚才指令的细节)。
~/pintos/src/userprog $
pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'
- echo 是一个把参数写入标准输出(Standard Output)的应用程序。
- 因此,echo 需要内核中的系统调用所提供的
功能
- 并且还需要用到用户栈,在栈上存储参数并将它们传递给内核。
- 但是,当前的 Pintos 没有实现 系统调用 和 用户栈 。
这就是为什么我们看不到 echo x 结果的原因!
0x03 代码实现
溪云初起日沉阁,山雨欲来风满楼。
在这个项目中,我们需要让 Pintos 能够正确执行 "用户程序" ,我们应该在下列目录下完成,并修改下列文件:
代码级流程(Code Level Flow)
$ pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'
/* 運行 */
run_actions(argv);/* 終了 */
shutdown();
thread_exit();
💬 run_actions 函数:
run_actions(char** argv )
{/* An action */struct action {char* name;int argc;void(*function) (char ** argv);};/* Table of supported actions. */static const struct action actions[] = {{"run", 2, run_task},
#ifdef FILESYS{"ls", 1, fsutil_ls},
while(*argv != NULL) {const struct action* a;int i;/* Find action name. */for (a = actions; ; a++)if (a->name == NULL)PANIC("unknow action");else if (!strcmp(*argv, a->name))break;/* Check for required arguments. */for (i = 0; i < a->argc; i++) if (argv[i] == NULL)PANIC("action");/* Invoke action and advance. */a->function(argv);argv += a->argc;
}
💬 run_task 函数:
static void run_task (char** argv)
{const char* task = argv[1];printf("Excuting '%s': \n", task);
#ifdef USERPROGprocess_wait (process_execute(task));
#elserun_test(test);
#endifprintf("Execution of '%s' complete.\n", task);
}
💬 process_execute 函数:
tid_t process_execute ( const char* file_name)
{char* fn_copy;tid_t tid;/* Make a copy of FILE_NAME.Otherwise there's a race between the caller and load(). */fn_copy = palloc_get_page(0);if (fn_copy == NULL)return TID_ERROR;strlcpy(fn_copy, file_name, PGSIZE);/* Create a new thread to execute FILE_NAME. */tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy);if (tid == TID_ERROR)palloc_free_page (fncopy);return tid;
}
pintos 工具选项:
$ pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'👆 👆多字符选项 单字符选项
- - :表示单字符选项
- - - :表示多个字符的选项 (你可以通过执行 pintos --help 来阅读选项)
分隔符:
$ pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'👆[Pintos选项] 分隔符 [内核参数]
- - - :Pintos 选项和 Pintos 内核参数之间的分隔符
* 根据 80x86 调用约定设置用户栈。
0x04 虚拟内存(Virtual Memory)
Pintos 将内存分为两个区域,用户内存和内核内存。
如果我们直接使用这些内存区域,就很难管理内存,例如:
- 每个进程都可以相互销毁
- 进程可以破坏对运行操作系统至关重要的内核代码
为了防止这些问题,操作系统采用了 虚拟内存系统(virtual memory system)。
由于虚拟内存的存在,每个进程都可以拥有自己专属的内存区域,并且能够使用它,就像该进程享用了整个内存一样。
而 Pintos 也是采用虚拟内存来管理内存区域的。
🔺 虚拟内存会被划分为两个区域:用户虚拟内存、内核虚拟内存。
虚拟内存:启动应用程序
内核虚拟内存是全局的,
Pintos 中的虚拟内存
- 每个进程都有自己的用户虚拟内存。
- Pintos分配给内核1GB作为全局内存。(PHYS_BASE (3 GB) ~ 4 GB的虚拟内存)
- 内存单元在Pintos中是一个页,大小为4KB。
- 用户程序可以通过页目录和页表翻译虚拟地址来访问物理内存。(参考上图 "页表")。
0x05 页函数(Functions for page)
threads/palloc.c
is_user_vaddr() // 检查给定的虚拟地址是否是用户虚拟地址
is_kernel_vaddr() // 检查给定的虚拟地址是否是内核虚拟地址ptov() // 将物理地址转换为内核虚拟地址
vtop() // 将内核虚拟地址转换为物理地址
threads/palloc.c
palloc_get_page() // 从用户或内核内存池中获取页
userprog/pagedir.c
pagedir_create() // 创建页表
pagedir_get_page() // 查询页面目录中与用户虚拟地址相对应的物理地址
pagedir_set_page() // 在页面目录中添加从用户虚拟地址到物理页面的映射
0x06 系统调用(System Calls)
正如我们所见,Pintos 将内存分为 用户虚拟内存 和 内核虚拟内存,以保护每个进程和内核代码。
随着虚拟内存的概念,操作系统防止用户程序访问包含核心功能的内核内存。
那么用户程序该如何使用内核的功能呢?
💡 操作系统提供了 系统调用(System Calls)来解决这个问题!
- 出于安全性考虑,操作系统提供了两种模式 —— 用户模式 和 内核模式。
- 当用户程序在用户模式下运行时,它不能访问内存或磁盘。
- 这些操作是在 内核模式 下进行的。
- 操作系统提供了进入内核模式的 系统调用 。
Pintos 在 lib/user/syscall.c 中提供了系统调用的用户级接口。
在 userprog/syscall.c 中提供了系统调用处理器的骨架。
Pintos 中的系统调用程序
- 用户程序调用系统调用的函数
#include <stdio.h>
#include <syscall.h>int main(int argc, char* argv[])
{bool success = true;int i;for (i = 1; i < argc; i++){int fd = open(argv[i]);if (fd < 0) {printf("%s: open failed\n", argv[i]);success = false;continue;}
- 系统调用号 和 附加 参数 被压到调用者的栈中。
- 通过使用 int $0x30 指令调用系统调用的中断
int open(const char* file)
{return syscall1 (SYS_OPEN, file);
}
/* Invokes syscall NUMBER, passing argument ARGO, and returns the return value as and 'int'. */#define syscall1(NUMBER, ARG0) \( int retval; \asm volatile("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \// 从系统调用处理程序返回后,恢复栈指针。: "=aa" (retval): [number] "i" (NUMBER), \[arg0] "g" (ARG0) \: "memory"); \retval; \} )
- 为了能够中断,设置栈并调用中断处理程序
.func intr_entry
intr_entry:/* 保存调用者的寄存器 */pushl %dspushl %espushl %fspushl %gspushal/* 设置内核环境 */cld // 字符串指令向上走mov $SEL_KDSEG, %eax // 初始化段寄存器 mov %eax, %dsmov %eax, %esleal 56(%esp), %edp // 设置框架指针/* 调用中断处理程序 */push %esp
.glovl intr_handlercall intr_handler // Call interrupt handlerandl $4, %esp
.endfunc
* source 代码位置: threads/intr-stubs.S
- intr_handler() 调用中断处理程序
void intr_handler(struct intr_frame* frame)
{bool external;intr_handler_func* handler;/* 外部中断很特别我们一次只能处理一个(所以中断必须关闭)而且它们需要在PIC上被确认(见下文)一个外部中断处理程序无法休眠。 */external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;if (external) {ASSERT (intr_get_level() == INTR_OFF);ASSERT (!intr_context());in_external_intr = true;yield_on_retrun = false;}/* 启用中断处理程序 */handler = intr_handlers[frame->vec_no];if (handler != NULL)handler(frame);
在 Pintos 启动时,系统调用的中断处理程序就已经被注册了。
* 参考以下函数调用:
- main() in 'threads/init.c' calls syscall_init() which is in 'userprog/syscall.c'
- syscall_init() calls intr_register_int() in 'threads/interrupt.c'
- syscall_handler() 得到控制权,它可以通过 intr_frame 结构的 'esp' 成员访问栈(在threads/interrupt.h。
- 80x86 的惯例是将系统调用的返回值存储在 EAX 寄存器中,因此我们可以将返回值存储在intr_frame 结构的 'eax' 成员中。
static void syscall_handler(struct intr_frame* f UNUSED)
{printf("system call!\n");thread_exit();
}
* Pintos 提供了系统调用处理程序的骨架,我们将在这个项目中开发它。
struct intr_frame
{/* 由intr-stubs.S中的 intr_entry 推送,这些是保存被中断任务的寄存器。 */uint32_t ebx; // 保存EBXunit32_t edx; // 保存EDXunit32_t ecx; // 保存ECXunit32_t eax; // 保存EAXvoid* esp; // 保存栈指针uint16_t ss, :16; // esp的数据段
};
需要修改的: syscall.c process.c
📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 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/. |