2022年Greekos操作系统实验——吴孔玲 .pdf

上传人:Che****ry 文档编号:27262863 上传时间:2022-07-23 格式:PDF 页数:22 大小:572.47KB
返回 下载 相关 举报
2022年Greekos操作系统实验——吴孔玲 .pdf_第1页
第1页 / 共22页
2022年Greekos操作系统实验——吴孔玲 .pdf_第2页
第2页 / 共22页
点击查看更多>>
资源描述

《2022年Greekos操作系统实验——吴孔玲 .pdf》由会员分享,可在线阅读,更多相关《2022年Greekos操作系统实验——吴孔玲 .pdf(22页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、计算机系统实验软件报告题目: Geekos操作系统的研究与实现院 (系) :计算机与控制学院专业:计算机应用技术学生姓名:吴孔玲学号:092031130指导教师:黄廷辉2010 年 06 月 30 日名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 22 页 - - - - - - - - - 2 目录1 GeekOS简介 . 311GeekOS 系统源代码结构 . 32 实验环境 . 53 项目实现 . 531 project0. 53.11 项目设计目的. 53.12

2、项目设计要求. 53.13 项目设计原理. 53.13 项目具体实现. 63.14 调试运行结果. 732 project1. 83.21 项目设计目的. 83.22 项目设计要求. 83.23 项目设计原理. 83.23 项目具体实现. 10 3.24 调试运行结果. 10 33 project2. 11 3.31 项目设计目的. 11 3.32 项目设计要求. 11 3.33 项目设计原理. 12 3.23 项目具体实现. 14 3.24 调试运行结果. 20 4 遇到问题及解决方法. 21 5 学习总结 . 22 参考文献 . 22 名师资料总结 - - -精品资料欢迎下载 - - -

3、- - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 22 页 - - - - - - - - - 3 1 GeekOS简介GeekOS 是一个基于X86 架构的 PC上运行的微操作系统内核。由美国马理兰大学的教师开发, 是一个用 C语言开发的操作系统。主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中。出于教学目的, 这个系统内核设计简单,却又兼备实用性,它可以运行在真正的X86 PC硬件平台。作为一个课程设计平台,GeekOS由一个基本的操作系统内核作为基础,提供了操作系统与硬件之间的所有必备接口

4、,实现了系统引导、实模式到保护模式的转换、中断调用及异常处理、基于段式的内存管理,FIFO 进程调度算法以及内核进程、基本的输入输出(键盘作为输入设备、显示器作为输出设备),以及一个用于存放用户程序的只读文件系统PFAT 。学生可以在Linux或 Unix 环境下对其进行功能扩充, 且其针对进程、 文件系统、 存储管理等操作系统核心内容分别设计了7 个难度逐渐增加的项目供学生选择11GeekOS 系统源代码结构GeekOS 操作系统源文件geekos-0.3.0.zip可以从 http:/下载。解压后的GeekOS目录结构如图1-3 所示 : 名师资料总结 - - -精品资料欢迎下载 - -

5、- - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 22 页 - - - - - - - - - 4 在 doc 目录下文件hacking.pdf和 index.htm是 GeekOS系统的参考文档。Scripts目录下有 startProject和 removeEmptyConflicts两个脚本文件。 GeekOS系统的源文件在src目录下,分为7 个项目: Project0, Project1, Project2, Project3, Project4, Project5, Project6。每个项目的文件结构都类似,

6、以Project0为例,结构如图1-4 所示:在 build文件夹中,包含系统编译后的可执行文件的文件、软盘镜像或是硬盘镜像、makefile项目管理文件。在inculde文件夹中有geekOS和 libc两个子目录,在geekOS子目录中有kthread.h,keyboard.h等头文件,在libc中包含有 geekOS支持的 C语言标准函数 string.H头文件。在scripts文件夹是项目编译时要用到的一些脚本文件。Src 文件夹中存放系统内核源代码,用户修改 geekOS系统时要修改的源代码如main.c 都位于这个目录中。在 User 子目录中一般是用来存放用户的测试文件,在too

7、ls子目录中的代码是用来建名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 22 页 - - - - - - - - - 5 立 PFAT测试文件系统的。2 实验环境1 下载并安装VMware-workstation 6.5-7.0 2 建立虚拟机,在VMware-workstation中新建New Virtual Machine,按系统提示安装好 ubuntu10.04 3 安装 VMware Tools 实现文件共享,4 Bochs-2.3安装和使用3 项目实现31 p

8、roject03.11 项目设计目的熟悉 GeekOS的项目编译、调试和运行环境,掌握GeekOS运行工作过程。3.12 项目设计要求( 1)搭建 GeekOS的编译和调试平台,掌握GeekOS 的内核进程工作原理。( 2)熟悉键盘操作函数,编程实现一个内核进程。该进程的功能是:接受键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结束进程的运行。3.13 项目设计原理项目要求从键盘输入,因此要用到键盘处理函数。 GeekOS的键盘处理函数定义在keyboard.h与 keyboard.c 文件中。键盘的初始化是在Main 函数中调用Init_Keyboard进行的, Init_Keyboa

9、rd主要功能是设置初始状态下存放键盘扫描码的缓冲区,并为键盘中断设置处理函数。 而键盘中断处理过程是:首先从相应的I/O 端口读取键盘扫描码,根据是否按下 Shift键,分别在键值表中寻找扫描码对应的按键值,经过处理后将键值放入键盘缓冲区,最后通知系统重新调度进程。若用户进程需要从键盘输入信息,可调用 Wait_For_Key()函数 , 进程调用该函数后,会阻塞进入按键操作的等待队列,直到按键操作结束,进程才会被唤醒。 Start_Kernel_Thread函数主要功能就是建立一个内核线程。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - -

10、- - - 名师精心整理 - - - - - - - 第 5 页,共 22 页 - - - - - - - - - 6 本项目主要要求设计一个函数对键盘的中断进行响应。这主要通过使用Geekos 提供的键盘响应函数Wait_For_Key()进行键盘中断的响应及返回键值。该函数首先检查键盘缓冲区是否有按键, 如果有, 就读取一个键码,如果此时键盘缓冲区中没有按键,就将线程放入键盘事件等待队列。于是可分两步完成: 1 编写函数,函数功能是: 接受键盘输入的按键,并将键值显示到显示器,当输入 Ctrl+D退出。2 在Main 函 数 体 内 调 用Start_User_Thread函 数 , 将

11、编 写 的 函 数 地 址 传 递 给startFunc,建立一个内核进程。3.13 项目具体实现编写的函数static void keyin(void) Keycode keycode; Print(n-Wait for your input,Ctrl+d to exit-n); while(1) keycode=Wait_For_Key(); / 读取键盘按键状态if(!( (keycode & 0 x0100) | (keycode & 0 x8000) ) /处理非特殊按键的按下事件 int asciiCode = keycode & 0 x03ff; /低 8 位为 Ascii 码i

12、f( (keycode & 0 x4000)=0 x4000 & asciiCode=d) /按下 Ctrl 键 Print(n-Bye-n); Exit(1); else Print(%c,(asciiCode=r) ? n : asciiCode); 在 mian 函数中编写: Start_Kernel_Thread(keyin,0,PRIORITY_NORMAL,false);将编写的名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 22 页 - - - - - -

13、- - - 7 函数传入,生成内核进程。3.14 调试运行结果进入 /os/ project0/build目录执行make depend 执行make 成功之后在build 目录下生成 fd.img文件。启动 bochs 在 build 目录中执行bochs f bochsrc 成功后,运行结果:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 22 页 - - - - - - - - - 8 32 project13.21 项目设计目的熟悉 ELF文件格式,了解GeekO

14、S 系统如何将ELF格式的用户可执行程序装入内存,建立内核进程并运行的实现技术。3.22 项目设计要求(1) 修改 /geekos/elf.c文件:在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息 ) ,并填充 Exe_Format 数据结构的域值。(2) 掌握 GeekOS在核心态运行用户程序的原理,为项目2 的实现做准备。3.23 项目设计原理ELF是 UNIX 系统实验室作为应用程序二进制接口而开发和发布的。有两个平行视图。连接程序视图执行程序视图ELF 头部ELF 头部程

15、序头部表(可选)程序头部表节区 1 段 1 . 节区 n 段 2 . . . 节区头部表节区头部表(可选)GeekOS中的用户程序全部在系统的编译阶段完成编译和连接,形成可执行文件,用户可执行文件保存在PFAT文件系统中。 本项目要完成的就是在系统启动后,从 PFAT文件系统名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 22 页 - - - - - - - - - 9 将可执行文件装入内存,建立进程并运行得到相应的输出。在磁盘中的ELF文件的映像和在内存中执行程序镜像间

16、的对应关系如下图所示而此过程主要由Spawner 函数实现,其主要经过简要概述为:先调用Read_Fully函数将 文 件 读 入 内 存 , 后 调 用Parse_ELF_Executable函 数 分 析ELF 文 件 , 最 后 调 用Spawn_Program函数将可执行程序的代码段和数据段等装入内存,此后便可以开始运行一个内核级进程了。如下图所示:在本项目中,我们要完成Parse_ELF_Executable函数,此函数的作用为根据ELF文件格式,从 exeFileData指向的内容中得到ELF文件头, 继续分析可得到程序头和程序代码段等信息。ELF headerCode Data

17、section headerCode section headerData Code offset Data offset Data size Code size Code Data Stack (4096)ELF文件镜像内存中执行镜像名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 22 页 - - - - - - - - - 10 3.23 项目具体实现int Parse_ELF_Executable(char *exeFileData, ulong_t exeFile

18、Length, struct Exe_Format *exeFormat) /TODO(Parse an ELF executable image); elfHeader *eh = (elfHeader*)exeFileData; programHeader *ph=(programHeader *)(exeFileData+eh-phoff); int i; for(i=0;iphnum;i+) exeFormat-segmentList i.offsetInFile=ph-offset; exeFormat-segmentList i.lengthInFile=ph-fileSize;

19、exeFormat-segmentList i.startAddress=ph-vaddr; exeFormat-segmentList i.sizeInMemory=ph-memSize; exeFormat-segmentList i.protFlags=ph-flags; ph+; exeFormat-numSegments=eh-phnum; exeFormat-entryAddr=eh-entry; return 0; 3.24 调试运行结果进入 /os/ project1/build目录执行make depend 执行make 成功之后在build 目录下生成fd.img 、dis

20、kc.img文件。启动 bochs 在 build 目录中执行bochs f bochsrc 成功后,运行结果:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 22 页 - - - - - - - - - 11 33 project23.31 项目设计目的扩充 GeekOS操作系统内核,使得系统能够支持用户级进程的动态创建和执行。3.32 项目设计要求开始本项目前需要阅读/src/geekos目录中的entry.c、lowlevel.asm、kthread.c、user

21、seg.c , 其中在 userseg.c中主要关注 Destroy_User_Context()和 Load_User_Program()两个函数。本项目要求用户对以下几个文件进行修改:1)“src/GeekOS/user.c ”文件中的函数Spawn () ,其功能是生成一个新的用户级进程;2)“src/GeekOS/user.c ”文件中的函数Switch_To_User_Context() ,调度程序在执行一个新的进程前调用该函数以切换用户地址空间;3)“src/GeekOS/elf.c”文件中的函数Parse_ELF_Executable() 。该函数的实现要求和项目1 相同。4)

