操作系统课内实验指导书_第1页
操作系统课内实验指导书_第2页
操作系统课内实验指导书_第3页
操作系统课内实验指导书_第4页
操作系统课内实验指导书_第5页
已阅读5页,还剩34页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

操作系统原理课内实验指导书实验一:进程管理实验指导准备知识1. 基本概念1) 进程的概念2) 进程与程序的区别3) 并发执行的概念4) 进程互斥的概念5) 进程通信的基本概念2. 系统调用系统调用是一种进入系统空间的办法。通常,在OS的核心中都设置了一组用于实现各种系统功能的子程序,并将他们提供给程序员使用。程序员在需要OS提供某种服务的时候,便可以调用一条系统调用命令,去实现希望的功能,这就是系统调用。因此,系统调用就像一个黑箱子一样,对用户屏蔽了操作系统的具体动作而只是提供了调用功能的接口。不同的操作系统有各自的系统调用方法。如Windows API,便是Windows的系统调用。Linux的系统调用与之不同的是源于Linux内核代码完全公开,所以可以细致地分析出其系统调用的机制。2.1系统调用和普通函数的区别1) 运行于不同的状态用户程序可以通过系统调用进入系统空间,在核心态执行;而普通函数则只能在用户空间当中进行。2) 通过软中断切断由于用户程序使用系统调用后要进入系统空间,所以需要调用一个软中断;而普通函数在被调用时则没有这个进程。2.2系统调用的类型系统调用的作用与它的宿主操作系统有密切关系。根据操作系统的性质不同,它们所提供的系统调用会有一定的差异,不过对于普通操作系统而言,应该具有下面几类系统调用: 进程控制类 文件操纵类 进程通信类 信息维护类2.3系统调用的实现机制 由于操作系统的不同,其系统调用的实现方式可能不一样,然而实现机制应该大致相同的,一般包含下面两个步骤。1) 设置系统调用号在系统当中,往往设置多条系统调用命令,并赋予每条系统调用命令一个惟一的系统调用号。根据分配系统调用号方式的不同分为:直接方式和参数表方式。系统调用的参数表方式分为直接和间接两种方式,如图1所示。 图1 系统调用的参数表方式2) 处理系统调用操作系统中有一张系统调用入口表。表中的每个表目都对应一条系统调用命令,它包含有该系统调用自带参数的数目、系统调用命令处理程序的入口地址等等。操作系统内核便是根据所输入的系统调用号在该表中查找到相应的系统调用,进而转入它的入口地址去执行系统调用程序。3. Linux的系统调用机制Linux的系统调用是通过中断机制实现的。中断这个概念涉及计算机系统结构方面的知识,显然它与微处理器等硬件有着密不可分的关系。中断(Interrupt),是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU不得不暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后再返回原来被中断处继续执行的进程。其发生一般而言是“异步”的。换句话说就是在无法预测的情况下发生的(如系统掉电)。所以计算机的软硬件对于中断的相应反应完全是被动的。软中断,是对硬中断的一种模拟,发送软中断就是向接收进程的proc结构中的相应项发送一个特定意义的信号。软中断必须等到接收进程执行时才能生效的。陷阱(Trap),即由软件产生的中断,指处理机和内存内部产生的中断,它包括程序运算引起的各种错误,如地址非法、校验错误、页面失效等。它有专门的指令,如X86中的“INTn”,在程序中是有意产生的。所以说陷阱是主动的、“同步”的。异常(Exception),一般也是异步的,大多是由于不小心或无意造成的,比如在进行除法操作时除数为0,就会产生一次异常。4. 相关函数fork函数fork()函数用于创建一个新进程(子进程)。其调用格式为:int fork();正确返回:等于0,创建子进程,从子进程返回的ID值。 等于1,从父进程返回的子进程的进程ID值。错误返回:等于-1,创建失败。1) 子进程和父进程的调度执行子进程被创建后就进入就绪队列和父进程分别分别独立地等待调度。子进程继承父进程的程序段代码,子进程被调度执行时,也会和父进程一样从fork()返回。从共享程序段代码的角度来看,父进程和子进程所执行的程序代码是同一个,在内存中只有一个程序段副本;但是从编程的角度来看,为了使子进程和父进程做不同的事,要在程序中区分父进程和子进程的代码段,这就需要借助于从fork()带回的值来标志当前程序身份。从fork()返回后,都会执行如下语句:pid=fork();得到返回的值pid,有如下几种情况:(1) 若pid小于0,则表示fork()出错,相应语句为if(pid 0) printf(The parent process is running now!n); exit(0); 由于父进程和子进程分别独立地进入就绪队列等待调度,所以谁会先得到调度是不确定的,这与系统的调度策略和系统当前的资源状态有关。因此谁先从fork()返回,继续执行后面的语句也是不确定的。2) 父进程和子进程的存放及资源共享当父进程创建子进程时,首先为子进程分配由task向量数组指向的task_struct结构的内存、所需的堆栈和页表等,创建进程标识号(在系统的进程标识号组中是惟一的),并且将其父进程的进程标识号填入task_struct中的家族信息中。然后将父进程的task_struct的相关内容复制到子进程的task_struct,对一些数据成员进行初始化,如图2所示。子进程从父进程处继承的资源包括:真实用户标识号和组标识号、有效用户标识号和组标识号、进程组标识号、对话标识号、控制终端、根目录与当前工作目录、设置用户标识号和设置组标识号计位、信号标识、文件描述符、文件默认创建权限掩码、可访问的内存区、线程、环境变量及其他资源分配。图2 父进程和子进程的内存映像wait()函数wait()函数常用来控制父进程与子进程的同步。在父进程中调用wait()函数,则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,产生一个终止状态字,系统会向父进程发出SIGCHLD信号。当接到信号后,父进程提取子进程的终止状态字,从wait()函数返回继续执行原来的程序。其调用格式为:#include #include (pid_t) wait(int*statloc);正确返回:大于0,子进程的进程ID值。 等于0,其他。错误返回:等于-1,调用失败。exit()函数exit()函数是进程结束时最常调用的函数,在main()函数中调用return,最终也是调用exit()函数。这些都是进程的正常终止。在正常终止时,exit()函数返回进程结束状态。其调用格式为:#include void exit(int status); 其中,status为进程结束状态。kill()函数 kill()函数用于删除执行中的程序或者任务。其调用格式为: kill(int PID,int IID); 其中,PID是要被杀死的进程号,IID为向将被杀死的进程发送中的中断号。Signal()函数 signal()函数是允许调用进程控制软中断信号的处理。其调用格式为:#include int sig; void (*func)(); signal(sig,function); 其中,(1) sig的取值如下:信号功能值SIGHUP挂起1SIGINT键盘中断,键盘按Delete键或Break键2SIGQUIT键盘按Quit键3SIGILL非法指令4SIGTRAP跟踪中断5SIGIOTIOT指令6SIGBUS总线错7SIGFPE浮点运算溢出8SIGKILL要求终止进程9SIGUSR1用户定义信号#110SIGSEGV段违法11SIGUSR2用户定义信号#212SIGPIPE向没有读进程的管道上写13SIGALRM定时器告警,时间到14SIGTERMkill发出的软件结束信号15SIGCHLD子进程死17SIGCONT若已停止则继续18SIGPWR电源故障30(2)function的解释如下: SIG_DFL 默认操作。对除SIGPWR和SIGCHLD外所有信号的默认操作是进程终结。对信号SIGQUIT、SIGRAP、SIGLL、SIGFPE、SIGBUS和SIGSEGV,它产生一内存映像文件。 SIG_IGN 忽视该信号的出现。 Function 在该进程中的一个函数地址,在核心返回用户态时,它以软中断信号的序号作为参数调用该函数,对除了信号SIGILL、SIGTRAP、SIGPWAP以外的信号,核心自动地重新设置软中断信号处理程序的值SIG_DFL,一个进程不能捕获SIGKILL信号。pipe()函数 pipe()函数用于创建一个管道。其调用格式为:#include pipe(int fp2); 其中,fp2是供进程使用的文件描述符数组,fp0用于写,fp1用于读。正确返回:0,调用成功错误返回:-1,调用失败。实验指导1. 进程的软中断通信(1) 算法流程图图3为进程软中断通信的算法流程图 图3 软中断通信程序流程图(2) 参考程序源代码#include #include #include #include int wait_flag; void stop( ); main( ) int pid1, pid2; / 定义两个进程号变量 signal(3,stop); / 或者 signal(14,stop); while(pid1 = fork( ) = -1);/ 若创建子进程1不成功,则空循环if(pid1 0) / 子进程创建成功,pid1为进程号 while(pid2 = fork( ) = -1);/ 创建子进程2 if(pid2 0) wait_flag = 1; sleep(5); / 父进程等待5秒 kill(pid1,16); / 杀死进程1kill(pid2,17); / 杀死进程2 wait(0); / 等待第1个子进程1结束的信号 wait(0); / 等待第2个子进程2结束的信号 printf(n Parent process is killed !n); exit(0); / 父进程结束 else wait_flag = 1; signal(17,stop); / 等待进程2被杀死的中断号17 printf(n Child process 2 is killed by parent !n); exit(0); else wait_flag = 1; signal(16,stop); / 等待进程1被杀死的中断号16 printf(n Child process 1 is killed by parent !n); exit(0); void stop( ) wait_flag = 0; (3)程序运行结果 Child process 1 is killed by parent ! Child process 2 is killed by parent ! Parent process is killed ! 或者多次运行,并且Delete键后,会出现如下结果:Child process 2 is killed by parent ! Child process 1 is killed by parent ! Parent process is killed ! (4)简要分析1) signal函数 上述程序中,调用函数signal()都放在一段程序的前面部位,而不是在其他接收信号处。这是因为signal()的执行起的作用只是为进程指定信号量16和17,以及分配相应的与stop()过程连接的指针。因而signal()函数必须在程序前面部分执行。 2)wait函数 在父进程中调用第1个wait(0)后,则父进程被阻塞。进入等待第一个子进程运行结束的队列,等待子进程结束。当子进程结束后,会产生一个终止状态字,系统会向父进程发出SIGCHLD信号。当接到信号后,父进程提取子进程的终止状态字,从wait()返回继续执行原程序。同样的方式,父进程继续执行第二个wait(0),并再次阻塞,等待第2个子进程运行结束。当第二个子进程运行结束后父进程继续执行剩余的语句。 3)关于exit函数 该函数中每个进程退出时都用了语句exit(0),这是进程的正常终止。在正常终止时,exit()函数返回进程结束状态。进程终止时,则由系统内核产生一个代表异常终止原因的终止状态,该进程的父进程都能用wait()得到其终止状态。在子进程调用exit()后,子进程的结束状态会返回给系统内核,由内核根据状态字生成终止状态,供父进程在wait()中读取数据。若子进程结束后,父进程还没有读取子进程的终止状态,则子进程就变成了“孤儿进程”,系统进程init会自动“收养”该子进程,成为该子进程的父进程,即父进程标识号变成1,当子进程结束时,init会自动调用wait()读取子进程的遗留数据,从而避免在系统中留下大量的垃圾。 4)结果显示上述结果中“Child process 1 is killed by parent !” 和“Child process 2 is killed by parent !”相继出现,当运行几次后,谁在前谁在后是随机的。这是因为:从进程调度的角度看,子进程被创建后处于就绪态。此时,父进程和子进程作为两个独立的进程,共享同一个代码段,分别参加调度、执行,直至进程结束。但是谁会先被调度程序选中执行,则与系统的调度策略和系统当前的资源状态有关,是不确定的。因此,谁先从fork()函数中返回继续执行后面的语句也是不确定的。2. 进程的管道通信 (1) 算法流程图图4为基于进程的管道通信的算法流程图 图4 管道通信程序流程图(2) 参考程序源代码#include #include #include int pid1,pid2; / 定义两个进程变量 main( ) int fd2; char OutPipe100,InPipe100; / 定义两个字符数组 pipe(fd); / 创建管道 while(pid1 = fork( ) = -1); / 如果进程1创建不成功,则空循环 if(pid1 = 0) / 如果子进程1创建成功,pid1为进程号 lockf(fd1,1,0); / 锁定管道 sprintf(OutPipe,n Child process 1 is sending message!n); / 给Outpipe赋值 write(fd1,OutPipe,50);/ 向管道写入数据 sleep(5); / 等待读进程读出数据 lockf(fd1,0,0); / 解除管道的锁定 exit(0); / 结束进程1 else while(pid2 = fork() = -1); / 若进程2创建不成功,则空循环 if(pid2 = 0) lockf(fd1,1,0); sprintf(OutPipe,n Child process 2 is sending message!n); write(fd1,OutPipe,50); sleep(5); lockf(fd1,0,0); exit(0); else wait(0); / 等待子进程1 结束 read(fd0,InPipe,50); / 从管道中读出数据 printf(%sn,InPipe); / 显示读出的数据 wait(0); / 等待子进程2 结束 read(fd0,InPipe,50); printf(%sn,InPipe); exit(0); / 父进程结束 (3) 程序运行结果 Child process 1 is sending message ! Child process 2 is sending message!(4) 简要分析 管道,是指用于连接一个读进程和一个写进程,以实现它们之间信息的共享文件又称pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接收管道输送的接收进程(读进程),可以从管道中接收数据。 为了协调双方的通信,管道通信机制必须提供以下3方面的协调能力: 互斥。当一个进程正在对pipe进程读/写操作时,另一进程必须等待,程序中使用lock(fd1,1,0)函数实现对管道的加锁操作,用lock(fd1,0,0)解除管道的锁定。 同步。当写进程把一定数量的数据写入pipe后,便去睡眠等待,直到读进程取走数据后,再把它唤醒。当读进程试图从一空管道中读取数据时,也应睡眠等待,直至写进程将数据写入管道后,才将其唤醒。 判断对方是否存在。只有确定写进程和读进程都存在的情况下,才能通过管道进行通信。实验二:存储器管理实验准备知识(1) C+、指针、结构体(类)(2) HASH表查找方式(3) 操作系统相关内存交换知识(4) 用到的LINUX函数Int getpid() 获得当前进程的idVoid srand ( int a ) 以a为种子产生随机数Int rand() 根据前面的种子,返回一个随机数实验指导本实验并没有进入系统空间对实际进程页面进行控制,而是在用户空间用线性表的连续存储方式对进程页面交换进行模拟。1. FIFO算法l 原理简述(1) 在分配内存页面数(AP)小天进程页面数(PP)时,当然是最先运行的AP个页面放入内存;(2) 这时又需要处理新的页面,则将原来放的内存中的AP个页中最先进入的调出(FIFO),再将新页面放入;(3) 以后如果再有新页面需要调入,则都按上述规则进行。(4) 算法特点:所使用的内存页面构成一个队列。l 图表描述假设某个进程在硬盘上被划分成5个页面(PP=5),以1、2、3、4、5分别表示,CPU调用它们的顺序(这应该取决于进程本身)为:1、4、2、5、3、3、2、4、2、5,如果内存可以控制的页面数为3(AP=3),那么在使用FIFO算法时,这3个页面的内存使用情况应该如图5所示。图5不难看出,本例共换入页面8次,若用变量diseffect表示页面换入次数,则diseffect=8。l 算法实现提示要得到命中率,必然应该有一个常量total_instruction来记录页面总共使用的次数,此外还需要一个变量记录总共换入页面的次数diseffect(需要换出页面总是因为缺页中断而产生)。利用公式1-diseffect / total_instruction*100% 可以得到命中率。(1) 初始化。设置两个数组pageap和pagecontrolpp分别表示进程页面数和内存分配的页面数,并产生一个随机数序列maintotal_instruction(这个序列由pageap的下标随机构成)表示待处理的进程页面顺序,diseffect置0。(2) 看main中是否有下一个元素,若有,就由main中获取该页面下标,并转(3),如果没有则转(7)。(3) 如果该页已在内存中,就转(2),否则转(4),同时未命中的diseffect加1。(4) 观察pagecontrol是否占满,如果占满则须将使用队列(在第(6)步中建立的)中最先进入的(就是队列的第一个单元)pagecontrol单元“清干净”,同时将page单元置为“不在内存中”。(5) 将该page与pagecontrol建立对应关系(可以改变pagecontrol的标志位,也可以采用指针链接,总之至少要使对应的pagecontrol单元包含两个信息:一是它被使用了,二是哪个page单元使用的。page单元也包含两个信息:对应的pagecontrol单元号和本page单元已在内存中)。(6) 将用到的pagecontrol置入使用队列(这里的队列是一种FIFO的数据结构),返回(2)。(7) 显示计算1-diseffect / total_instruction*100%,完成。2. LRU算法l 原理简述(1) 当内存分配页面数(AP)小于进程页面数(PP)时,把最先执行的AP个页面放入内存。(2) 当需调页面进入内存,而当前分配的内存页面全部不空闲时,选择将其中最长时间没有用到的那一页调出,以空出内存来放置新调入的页面(LRU)。(3) 算法特点:每个页面都有属性来表示有多长时间未被CPU使用的信息。l 图表描述这里采用的例子和前面的一样。假设某个进程在硬盘上被划分成5个页面(PP=5),以1、2、3、4、5分别表示,CPU调用它们的顺序(这应该取决于进程本身)为:1、4、2、5、3、3、2、4、2、5,而内存可以控制的页面数为3(AP=3),那么在使用LRU算法时,这三个页面的内存使用情况应该如图6所示。不难看出,本例共换入页面7次,若用变量diseffect表示页面换入次数,则diseffect=7。图6l 算法实现提示与前述算法一样,只有先得到diseffect才能获得最终的命中率。(1) 初始化。设置两个数组pageap和pagecontrolpp分别表示进程页面数和内存分配的页面数,并产生一个随机数序列maintotal_instruction(这个序列由pageap的下标随机构成)表示待处理的进程页面顺序,diseffect置0。(2) 看序列main中是否有下一个元素,如果有,就由main中获取该页面下标,并转(3),如果没有则转(6)。(3) 如果该page单元在内存中便改变页面属性,使它保留“最近使用”的信息,转(2),否则转(4),同时diseffect加1。(4) 看是否有空闲页面,如果有,就返回页面指针,并转到(5),否则,在内存页面中找出最长时间没有使用到的页面,将其“清干净”,并返回该页面指针。(5) 在需处理的page与(4)中得到的pagecontrol之间建立联系,同时让对应的page单元保存“最新使用”的信息,转(2)。(6) 如果序列处理完成,就输出计算1-diseffect / total_instruction*100%的结果,完成。3. NUR算法l 原理简述所谓“最近未使用”,首先是要对“近”做一个界定,比如CLEAR_PERIOD=50,便是指在CPU最近的50次进程页面处理工作中,都没有处理到的页面。那么可能会有以下几种情况:(1) 如果这样的页面只有一个,就将其换出,放入需要处理的新页面。(2) 如果有这样的页面不止一个,就在这些页面中任取一个换出(可以是下标最小的或者最小的),放入需要处理的页面。(3) 如果没有一个这样的页面,就随意换出一个页面(可以是下标最小的或者最大的)。算法特点:有一个循环周期,每到达这个周期,所有页面存放是否被CPU处理的信息的属性均被置于初始态(没有被访问)。l 图表描述还是用前面的例子,某进程在硬盘上被划分为5个页面,用1、2、3、4、5表示,而处理及处理它们的顺序为:1、4、2、5、3、3、2、4、2、5,而内存可以控制的页面数为3(AP=3),CLEAR_PERIOD取5;在循环周期内,如果所有内存页面均被CPU处理或者有多个页面未被CPU处理,取页码最小的页面换出。算法实现过程如下图:图7显示页面交换共6次,disaffect=6。l 算法实现提示(1) 初始化。设置两个数组pageap和pagecontrolpp分别表示进程页面数和内存分配的页面数,并产生一个的随机数序列maintotal_instruction(当然这个序列由page德下标随机构成)表示待处理的进程页面顺序,diseffect置0,设定循环周期CLEAR_PERIOD。(2) 看main是否有下一个元素。若有,就从main中获得一个CPU将处理页面的序号;如果没有就转到(8)。(3) 如果待处理的页面在内存中,就转到(2);否则diseffect加1,并转到(4)。(4) 看是否有空闲得内存页面,如果有,返回空闲内存页面指针,并转到(5);否则,在所有没有被访问且位于内存中的页面中按任意规则(或者取最近的一个页面;或者取下标最小的)取出一个,返回清空后的内存页面指针。(5) 在待处理进程页面与内存页面之间建立联系,并标注该页被访问。(6) 如果CPU已处理了CLEAR_PERIOD个页面,就将所有页面均设为“未访问”。(7) 返回(2)。(8) 如果CPU所有处理工作完成,就返回1-(disaffect / total_instruction )*100%的结果,并结束。4. OPT算法l 原理简述所谓的最佳算法是一种理想状况下的算法,它要求先遍历所有的CPU待处理的进程页面序列。在这些页面中,如果有些已经在内存中,而CPU不再处理的,就将其换出;而有些页面已在内存中而CPU即将处理,就从当前位置算起,取最后才会处理到的页面,将其换出。如CPU待处理的页面序列为:13224525143411553421如果已经处理了前5个页面(底纹为黑色),那么随后的页面5是第一个待处理的页面,这时若要将页面5调入内存,需选择页面3换出,因为页面3 是最后才会被处理到的。l 图表描述这里采用的例子和前面的一样。假设某个进程在硬盘上被划分成5个页面(PP=5),以1、2、3、4、5分别表示,CPU调用它们的顺序(这应该取决于进程本身)为:1、4、2、5、3、3、2、4、2、5,而内存可以控制的页面数为3(AP=3),那么在使用LRU算法时,这三个页面的内存使用情况应该如图8所示。不难看出,本例共换入页面6次,若用变量diseffect表示页面换入次数,则diseffect=6。图8l 算法实现提示为每个进程页面设一个“间隔”属性cDistance表示CPU将在第几步处理到该页面,如果页面不再被CPU处理,可以被设为某个很大的值(如32767),这样每次被换出的就是cDistance最大的那个页面。(1) 初始化。设置两个数组pageap和pagecontrolpp分别表示进程页面数和内存分配的页面数,并产生一个随机数序列maintotal_instruction(这个序列由pageap的下标随机构成)表示待处理的进程页面顺序,diseffect置0。然后扫描整个页面访问序列,对cDistanceTOTAL_VP数组进行赋值,表示该页面将在第几步被处理。(2) 看序列main中是否有下一个元素,如果有,就由main中获取一个CPU待处理的页面号,并转(3),如果没有则转(6)。(3) 如果该页面已经在内存中了,就转(2),否则转(4),同时diseffect加1。(4) 看是否有空闲页面,如果有,就返回页面指针,并转到(5),否则,遍历所有未处理的进程页面序列,如果有位于内存中的页面而以后CPU不再处理的,首将其换出,返回页面指针;如果没有这样的页面,找出CPU将最晚处理的页面,将其换出,并返回该页面指针。(5) 在内存页面和待处理的进程页面之间建立联系,转(2)。(6) 输出计算1-diseffect / total_instruction*100%的结果,完成。实验三:文件系统实验准备知识这是相对来说有一定难度的实验,它含盖了一个简单的二级文件系统的设计以及相关的接口命令编写的内容,也鉴于此把它放在了最后一个实验。“一分耕耘,一分收获”,在完整的完成本实验,你将获得的收益是:对文件系统工作的机理,特别是linux的ext2文件系统工作机理了如指掌;linux下较强的编程能力。好了,从此开始:1. 外存管理其实很早人们设计操作系统的时候就意识到一个问题:所有的程序和数据不可能都放在内存当中。所以为了腾出宝贵的内存空间,也为了方便用户管理外存上的文件,文件系统应运而生。文件系统是一个含有大量的文件及其属性,对文件进行操作、管理的软件,以及向用户提供使用文件的接口的一个集合。在逻辑上它的层次结构是这样的:文件系统接口对对象的操作和管理的软件集合逻辑文件系统基本I/O管理程序(文件组织模块)基本文件系统(物理I/O层)I/O控制层(设备驱动程序)对象及其属性说明 作为产品的操作系统目前种类已经很多了,一般来说它们有各自的文件系统。比如MS的WINDOWS系列使用的是FAT16、FAT32或NTFS的文件系统、LINUX使用的是EXT2、EXT3文件系统等等。2. linux的EXT2文件系统 文件系统是操作系统中负责管理和存取文件信息的软件机构,负责文件的建立、撤消、存入、读写、修改和复制,还负责完成对文件的按名存取和进行存取控制,以及向用户提供使用文件系统的接口。LINUX使用了虚拟文件系统(VFS)的技术,从而可以支持多达几十种不同类型的文件系统,其中EXT2是LINUX自己的文件系统。无论是VFS,还是EXT2都是采用UNIX的文件系统模型来构造的。EXT2有几个重要的数据结构,一个是超级块,用来描述目录和文件在磁盘上的物理位置、文件大小和结构等信息。inode也是一个重要的数据结构。文件系统中的每个目录和文件均由一个inode描述。它包含:文件模式(类型和存取权限)、数据块位置等信息。如果希望详细学习EXT2文件系统可以参看linux内核代码include/linux/ext2_fs.h、include/linux/ext2_fs_sb.h等文件。一个文件系统除了重要的数据结构之外,还必须为用户提供有效的接口操作。比如EXT2提供的OPEN/CLOSE接口操作。图9 Linux的EXT2文件系统3. 可能用到的编程技术1) fopen,打开文件格式:FILE *fopen(const char *filename,const char *mode)需要头文件stdio.hfilename:待打开的文件名,如果不存在就创建改文件。mode:打开方式,常用的有“w”写方式打开,文件不存在就被创建,否则清除原来内容;“r”读方式打开,文件必须存在;“a”添加方式打开;“w+”读写方式打开,有清楚功能;“r+”读写方式打开,文件必须存在;“a+”“t”TEXT方式打开;“b”二进制方式打开2) fwhite和fread,读写文件size_t fwite(const void *buffer,size_t size,size_t count,FILE *stream);size_t fread( void *buffer, size_t size, size_t count, FILE *stream );buffer:待读写的内容;size:一次读写的量;count:需读写buffer的次数;stream:打开的文件指针3) fseek,定位文件int fseek( FILE *stream, long offset, int origin );stream:文件指针;offset:偏移量;origin:初始位置,有三个常量,SEEK_CUR是当前位置,SEEK_SET文件开头,SEEK_END文件尾。4. 用内存来模拟外存 真正的文件系统对外存进行管理,涉及到许多硬件、设备管理方面的底层技术,一方面这些技术不属于操作系统核心内容,一方面过多的内容不免造成实验者顾此失彼,所以这里推荐一种使用内存来模拟外存的方式,可以跳过这些硬件技术而直接把精力放在数据结构设计和操作算法设计上面。 假定pInode是一个指向inode结构的指针,而且它已经放入的需要放入的数值了,现在需要将其写入到特定位置。可用如下代码:fd=fopen(“filesystem”,”w+b”); /fd是FILE指针类型,w便是写方式,b表示二进制fseek(fd, specific_area,SEEK_SET);/ fd是文件指针;specific_area为整形,/ 为需要入pInode的位置fwrite(pInode,1,sizeof(inode),fd); / 写入pInode信息实验指导1. 文件系统的数据结构a) i节点struct inodestruct inode *i_forw;struct inode *i_back;char I_flag;unsigned int i_into; /*磁盘i节点标号*/unsigned int i_count; /*引用计数*/unsigned short di_number; /*关联文件书,当为0时,则删除该文件*/unsigned short di_mode; /*存取权限*/unsigned short di_uid; /*磁盘i节点用户*/unsigned short di_gid; /*磁盘i节点组*/Unsigned int di_addrNADDR; /*物理块号*/b) 磁盘i结点Struct dinode unsigned short di_number; /*关联文件数*/ unsigned short di_mode; /*存取权限*/ unsigned short di_uid; unsigned short di_gid;unsigned long di_size; /*文件大小*/unsigned int di_addrNADDR; /*物理块号*/c) 目录项结构Struct direct char d_nameDIRSIZ; /*目录名*/ unsigned int d_ino; /*目录号*/d) 超级块Struct filsys unsigned short s_isize; /*i节点块块数*/ unsigned long s_fsize; /*数据块块数*/ unsigned int s_nfree; /*空闲块块数*/ unsigned short s_pfree; /*空闲块指针*/ unsigned int s_freeNICFREE; /*空闲块堆栈*/ unsigned int s_ninode; /*空闲i节点数*/ unsigned short s_pinode; /*空闲i节点指针*/ unsigned int s_inodeNICINOD; /*空闲i节点数组*/ unsigned int s

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论