77范文网 - 专业文章范例文档资料分享平台

xv6操作系统整体报告(5)

来源:网络收集 时间:2020-03-27 下载这篇文档 手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:或QQ: 处理(尽可能给您提供完整文档),感谢您的支持与谅解。点击这里给我发消息

只有System call中断权限为用户权限,也就是说只能int 0x30。另外对tickslock这个锁进行初始化,这个锁是用来处理时钟中断的。

idtinit是用来加载idt表的。idtinit之所以和tvinit分开的原因是idtinit会被多个CPU调用。而tvinit过程只需要被调用一次。

vectors.S & vectors.pl

vectors.S是由vectors.pl生成的。其中定义了每个中断的入口程序和入口地址(存储在vecotrs数组中)。可以注意到,中断分成两类:一类是压入错误编码的(error code), 另一类不会压入错误编码。因此对于第二类,vectors.S将压入一个0。此外vectors.S还会将中断号压入栈。在压完两个必要的值之后,所有中断都将统一的跳转进入alltraps入口程序。

中断处理过程

trap函数的实现

trap过程便是对中断进行处理的。所有的中断在经过中断入口程序(trapasm.S中定义)后,都会最终跳转到这里。其处理过程如下:

1. 在37~45行,如果中断是系统调用,则通过syscall过程完成系统调用的处理。值得注

意的是,这里进行了当前进程是否被其他进程kill的情况。也就是说,如果一个进程被其他进程Kill,那么此进程将在进入或退出内核态时被切断。

2. 48~56行是对不同的中断进行处理。case IRQ_OFFSET+IRQ_TIMER是处理时钟中

断,如果当前CPU是0号CPU,那么ticks将增加一,同时进行对处于sleeping状态的进程进行检查(通过wakeup)。在wakeup中,一旦发现某些进程休眠的时间结束,则把其转化成RUNNABLE;

3. 57~60行通过 ide_intr()完成磁盘中断处理; 4. 61~64行通过kbd_intr()完成键盘中断处理;

5. 65~69行打印出产生的IRQ_SPURIOUS号中断;[???]

6. 72~82行,判断如果是在内核执行时产生的中断,则系统挂起;如果是在用户态执行

时产生的中断,则赋值当前进程的p->killed=1,表示要杀死此进程

7. 84~88行,将检查当前进程的p->killed,如果是非零且在用户态引起的中断,则调用

exit函数退出此进程;

8. 90~93行,判断如果当前进程占用了CPU(即cp->state == RUNNING),且是时钟

中断,则调用yield函数,让当前进程放弃所在CPU,选择其他进程占用当前CPU。

trapasm.S

在这个文件中,主要是将寄存器进行保存。 9~11行都是将相关寄存器压入到栈。寄存器值在栈中的布局可以参考x86.h中trapframe的定义。需要注意到的是栈底是在内存的高地址位置,而trapframe结构是从低到高放置的。14~16行是将段寄存器换到内核段。然后调用trap.c中的trap函数。因为trap的参数是指向trapframe的指针,所以将esp寄存器压入栈,其正好是trapframe的地址。

在从trap返回之后,将开始保存的所有寄存器恢复(26~28行),然后将中断编号和错误号弹出栈即可。然后调用iret进行返回。

syscall.c

这个文件的主要目的是处理系统调用,根据系统调用的编号分配到不同的处理函数中去。其中文件中还提供了提取参数的过程。

其中fetchint和fetchstr是从进程(参数p)中的地址addr拷贝到kernel的地址ip。由于addr是进程中的地址,其对应的物理地址在内核态的虚拟地址是不同的,因此需要转换。转换是十分简单的,进程p中addr对应的是内核的地址p->mem+addr。因为进程p的内存段是从p->mem开始的p->sz大小的连续空间。需要注意的是,fetchint和fetchstr都需要判断参数给出的内存地址是否超过了进程所分配的内存范围。

argint, argptr和argstr是获取system call中传入的参数。进程在进程调用前,都会把参数压入栈中。因此这几个过程需要先把进程栈中参数的地址找到,然后调用fetchint和fetchstr获取参数。可以看到argint,就是调用栈中的第n个参数。第n个参数在进程中的地址为cp->tf->esp+4+4*n。