22、“src/GeekOS/userseg.c ”文件中主要是实现一些为实现对“src/GeekOS/user.c ”中高层操作支持的函数。 Destroy_User_Context()函数的功能是释放用户态进程占用的名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 22 页 - - - - - - - - - 12 内存资源。 Load_User_Program ()函数的功能通过加载可执行文件镜像创建新进程的User_Context结构。 Copy_From_User()

23、和 Copy_To_User()函数的功能是在用户地址空间和内核地址空间之间复制数据,在分段存储器管理模式下,只要段有效,调用memcpy函数就可以实现这两个函数的功能。 Switch_To_Address_Space ()函数的功能是通过将进程的 LDT装入到 LDT寄存器来激活用户的地址空间;5)“src/GeekOS/kthread.c”文件中的Start_User_Thread函数和 Setup_User_Thread函数。 Setup_User_Thread ()函数的功能是为进程初始化内核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用的数据。 Start_User_T

24、hread()是一个高层操作,该函数使用User_Context对象开始一个新进程。6)“src/GeekOS/kthread.c”文件中主要是实现用户程序要求内核进行服务的一些系统调用函数定义。 要求用户实现的有Sys_Exit( ) 函数、Sys_PrintString() 函数、Sys_GetKey() 、Sys_SetAttr () 、 Sys_GetCursor () 、Sys_PutCursor () 、Sys_Spawn ()函数、Sys_Wait()函数和Sys_GetPID( )函数。7 ) 在main.c文 件 中 改 写 生 成 第 一 个 用 户 态 进 程 的 函 数

