pintos代码整体阅读

前言

pintos文件夹内共有下面几个文件,最主要的是src,其他的都是一些说明文件和测试文件。
在这里插入图片描述

src

src中有以下几个文件
在这里插入图片描述
threads负责内核的基本代码
userprog负责程序加载器
vm负责虚拟内存管理,目前为空
filesys是基本文件系统的源代码
devices是I/O设备接口的源代码,在project1中修改timer
lib标准C库的子集的实现
lib/kernel是C库中仅包含在pintos内核中的部分
lib/user是C库中仅包含在pintos用户程序的部分
tests用来对每个项目进行测试
examples是从项目2开始使用的示例用户程序

threads

在这里插入图片描述
线程四种状态
THREAD_RUNNING:运行中,占用CPU
THREAD_READY:就绪,对应ready队列中所有的线程,一切就绪等待CPU
THREAD_BLOCKED:阻塞,等待I/O、被睡眠或者其他事件,这些事件完成之后又转换为THREAD_READY进入等待队列等待CPU
THREAD_DYING:销毁,线程结束

线程状态切换
RUNNING -> READY:时间片到期、被高优先级进程抢占,thread_yield实现
RUNNING -> BLOCKED:线程被休眠、等待某资源
RUNNING -> DYING:出现故障、进程结束
BLOCKED -> READY:I/O结束、资源得到满足
READY -> RUNNING:线程被调度,获得CPU

init.c

ram_init();初始化BSS
argv=read_command_line();读取命令行
argv=parse_options(argv);分析命令行
thread_init();初始化主线程
console_init();终端初始化
palloc_init();malloc_init();paging_init();初始化内存
intr_init();timer_init();kbd_init();input_init();初始化中断及时钟
thread_start();serial_init_queue();timer_calibrate();开启线程调度、开中断
run_actions(argv);运行程序(应用及测试)

thread.c

ready_list就绪队列一直保持按照优先级priority高低排序。每一次新线程加入ready_list时排序
创建线程:在thread_create()中thread_yield,按照priority大小排序加入ready队列中
更新新优先级:重新进行thread_yield——线程状态(RUNNING -> READY),让被改变了优先级的线程从CPU中移出进入ready队列中进行优先级的重新排序
信号量唤醒:优先唤醒高优先级的线程
条件变量唤醒:优先唤醒高优先级的线程
优先级捐赠问题:解决优先级翻转问题,在锁lock结构的基础上获取

thread.h

内核线程结构
在这里插入图片描述

vaddr.h

虚拟地址
pintos中的虚拟地址为32位,分为20位页码和12位页偏移量
宏定义:虚拟地址中偏移部分的位索引、位掩码、页面大小等
函数:获取页面、页偏移量,返回虚拟页面的起始等
虚拟内存
虚拟内存分为两个区域:用户虚拟内存和内核虚拟内训。

userprog

在这里插入图片描述
exception.c/h 用户进程执行特权或禁止操作时,这些文件用于处理异常
gdt.c/h 全局描述符表(GDT)是描述正在使用的段的表
pagedir.c/h 80x86硬件页表管理器
process.c/h 加载ELF二进制文件并启动进程
syscall.c/h 系统调用所需要的代码
tss.c/h 任务状态段(TSS)用于80x86体系结构任务切换

process.c

process_exit
每当一个用户进程因为该进程调用exit或其它原因而结束时,需要打印该进程的进程名和退出码。同时,在结束一个进程时,我们要释放该进程占用的所有资源。
process_execute
把传入的参数file_name用strtok_r函数分隔开,获得线程名,为接下来的参数传递做准备。并以此为线程名创建一个新线程,然后新线程转去执行start_process函数。若子进程加载可执行文件的过程没有问题,则返回新建线程的tid.在这之前,父进程无法返回。这样的同步操作是依靠struct thread里的success变量以及信号量的增减实现的。success记录了线程是否成功执行,而通过创建子进程时父进程信号量减少、子进程结束时父进程信号量增加来实现父进程等待子进程的效果,并保证子进程结束唤醒父进程。
push_argument
简单来说,这个函数完成了根据argc的大小将argv数组压入栈的操作。在2.3start_process函数中被调用。压入栈顶的过程中,依次存放入参数argv数组、argv的地址和argc的地址。
start_process
对于刚刚新创建的线程,先初始化中断帧,再调用load函数。如果调用成功,分配好了地址空间并创建完成用户栈,则调用push_argument把argv数组压入栈顶,否则就退出当前的线程。
** load**
为当前用户程序分配内存,初始化页目录。之后调用setup_stack创建用户栈,并把参数传入setup_stack.创建用户栈,并返回创建是否成功的状态。
process_wait
整个函数的功能是等待tid为child_tid的子进程,记录它的退出状态。
其中,首先循环当前进程的子进程列表,找到要wait的那个子进程。然后判断它是否已经运行结束:如果已经结束则减少子进程信号量以唤醒父进程,再从子进程列表中删除该子进程并返回它的退出状态;如果还在运行则返回-1(没有找到child_tid也返回-1)。

