pintos pro2 项目2 用户程序(38页).doc

上传人:1595****071 文档编号:36361678 上传时间:2022-08-26 格式:DOC 页数:37 大小:193.50KB
返回 下载 相关 举报
pintos pro2 项目2 用户程序(38页).doc_第1页
第1页 / 共37页
pintos pro2 项目2 用户程序(38页).doc_第2页
第2页 / 共37页
点击查看更多>>
资源描述

《pintos pro2 项目2 用户程序(38页).doc》由会员分享,可在线阅读,更多相关《pintos pro2 项目2 用户程序(38页).doc(37页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、-pintos pro2 项目2 用户程序-第 37 页Pintos project2作者:西安电子科技大学王永刚 QQ:357543420这个项目将使pintos可以加载并执行用户程序,并且为用户程序提供系统调用。Project2 需要完成的的任务有四个: Task1 Process Termination Messages进程终止信息 Task2 Argument Passing 参数传递 Task3 System Calls 系统调用 Task4 Denying Writes to Executables不能写入可执行文件Task1: Process Termination Messag

2、es进程终止信息要求: 1.在进程结束时输出退出代码(就是main函数的返回值,或者异常退出代码。 注意: 用户进程结束时输入退出代码,核心线程返回时不输入。输出格式被规定如下: printf (“%s: exit(%d)n”,.); 实现方法:1. 既然要打印返回值,就得用一个变量保存返回值,于是在struct thread 结构中加入一个变量回保存返回值:int ret; 在init_thread()函数中初始化为0(这里可以不用初始化)。2. 在线程退出里要保存其返回值到ret中,这个将在系统调用里的exit函数中保存,这里先不考虑。在什么地方加入printf()呢?每个线程结束后,都要

3、调用thread_exit()函数,如果是加载了用户进程,在thread_exit()函数中还会调用process_exit()函数, 在process_exit()函数中,如果是用户进程,那么其页表一定不为NULL,而核心进程页表一定为NULL,即只有用户进程退出时if(pd!=NULL) 就会成立,所以在大括号中加入:printf (“%s: exit(%d)n”,cur-name,cur-ret);其中cur=thread_current();即当前线程的struct thread 指针。TASK1 OKTASK2 Argument Passing 参数传递要求:1. 分离从命令行传入的

4、文件名和各个参数。2. 按照C函数调用约定,把参数放入栈中。实现方法:1.分离参数的方法:用string.h中的strtok_r()函数,在string.c中有详细的说明。2.在process_execute()函数中,因为thread_create()需要一个线程名,此时应该传递给它文件名(不带参数)。可如下处理:char *real_name, *save_ptr; real_name = strtok_r (file_name, , &save_ptr); tid = thread_create (real_name, PRI_DEFAULT, start_process, fn_cop

5、y);(3)在start_process()函数中,再次分离参数,放入栈中。由于在process_execute()对file_name作了复制,文件名并未丢失,但是要注意,无论加载用户程序成功还是失败,都得释放file_name所占用的一个页的空间(Debug here 3 weeks)。 注意:传给Load()函数的参数也只能有文件名,所以在load()函数前要分离出文件名:char *token=NULL, *save_ptr=NULL;token = strtok_r (file_name, , &save_ptr);success = load (token, &if_.eip, &

6、if_.esp);参数放置的一种方法:(1)找到用户栈指针:在start_process()函数中有struct intr_frame if_; 这样一个结构,其中有一个成员if_.esp,这就是用户栈指针,在load()函数中为其赋值,分配了栈空间。(2)调用strtok_r 分离出一个个参数(就是一个个字符串了),把每个字符串都复制到用户栈中,并把他在栈中的位置记录到一个数组中,以备下一步使用。注意:栈是向下增长,而字符串是向上增长。 char *esp=(char *)if_.esp; char *arg256; /assume numbers of argument below 256

7、 int i,n=0; for (; token != NULL;token = strtok_r (NULL, , &save_ptr) esp-=strlen(token)+1; /because user stack increase to low addr. strlcpy(esp,token,strlen(token)+2); /copy param to user stack argn+=esp;(3)要加入一个双字的对齐,因为是32位的,所以就是四字节对齐。while(int)esp%4make) /word align esp-; /注意:栈是向下增长,所以这里是而不是+;(4

8、)要将第(2)步保存下的指针逆序放入栈中。按照C约定,先要放入一个0,以防没有参数。 int *p=esp-4; *p-=0; 然后依次放入参数n的地址,参数n-1的地址 参数0的地址。for(i=n-1;i=0;i-) /place the arguments pointers to stack *p-=(int *)argi;(5)放入argc,argv*p-=p+1; *p-=n; *p-=0; esp=p+1;(6)让用户栈指针指向新的栈顶 if_.esp=esp如下图摆放。 如果命令行是:/bin/ls l foo bar完整代码见附录!TASK 3 system call 系统调用

9、要求:(1)实现以下系统调用: pfnSYS_WRITE=IWrite; /printf和写文件需要。 pfnSYS_EXIT=IExit; /退出时return后调用 pfnSYS_CREATE=ICreate; /创建文件 pfnSYS_OPEN=IOpen;/打开文件 pfnSYS_CLOSE=IClose; /关闭文件 pfnSYS_READ=IRead; / 读文件 pfnSYS_FILESIZE=IFileSize; /返回文件大小 pfnSYS_EXEC=IExec; /加载用户程序 pfnSYS_WAIT=IWait;/等待子进程结束 pfnSYS_SEEK=ISeek; /移

10、动文件指针 pfnSYS_REMOVE=IRemove; /删除文件 pfnSYS_TELL=ITell; /返回文件指针位置 pfnSYS_HALT=IHalt; /关机要想完成以上系统调用,还要明白系统调用的机制,见后边。参考文件有:src/lib/user/syscall.c 了解每个系统调用的形式。 src/lib/syscall-nr.h 了解每个系统调用号。实现方法:(1) 搭建框架用一个数组保存各函数名,数组下标就是系统调用号。在syscall_init()函数中初始化数组pfn为NULL在syscall_handler()函数中依据系统调用号调用相函数。typedef void

11、 (*CALL_PROC)(struct intr_frame*);CALL_PROC pfnMAXCALL;voidsyscall_init (void) intr_register_int (0x30, 3, INTR_ON, syscall_handler, syscall); int i; for(i=0;iesp) ExitStatus(-1); int No=*(int *)(f-esp); if(No=MAXCALL|MAXCALLret=status; /保存返回值。 thread_exit();SYS_CREATE-创建文件 void ICreate(struct intr_

12、frame *f) 取出仅有的一个参数文件名。调用filesys_create()函数。保存返回值。SYS_OPEN-打开文件void IOpen(struct intr_frame *f)取出文件名。调用filesys_open()函数打开文件。这里需要为每个进程维护一个打开文件表。打开文件后要为这个文件分配一个句柄号。在struct thread 结构中加入: int FileNum; /打开文件数 限制进程打开文件数 struct list file_list; /打开文件列表 int maxfd; /句柄分配使用每打开一个文件就让maxfd加1,关闭文件可以不减小。关联文件句柄与文件指

13、针的结构:(被链入file_list)struct file_node int fd; struct list_elem elem; struct file *f;有了以上准备,每打开一个文件都要新创建一个file_node结构,分配句柄,并把file_node加入file_list; 最后返回文件句柄就OK.SYS_CLOSE关闭文件 void IClose(struct intr_frame *f)一种是关闭一个文件。一种是进程退出时关闭所有文件。从用户栈中获取要关闭文件的句柄。在用户打开文件列表中找到对应文件,以得到文件指针。调用file_close()函数关闭文件,释放struct f

14、ile_node。关闭所有文件自然是每一个都要关闭,释放了。 Debug here 3 weeksSYS_READ读文件IRead()从用户栈中获得fd buffer size三个参数如果fd是标准输入设备,则调用input_getc()如果fd是文件句柄由fd 从进程打开文件表中得到文件指针调用file_read()函数从文件中读数据。SYS_FILESIZE 获取文件大小 IFileSize()从用户栈中获得fd由fd 从进程打开文件表中得到文件指针调用file_len gth得到文件大小SYS_EXEC - 加载用户程序 IExec()用户程序通过SYS_EXEC这个系统调用创建子进程。

15、在IExec()函数中,分配一个页,复制一份用户提供的用户名。否则在后来分离参数时,加入0时出现核心线程写入用户内存空间的页错误。还要注意线程同步问题。在IExec()中调用process_execute()函数创建子进程,但是从process_execute()得到了用户进程pid后,用户程序并没用加载。所以要等待用户进程被调度后调用了start_process()函数才能知道。 Start_process()函数真正加载用户程序,可能会因为找不到程序文件或内存不足等原因导致加载失败。 所以父进程调用process_execute()后不能立即返回,要在一个信号量上等待sema_down(s

16、ema),直到start_process()函数中加载用户程序成功后再semp_up(sema)激活父进程,激活父进程后应该立即挂起自己sema_down(sema),这里父进程获取子进程状态信息后,再出父进程sema_up()激活子进程。 如果父进程创建了一个优先级比自己高的子进程,如果不这样坐,start_process()函数每一次执行sema_up(sema)后,父进程还是不会被调度,而子进程可以已经运行完毕,这样父进程就得不到子进程的状态了。在struct_thread结构中加入semaphore SemaWaitSuccess;可以在父进程的的这个信号量上等,也可是子进程的Sema

17、WaitSuccess上等。如果子进程创建成功则返回pid,失败返回-1。SYS_WAIT等待函数 IWait()主线程创建子进程后,出于他与子进程优先级一样,所以,二者交替执行,这样主线程就有可能先结束,这导致了一开始的test失败。起初可以通过创建子进程时提高子进程优先级或者在process_wait()中加入while(true)这样的死循环来解决。后期要通过信号量同步。这个系统调用的需求: 父进程创建子进程后可能要等子进程结束。Process_wait()要返回子进程的返回值。情况有如下:父进程调用process_wait()时子进程还未结束,此进父进程将被挂起,等子进程结束后再唤醒父

18、进程,父进程再取得返回值。父进程调用process_wait()时子进程已经结束,这就要求子进程结束后应该把返回值保存到父进程的进程控控制块中。于是在struct thread要加入一个链表,struct list sons_ret;结构:struct ret_dataint pid;int ret;struct list_elem elem;这样就能保存子进程的返回值了。在struct thread结构中加入 bool bWait; 表示进程本身有没有被父进程等待。 在struct thread结构中加入bool SaveData; 如果子进程已经把返回值保存到父进程里了就设SaveData

19、为true; SaveData应该被初始化为false;在struct thread结构中加入struct thread *father;表示父线程。每创建一个子线程,都要在子线程中记录父线程。信号量同步方法:在struct thread结构中加入semaphore SemaWait;这里选择在父进程的SemaWait上等。这个等待会把父进程的struct thread 进程控制块插入到SemaWait的list中去。要想同时等待多个进程则不可能把父进程插入到多个子进程中去。当然,这里的测试只能等一个子进程,所以在父进程和子进程上等都可以。父进程执行process_wait(child_pid

20、)后,可以由child_pid得到子进程struct thread 指针t。通过遍历all_list比较pid实现.如果在all_list没有发现子进程的进程控制块或者发现t-SaveData=true|t-status=THREAD_DYING;表示子进程已经结束,直接从自己的sons_ret链表中找到子进程的返回值返回就OK.如果子进程还在运行,则执行sema_down(t-father-SemaWait)把自己挂起,子进程执行完毕后,发现在bWait=true,自己被等待了,再释放父进程sema_up(SemaWait); 如果bWait=fale,则不用唤醒父进程。父进程被唤醒后,再从

21、sons_ret链表中得到子进程的返回值。每个子进程只能等一次,第二次等同一个子进程只能返回-1.一个进程结束时,在process_exit()函数中,要释放自己打开的所有文件,保存返回值到父进程,输出退出信息,如果有父进程在等他就唤醒父进程,释放子进程链表。SYS_SEEK -移动文件指针 ISeek()从用户栈中取出文件句fd柄要移动的距离,把fd转为文件指针,调用file_seek()函数移动文件指针即可。SYS_REMOVE 删除文件 IRemove从用户栈中取出要删除文件的文件名。调用filesys_remove()删除文件。SYS_TELL 返回文件指针当前位置 ITell()从用

22、户栈中取出文件句fd柄要移动的距离,把fd转为文件指针,调用file_tell()函数得到指针位置。SYS_HALT 关机 IHALT调用shutdown_power_off()函数关机用户程序导致页错误时,会进入page_fault()函数,在exception.c中。 在page_fault()中加入 if(not_present|(is_kernel_vaddr(fault_addr)&user) ExitStatus(-1);来处理页错误。Task4 Denying Writes to Executables不能写入可执行文件在start_process函数中加入 t-FileSelf

23、=filesys_open(token);file_deny_write(t-FileSelf);其中FileSelf变量是要在struct thread结构中添加的。进程退出时就解除:在process_exit()中加入 if(cur-FileSelf!=NULL) /撤销对自己人deny_write file_allow_write(cur-FileSelf); file_close (cur-FileSelf);注意:所有系统调用的返回值都放到用户的eax寄存器中。取出参数时要对用户栈指针作详细的检查,是否越界,越界则直接调用Exit_Status(-1)中止用户进程。用户程序加载过程:

24、(1)核心线程通过调用process_execute(char *file_name);函数来创建用户进程。 File_name为要加载的文件名。这个函数中还调用了thread_create()函数为用户进程创建了一个线程。File_name 和一个叫start_process的函数 被传递给了thread_create(), thread_create()函数创建线程后就把线程放入ready_list()等待被调度;(2) 得到CPU后就开始start_process(void *file_name)函数。这个函数做了以下几件事儿:根据file_name把用户程序从硬盘调入内存,还为其分配了

25、虚拟内存。注意这里要完成task2,不然文件名不正确,就没法打开文件。给用户分配了栈空间3GB开始。向低字节增长。在这里要实现参数分离,并且把参数按规定放入用户栈中。(3)通过内嵌汇编 asm volatile (.)调用了用户程序中的main()函数。(4)main()函数从栈中取出传给他的参数,执行完毕后会调用系统调用exit(),Exit()函数又调用thread_exit()函数,thread_exit()函数又调用process_exit()函数,最后在thread_exit()函数中把即将退出的函数的进程控制块struct thread从all_list中remove掉,调用了进程

26、调度器schedule()函数,调用下一下进程执行。系统调用过程:在用户程序使用一个系统调用,如printf();在必然会触发一个30号中断,正如src/lib/user/syscall.c文件中所述。可见参数个数不同,系统调用不同。 这个30号中断调用之前,把系统调用号、用户参数(0到3个不等)压入栈中。然后开始执行中断程序,中断程序又调用了syscall_handler(struct intr_frame *f)函数,其中f是一个指向了用户程序当前运行信息的的指针,其中就有用户栈指针esp,所以在我们添加的系统调用中,就可以根据这个指针取出系统调用号和各个参数。系统调用结束后,要把返回值入

27、如f-eax中.注意: 用户栈中的各个参数并不连续存放: 三个参数write(fd,buffer,size); int fd=*(esp+2); char *buffer=(char *)*(esp+6); unsigned size=*(esp+3); 两个参数create(pFileName,size); bool ret=filesys_create(const char *)*(unsigned int *)f-esp+4),*(unsigned int *)f-esp+5); 一个参数 exit(-1);cur-ret=*(int *)f-esp+1);附录:Task2 参数传递代码

28、(红色)Task3 系统调用(蓝色)Task4 deny write (绿色)tid_tprocess_execute (const char *file_name) char *fn_copy; tid_t tid; /* Make a copy of FILE_NAME. Otherwise theres 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, PGSIZ

29、E); char *real_name, *save_ptr; real_name = strtok_r (file_name, , &save_ptr); /* Create a new thread to execute FILE_NAME. */ tid = thread_create (real_name, PRI_DEFAULT, start_process, fn_copy); if (tid = TID_ERROR) palloc_free_page (fn_copy); return tid;/* A thread function that loads a user proc

30、ess and starts it running. */static voidstart_process (void *file_name_) char *file_name = file_name_; struct intr_frame if_; bool success; char *token=NULL, *save_ptr=NULL; token = strtok_r (file_name, , &save_ptr); / get real file name, use it in load() /* Initialize interrupt frame and load execu

31、table. */ memset (&if_, 0, sizeof if_); if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.cs = SEL_UCSEG; if_.eflags = FLAG_IF | FLAG_MBS; success = load (token, &if_.eip, &if_.esp); struct thread *t=thread_current(); if (!success) palloc_free_page (file_name); t-tid=-1; sema_up(&t-SemaWai

32、tSuccess); ExitStatus(-1); sema_up(&t-SemaWaitSuccess); t-FileSelf=filesys_open(token); file_deny_write(t-FileSelf); char *esp=(char *)if_.esp; char *arg256; /assume numbers of argument below 256 int i,n=0; for (; token != NULL;token = strtok_r (NULL, , &save_ptr) /copy the argument to user stack es

33、p-=strlen(token)+1; /because user stack increase to low addr. strlcpy(esp,token,strlen(token)+2); /copy param to user stack argn+=esp; while(int)esp%4make) /word align esp-; int *p=esp-4; *p-=0; /first 0 for(i=n-1;i=0;i-) /place the arguments pointers to stack *p-=(int *)argi; *p-=p+1; *p-=n; *p-=0;

34、 esp=p+1; if_.esp=esp; /set new stack top palloc_free_page (file_name); asm volatile (movl %0, %esp; jmp intr_exit : : g (&if_) : memory); NOT_REACHED ();Syscall.c中所有代码:#include userprog/syscall.h#include threads/vaddr.h#include #include #include threads/interrupt.h#include threads/thread.h#include

35、filesys/filesys.h#include filesys/file.h#include devices/input.h#include process.h#include #include devices/shutdown.h#define MAXCALL 21#define MaxFiles 200#define stdin 1static void syscall_handler (struct intr_frame *);typedef void (*CALL_PROC)(struct intr_frame*);CALL_PROC pfnMAXCALL;void IWrite(

36、struct intr_frame*);void IExit(struct intr_frame *f);void ExitStatus(int status);void ICreate(struct intr_frame *f);void IOpen(struct intr_frame *f);void IClose(struct intr_frame *f);void IRead(struct intr_frame *f);void IFileSize(struct intr_frame *f);void IExec(struct intr_frame *f);void IWait(str

37、uct intr_frame *f);void ISeek(struct intr_frame *f);void IRemove(struct intr_frame *f);void ITell(struct intr_frame *f);void IHalt(struct intr_frame *f);struct file_node *GetFile(struct thread *t,int fd);voidsyscall_init (void) intr_register_int (0x30, 3, INTR_ON, syscall_handler, syscall); int i; f

38、or(i=0;iesp) ExitStatus(-1); int No=*(int *)(f-esp); if(No=MAXCALL|MAXCALLesp; if(!is_user_vaddr(esp+7) ExitStatus(-1); int fd=*(esp+2); /文件句柄 char *buffer=(char *)*(esp+6); /要输出人缓冲 unsigned size=*(esp+3); /输出内容大小。 if(fd=STDOUT_FILENO) /标准输出设备 putbuf (buffer, size); f-eax=0; else /文件 struct thread *cur=thread_current(); struct file_node *fn=GetFile(cur,fd); /获取文件指针 if(fn=NULL) f-eax=0; return; f-eax=file_write(fn-f,buffer

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 小学资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