25、 调 用 :Spawn_Init_Process(void) 。3.33 项目设计原理Geekos 的初始系统不支持用户态进程,但提供了用户态进程上下文接口和实现用户态进程需要用到的数据结构。所以,用户态进程及相关函数都要开发者实现。在Geekos 中为了区分用户态进程和内核进程,在Kernel_Thread结构体中设置了一个字段userContext,指向用户态进程上下文。对于内核进程来说,这个指针为空, 而用户态进程都拥有自己的用户上下文。因此在Geekos 中要判断一个进程是内核进程还是用户态进程,只要通过userContext字段是否为空来判断就可以了。GeekOS的进程结构图如10-

26、1 所示:图 10.1 用户态进程结构每个用户态进程都拥有属于自己的内存段空间,如:代码段、数据段、堆栈段等,每个段有一个段描述符(segment descriptor) ,并且每个进程有一个段描述符表(Local Descriptor Table) ,用于保存该进程的所有段描述符。操作系统中还设置一个全局描述符名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 22 页 - - - - - - - - - 13 表( GDT ,Global Descriptor Tabl

27、e ) ,用于记录了系统中所有进程的ldt描述符。 GDT 、LDT和 User_Context的关系如图10-2 所示图 10-2 GDT、 LDT和 User_Context的关系程序流程图:根据 exeFileData得出程序头表的偏移量用户程序编译和连接后形成可执行文件保存在PFAT 文件系统从 PFAT 文件系统读入可执行文件装入内存缓冲区对装入内存中的可执行文件进行分析初始化 User_Context 结构,并将可执行程序的程序段和数据段载入内存创建一个用户进程,并加入准备运行队列用户进程运行结束,调用Exit 函数退出名师资料总结 - - -精品资料欢迎下载 - - - - -