syscall.c

syscall_init
初始化系统调用,通过syscall数组来存储13个系统调用,在syscall_handler里通过识别数组的序号决定调用哪一个系统。
syscall_handler
用户的命令会被中断识别,并把命令的参数压入栈。所以系统调用的类型是存放在栈顶的。在syscall_handler中,我们弹出用户栈参数,将这一类型取出,再按照这个类型去查找在syscall_init中定义的syscalls数组,找到对应的系统调用并执行它。
sys_halt
调用shutdown_power_off让pintos关机。
sys_exit
结束当前的用户程序,并返回状态给内核kernel. 这里先检查栈指针的合法性,然后将当前线程的退出状态st_exit设为user_ptr的值。
sys_exec
执行文件。首先,检查由file_name指向的文件是否有效(调用check_ptr2)。若有效,则调用process_execute来去执行它。
sys_wait
首先,还是检查传入参数f是否有效。若有效则调用process_wait来完成系统调用,等待一个子进程的结束。
sys_create
这个函数创建了一个文件。首先还是检查地址和页面的有效性。然后先获得文件的锁,这是为了保证用户程序在运行时,可执行文件不可被修改(文件系统的系统调用的同步性)。然后调用filesys_create来创建文件,参数(文件名等)存放在栈顶,用user_ptr可以找到。系统调用完成后释放文件锁。
sys_remove
这个函数删除了一个文件。首先还是检查地址和页面的有效性。之后先获得文件的锁,然后调用filesys_remove来删除文件,参数(文件名等)存放在栈顶,用
user_ptr可以找到。系统调用完成后释放文件锁。
sys_open
打开栈顶指向的文件。调用filesys_open打开文件。之后,为该文件按序添加一个属于当前线程(打开这个文件的线程)的文件标识符fd. (同一个文件的不同文件标识符是独立的,就是说当一个文件被重复打开了多次,不管它是被同一个或者不同的进程打开的,每一个open都要返回一个新的文件标识符)再将这个文件添加入当前线程打开文件的列表t->files中。将文件标识符fd存放于eax中。
sys_filesize
通过find_file_id获取文件标识符。之后调用file_length返回以文件标识符fd指代的文件的大小。
sys_write
write的情况有两种:往缓冲区写入数据或往文件中写入数据。取栈顶元素记为temp2,标记了我们是要往哪里写入数据。
如果是往缓冲区写入,则调用putbuf函数完成写入;如果是往文件中写入,则先调用find_file_id获取要写入的文件的文件标识符,然后调用file_write往对应文件中写入数据。
sys_seek
seek函数先通过find_file_id找到要进行seek操作的文件,然后根据传入的参数(文件标识符、位置),调用file_seek函数把下一个要读入或写入的字节跳转到指定文件的指定位置。
sys_tell
通过find_file_id获取文件标识符。然后调用file_tell返回下一个在已打开文件fd中即将被读入或写入的字节的位置。
sys_close
通过find_file_id获取文件标识符。判断,如果该文件处于打开状态,则调用file_close关闭它。关闭后,把这个文件从线程的文件list中移除并释放资源。

filesys

在这里插入图片描述

file.c

file_deny_write()
将文件设置为拒绝写入的状态,目的是为了防止对一个文件进行写入操作,直到file_allow_write被调用或该文件被关闭。

总结

project1

在这一项目中,我们需要进行三部分的改进,以实现如下功能:
第一部分:重新实现timer_sleep ()函数,以避免线程在就绪和运行状态间的不停切换。
第二部分:实现优先级调度
第三部分:实现多级反馈调度

project2

参数传递

process_execute创建线程,并分离参数,并把参数(含文件名)传递给start_process函数,让新线程执行start_process函数。start_process将参数继续传递给load函数,load函数为用户程序分配了地址空间,并继续将参数传递给setup_stack函数,setup_stack创建了用户栈并返回到load,load返回到start_process。接下来,在start_process中调用push_argument将用户程序所需的参数argc,argv及他们的地址入栈。这样就利用参数传递,完成了用户程序执行的准备过程。

系统调用

syscall_init存储了系统调用的类型。当中断发生,参数(包含了系统调用的类型)入栈,这时,syscall_handler弹出栈顶元素,也就是系统调用的类型,并去syscall_init里寻找有无定义该系统调用,找到了的话就转而执行该系统调用。
在执行有关文件系统的系统调用时,要注意文件系统系统调用的线程安全,以便任意数量的用户进程可以同步进行调用。具体的操作是先获得文件的锁,这是为了保证用户程序在运行时,可执行文件不可被修改。然后执行完系统调用后再释放锁。这样就实现了同步操作。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值