syscall过程是将系统调用分配到不同的处理函数。处理后的返回结果存到cp->tf->eax中。这样在系统调用返回时,结果将被存在eax寄存器中。(见trapasm.S)。

sysproc.c

在这个文件里面有一部分系统调用处理函数的实现。其中大部分都是通过调用proc.c中的函数完成的。值得注意的是sys_sleep。一进入这个函数时,ticks将被保存在ticks0中。接着一直作循环,直到ticks-ticks0即实际睡眠时间超过预定的时间后就返回。由于不能始终占用CPU,所以将调用sleep进行休眠。此时进程将转入Sleeping状态,加入休眠队列。

第七章 文件系统 (file system)

1.概述

本章将给出xv6文件系统实现的概貌。读者将学习以下一些内容:

? 什么是文件系统? ? 什么是文件? ? 什么是目录?

? I/O Buffer有何作用?

? xv6的文件系统有哪些重要部分组成? ? xv6如何实现创建一个文件? ? xv6如何实现读写文件?

许多应用需要对数据进行长期保存和访问,这样就需要文件系统的支持。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:与文件管理有关的软件、被管理的文件以及实施文件管理所需的数据结构。从系统角度来看,文件系统是对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时删除文件等。

xv6的文件系统是整个操作系统中代码量最大的一部分了,但仍然只是一个非常简单的实现。不过这个文件系统仍然清楚地体现出了Unix文件系统的层次结构。大部分UNIX文件系统种类具有类似的通用结构,其中心概念是超级块(superblock), i节点(inode), 数据块(data block),目录块(directory block), 和间接块(indirection block)。超级块包括文件系统的总体信息,比如大小(其准确信息依赖文件系统)。 i节点包括除了名字外的一个文件的所有信息,名字与i节点数目一起存在目录中,目录条目包括文件名和文件的i节点数目。 i节点包括几个数据块的数目,用于存储文件的数据。 i节点中只有少量数据块数的空间,如果需要更多,会动态分配指向数据块的指针空间。这些动态分配的块是间接块;为了找到数据块,这名字指出它必须先找到间接块的号码。

xv6文件系统的结构大概是文件系统上层API、文件系统、Buf缓存结构和硬件设备I/O通讯。FD文件描述符和File文件结构提供了一个与文件系统交互的一个接口。其通过简单的Read和Write等函数与硬件设备进行交互,从而隐藏了很多实现的细节,简化了许多上层程序的操作。

Buf缓存结构是用来缓存需要写入硬件设备的数据和从硬件设备上读出的数据。Buf可以对硬件设备的操作进行统一的调度,从而提高效率。最底层的便是直接对硬件设备进行调度。 图7.1 xv6文件系统结构 2.FD文件描述符,File文件结构和源码分析 sysfile.c 此文件定义了许多有关文件系统的系统调用。xv6中的系统调用是针对文件描述符进行的。每一个文件描述符将指向一个文件结构File。有可能多个文件描述指向同一个文件。每个文件描述在每个进程结构用ofile记录了其指向的文件结构。下面对每个过程进行介绍。 argfd是对于从系统调用的第n个参数提出文件描述符及其相应的。首先把第n个参数读取出来(20行),然后通过ofile找到相应的文件结构(22行)。 falloc是用来分配一个文件描述符。只需要从ofile数组中寻找一个没有使用的文件描述符,然后返回即可。 sys_read是从一个文件描述符读取数据。只需要通过osfile读取出文件描述符所指向文件结构的,然后调用下层的fileread进行文件读取。sys_write与sys_read类似,只需要找到相应的文件结构,调用filewrite对文件结构进行写操作。同样,sys_dup、sys_close和sys_fstat都是对文件描述符进行解析,然后调用下层的API进行操作,这里就不再赘述了。 下面我们来看一下sys_link。这是建立一个硬链接。在xv6中,一个目录中存储的是一个个文件名与inode编号的对。因此对于同一个inode,可能有多个目录指向这个inode。sys_link就是用来建立这种指向的。为了避免形成大于1的环,dir的link只能指向一个文件,而不能是目录(122~124行)。在116~128行,首先找到被指向文件的inode,将其连接数加1。然后在130~136行中,在当前目录中加入硬链接。相应地,sys_unlink是用来取消一个硬链接。就是在目录中找到相应的表项,进行删除。 create()过程是用于建立相应的 file.h 每个文件结构都对应着一个磁盘设备上的真实文件、或一个硬件设备、或者通讯管道。从File的结构定义可以看出,File结构主要分成两种类型,一种是INODE(可能是磁盘上的文件,也可能是硬件设备),另一种是Pipe(管道)。如果是INODE型,则ip指针则指向其对应的INODE;若是PIPE型,则pipe指针指向一个PIPE结构。其中readable和writable是用来加上对文件的访问权限的。而ref则是表示被整个系统的用户进程应用了多少次(TODO:不同进程打开同一个文件,会在这里引用计数吗?还是在INODE层?)。