28、- - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 22 页 - - - - - - - - - 14 GeekOS的用户级进程创建过程可以描述如下:(1)Spawn 函数导入用户程序并初始化:调用 Load_User_Program 进行 User_Context的初始化及用户级进程空间的分配及用户程序各段的装入;(2)Spawn 函数调用Start_User_Thread(),初始化一个用户态进程,包括初始化进程Kernel_Thread结构以及调用Setup_User_Thread 初始化用户级进程内核堆栈;(3) 最后 Spa

29、wn函数退出, 这时用户级进程已被添加至系统运行进程队列,可以被调度了。3.23 项目具体实现User.c中int Spawn(const char *program, const char *command, struct Kernel_Thread *pThread) int result; char *exeFileData = 0; ulong_t exeFileLength; struct User_Context *userContext = 0; struct Kernel_Thread *thread; struct Exe_Format exeFormat; if (resu

30、lt= Read_Fully(program, (void*) &exeFileData, &exeFileLength) != 0 | (result = Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat) != 0 | (result= Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, &userContext) != 0) goto fail; Free(exeFileData); exeFileData = 0; /* 开始用户进程

31、*/ thread= Start_User_Thread(userContext, false); if (thread!= 0) *pThread = thread; result=thread-pid; else result = ENOMEM; return result; fail: if (exeFileData != 0) Free(exeFileData); if (userContext != 0) Destroy_User_Context(userContext); return result; void Switch_To_User_Context(struct Kerne

32、l_Thread* kthread, struct Interrupt_State* state) struct User_Context* userContext = kthread-userContext; if (userContext = 0) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 14 页,共 22 页 - - - - - - - - - 15 return; Switch_To_Address_Space(userContext); Set_Kernel_Sta

33、ck_Pointer(ulong_t)(kthread-stackPage+PAGE_SIZE); Elf.c 中int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) /TODO(Parse an ELF executable image); elfHeader * head = (elfHeader*)exeFileData; programHeader *ph=(programHeader *)(exeFileData+ head -phoff); K

34、ASSERT(exeFileData!=NULL); KASSERT(exeFileLengthhead-ehsize+head-phentsize*head-phnum); KASSERT(head-entry%4=0); exeFormat-numSegments=head-phnum; exeFormat-entryAddr=head-entry; int i; for(i=0;iphnum;i+) exeFormat-segmentList i.offsetInFile=proHeader-offset; exeFormat-segmentList i.lengthInFile= pr

35、oHeader -fileSize; exeFormat-segmentList i.startAddress= proHeader -vaddr; exeFormat-segmentList i.sizeInMemory= proHeader -memSize; exeFormat-segmentList i.protFlags= proHeader -flags; proHeader +; return 0; Userseg.c void Destroy_User_Context(struct User_Context* userContext) Free_Segment_Descript

36、or(userContext-ldtDescriptor); Free(userContext-memory); Free(userContext); int Load_User_Program(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat, const char *command, struct User_Context *pUserContext) int i; ulong_t maxva = 0; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - -

37、 - - - - 名师精心整理 - - - - - - - 第 15 页,共 22 页 - - - - - - - - - 16 unsigned numArgs; ulong_t argBlockSize; ulong_t size, argBlockAddr; struct User_Context *userContext = 0; for (i = 0; i numSegments; +i) struct Exe_Segment *segment = &exeFormat-segmentListi; ulong_t topva = segment-startAddress + segm

38、ent-sizeInMemory; if (topva maxva) maxva = topva; Get_Argument_Block_Size(command, &numArgs, &argBlockSize); virsize = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE; argBlockAddr = virsize; virsize += argBlockSize; userContext = Create_User_Context(virsize); if (userContext = 0) return -1; for (

39、i = 0; i numSegments; +i) struct Exe_Segment *segment = &exeFormat-segmentListi; memcpy(userContext-memory + segment-startAddress, exeFileData + segment-offsetInFile, segment-lengthInFile); Format_Argument_Block(userContext-memory + argBlockAddr, numArgs, argBlockAddr, command); userContext-entryAdd

40、r = exeFormat-entryAddr; userContext-argBlockAddr = argBlockAddr; userContext-stackPointerAddr = argBlockAddr; *pUserContext = userContext; return 0; bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) struct User_Context* current = g_currentThread-userContext; if (!Validate_

41、User_Memory(current, srcInUser, bufSize) return false; memcpy(destInKernel, User_To_Kernel(current, srcInUser), bufSize); return true; bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize) struct User_Context* current = g_currentThread-userContext; if (!Validate_User_Memory(curre

42、nt, destInUser, bufSize) return false; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 16 页,共 22 页 - - - - - - - - - 17 memcpy(User_To_Kernel(current, destInUser), srcInKernel, bufSize); return true; void Switch_To_Address_Space(struct User_Context *userContext) ushor

43、t_t ldtSelector; /* Switch to the LDT of the new user context */ ldtSelector = userContext-ldtSelector; _asm_ _volatile_ ( lldt %0 : : a (ldtSelector) ); Kthresd.c 中Start_User_Thread(struct User_Context* userContext, bool detached) struct Kernel_Thread* kthread = Create_Thread(PRIORITY_USER, detache

44、d); if (kthread != 0) Setup_User_Thread(kthread, userContext); Make_Runnable_Atomic(kthread); return kthread; void Setup_User_Thread( struct Kernel_Thread* kthread, struct User_Context* userContext) extern int userDebug; ulong_t eflags = EFLAGS_IF; ushort_t csSelector=userContext-csSelector; ushort_

45、t dsSelector=userContext-dsSelector; Attach_User_Context(kthread, userContext); Push(kthread, dsSelector); Push(kthread, userContext-stackPointerAddr); Push(kthread, eflags); Push(kthread, csSelector); Push(kthread, userContext-entryAddr); if (userDebug) Print(Entry addr=%lxn, userContext-entryAddr)

46、; Push(kthread, 0); Push(kthread, 0); Push(kthread, 0); /* eax */ Push(kthread, 0); /* ebx */ Push(kthread, 0); /* edx */ Push(kthread, 0); /* edx */ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 17 页,共 22 页 - - - - - - - - - 18 Push(kthread, userContext-argBlockAdd

47、r); /* esi */ Push(kthread, 0); /* edi */ Push(kthread, 0); /* ebp */ Push(kthread, dsSelector); /* ds */ Push(kthread, dsSelector); /* es */ Push(kthread, dsSelector); /* fs */ Push(kthread, dsSelector); /* gs */ Syscall.c 中static int Sys_Exit(struct Interrupt_State* state) Exit(state-ebx); static

48、int Sys_PrintString(struct Interrupt_State* state) int rc = 0; uint_t length = state-ecx; uchar_t* buf = 0; if (length 0) /* Copy string into kernel. */ if (rc = Copy_User_String(state-ebx, length, 1023, (char*) &buf) != 0) goto done; /* Write to console. */ Put_Buf(buf, length); done: if (buf != 0)

49、 Free(buf); return rc; static int Sys_GetKey(struct Interrupt_State* state) return Wait_For_Key(); static int Sys_SetAttr(struct Interrupt_State* state) Set_Current_Attr(uchar_t) state-ebx); return 0; static int Sys_GetCursor(struct Interrupt_State* state) int row, col; Get_Cursor(&row, &col); if (!

50、Copy_To_User(state-ebx, &row, sizeof(int) | 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 18 页,共 22 页 - - - - - - - - - 19 !Copy_To_User(state-ecx, &col, sizeof(int) return -1; return 0; static int Sys_PutCursor(struct Interrupt_State* state) return Put_Cursor(state

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

当前位置:首页 > 教育专区 > 高考资料

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

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