file.c

此文件中提供了对FILE结构的一些基本操作,例如FILE文件结构的分配(filealloc)、文件结构的复制(filedup)、文件结构的关闭(fileclose)、文件读取(fileread)和文件写入(filewrite)。所有的FILE文件结构在系统中都有一个统一的列表保存,这个列表的大小限制了整个系统可以同时打开的文件的数目。注意到,由于是SMP结构,在访问这个列表时需要加上锁来保证互斥的资源访问。

filealloc是用来分配一个FILE结构的,也就是从文件列表中选取一个状态为FD_CLOSED的文件文件分配。新分配的文件状态变为FD_NONE,引用计数为1。

filedup是增加此文件的引用计数。也就是说这个文件结构又被一个进程打开了,这种情况可能发生在fork中。

fileclose是将一个文件的引用计数减一。比如某个用户进程将此文件结构关闭了。当引用计数变为0时,说明系统中没有任何一处打开此文件,此时可以收回此文件结构。在回收时,同时要调用文件结构下层结构(PIPE或INODE)的关闭。

filestat是返回一个文件结构的统计信息。只有当此文件结构类型是INODEC时才有效。返回的统计信息有文件大小,文件链接数等等。

fileread和filewrite分别是通文件结构读取和写入数据。两个函数都是调用下层结构相应的函数完成的。在这两个过程中,由于没有访问文件结构列表,所以不需要加file_table_lock锁。但所有的下层结构都有其自身的加锁机制。

3.磁盘文件系统和源码分析

文件系统主要是以INODE结构进行操作的。INODE可以是一个文件,也可以是一个文件夹。用户进程通过文件路径来进行对INODE的查询和访问。INODE在xv6文件系统中有两种类型,一种是在系统实际运行时在内存中的INODE(即fsvar.h中的inode结构),另一种是在磁盘上的INODE(即fs.h中的dinode)。在运行系统中的INODE是用于操作系统对文件访问进行控制,例如需要为对INODE的互斥访问进行加锁等等。而在磁盘上的dinode则是用于组织这个文件系统在磁盘上的存储结构。

fsvar.h & fs.h

我们仔细看一下xv6文件系统在磁盘上的组织结构。xv6对磁盘空间的划分是以块(block)为单位的。一个dinode可能是由多个block组成。在磁盘上,xv6文件系统从低地址到高地址(即从第0块到最后一块)的布局为:超级块(super block)、dinode结构列表(inodes), 磁盘上块使用情况的统计表(block in-use-bitmap)和数据块。其中超级块的作用是给出磁盘文件系统的基本信息,包括文件系统总的块数,数据块的数目以及dinode的数目。超级块只占一个块(block)。而dinode结构列表则按顺序存放文件系统中所有的dinode,其中每个dinode都有自己的编号。dinode结构中记录的文件类型(文件或目录)、设备、硬链接数、文件大小和此文件所占用的数据块。占用数据块的编号存在addrs中,其中addrs[0]~addrs[NDIRECT-1]直接指向一个数据块;而addrs[INDIRECT]指向一个块,块里存放的都是其他被此文件占用的数据块的地址,因此是addrs[INDIRECT]是间接指针。磁盘块使用情况统计表是一段连续的磁盘空间,其中每一比特对应了整个盘上某块的使用状况。如果第i bit为1,则表示磁盘上的第0块被占用。磁盘中剩下的块都为数据块。注意:以上每一部分都占用整数个块,有些结构可能没有把其最后一块占满,那么则补0。 fs.c

readsb是读取super block。bzero,balloc,bfree都是数据块相关的操作。在操作中都是对有

buf缓存的block进行操作。其中balloc, bfree在操作之后都需要修改磁盘块使用状况统计相应的比特位。

下面主要重点介绍与inode操作相关的函数。在文件系统中,icache结构缓存当前系统中所有被访问的inode。访问这些inode的可能是活跃的File文件结构或者是进程所处的当前目录cwd。每个inode有个引用计数。当引用计数变为0时,inode便会被回收。每个inode都有一个编号inum,对应在磁盘上dinode的列表的编号。

iget函数是从icache列表中取得编号为inum的inode。如果icache中没有缓存此inode,则建立一个新的inode,并令其ref为1。

idup函数则是增加一个inode的引用计数。比如在fork时File文件结构被复制是会发生这个情况。

ilock作用是获得某inode的使用权,且将其锁上。也就是说,一旦inode状态为I_BUSY,同一时刻只能有一个线程对inode进行修改操作。获得inode使用权时,如果发现已被其他进程锁住,则当前进程进入SLEEP状态。知道其他进程放弃使用权后,用wakeup将其唤醒。需要注意的是在抢inode使用权时,仍然可能出现RACE状态(TODO)。抢到使用权后,如果发现ip->flags不是I_VALID,则从磁盘上读入。iunlock则是释放inode的使用权,同时唤醒等待这把锁的进程。iput是将是释放对一个未加锁的引用。当发现引用计数为1时回收此inode。ialloc是在磁盘上寻找一个未使用的inode进行分配。这个算法对磁盘进行顺序寻找空闲的inode,速度实际上比较慢。iupdate是将内存中的内容更新到磁盘。

bmap是返回inode的第n个块(block)。如果第n块是直接块,则可以马上返回。否则要加载block然后读到相应的块,然后返回。itrunc、stati、readi和writei则是对inode的数据访问。dirlookup、dirlink、skipelem、_namei都是对文件路径进行处理,相对比较简单,这里便不再赘述。需要注意的是,dir类型的inode所指的文件内容全都是一个个dirent项,描述了文件夹中的文件名和相应的inode编号。

4.I/O缓存Buf结构和源码分析

xv6用buf对磁盘上的block块进行缓存。buf是一个LRU链表。也就说最近访问的buf总是放在链表的首部。

bio.c

binit是将整个buf链表初始化,并将其链成一个表。bget则寻找一个扇区的buf。首先在buf链表中寻找是否有此扇区的buf,如果没有的话,则加入开辟一个新的buf存储扇区。bread是读取某一个扇区。首先通过bget找到相应的buf。如果发现状态不是B_VALID,则通过

ide_rw进行磁盘同步。bwrite是将buf中的内容写入扇区。调用bwrite之前需要在外部对buf,从而保证一致性。brelse是用来释放buf。这个函数是当对buf访问结束时调用的。此时基于LRU算法,将把buf放在buf链表的头部。

ide.c

此文件是对ide磁盘进行读写操作。I/O读写是异步的。

其中主要的流程如下:首先看ide_rw。对于每个读写操作,都有一个ide_queue进行排队。在ide_rw函数中,会把当前的进程加入到ide_queue中。如果之前disk没有开始读写,则唤起disk读写。(141~142行)。然后在145~146进行轮询等待读写进程的完成。轮询中并不是盲等,而是会sleep当前进程,等待进程完成是被唤起。

ide_start_request过程是将buf b进程请求发送到磁盘。当进程完成时,磁盘将发出完成的硬件中断。ide_intr则是相应此中断的过程。ide_intr将在106~108行清除状态,并唤醒等待此buf的进程。最后如果此进程在ide_queue有后继进程,则启动此进程。

百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说教育文库xv6操作系统整体报告(5)在线全文阅读。

xv6操作系统整体报告(5).doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印 下载失败或者文档不完整,请联系客服人员解决!
本文链接:https://www.77cn.com.cn/wenku/jiaoyu/894191.html(转载请注明文章来源)
Copyright © 2008-2022 免费范文网 版权所有
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ: 邮箱:tiandhx2@hotmail.com
苏ICP备16052595号-18
× 注册会员免费下载(下载后可以自由复制和排版)
注册会员下载
全站内容免费自由复制
注册会员下载
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: